Skip to content

Commit

Permalink
GH-458 Provide Result based Path extension functions and simplify FS …
Browse files Browse the repository at this point in the history
…provider implementation
  • Loading branch information
dzikoysk committed Aug 3, 2021
1 parent 9777031 commit da5dcce
Show file tree
Hide file tree
Showing 16 changed files with 330 additions and 330 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ import net.dzikoysk.dynamiclogger.Journalist
import net.dzikoysk.dynamiclogger.Logger
import org.panda_lang.reposilite.failure.api.ErrorResponse
import org.panda_lang.reposilite.failure.api.errorResponse
import org.panda_lang.reposilite.maven.MetadataUtils.toNormalizedPath
import org.panda_lang.reposilite.maven.api.DeployRequest
import org.panda_lang.reposilite.maven.api.DocumentInfo
import org.panda_lang.reposilite.maven.api.FileDetails
import org.panda_lang.reposilite.maven.api.LookupRequest
import org.panda_lang.reposilite.maven.api.Repository
import org.panda_lang.reposilite.web.toPath
import panda.std.Result
import java.nio.file.Path
import java.nio.file.Paths

class MavenFacade internal constructor(
private val journalist: Journalist,
Expand All @@ -40,7 +44,11 @@ class MavenFacade internal constructor(
private val repositoryService: RepositoryService
) : Journalist {

fun findFile(lookupRequest: LookupRequest): Result<FileDetails, ErrorResponse> {
companion object {
val REPOSITORIES: Path = Paths.get("repositories")
}

fun findFile(lookupRequest: LookupRequest): Result<out FileDetails, ErrorResponse> {
val repository = repositoryService.getRepository(lookupRequest.repository) ?: return errorResponse(NOT_FOUND, "Repository not found")
val gav = lookupRequest.gav.toPath()

Expand All @@ -54,7 +62,7 @@ class MavenFacade internal constructor(
return repository.getFileDetails(gav)
}

fun deployFile(deployRequest: DeployRequest): Result<FileDetails, ErrorResponse> {
fun deployFile(deployRequest: DeployRequest): Result<DocumentInfo, ErrorResponse> {
val repository = repositoryService.getRepository(deployRequest.repository) ?: return errorResponse(NOT_FOUND, "Repository not found")

if (!repository.isDeployEnabled) {
Expand All @@ -65,14 +73,12 @@ class MavenFacade internal constructor(
return errorResponse(INSUFFICIENT_STORAGE, "Not enough storage space available")
}

val path = repository.relativize(deployRequest.gav) ?: return errorResponse(BAD_REQUEST, "Invalid GAV")
val metadataFile = path.resolveSibling(METADATA_FILE)
metadataService.clearMetadata(metadataFile)
val path = deployRequest.gav.toNormalizedPath() ?: return errorResponse(BAD_REQUEST, "Invalid GAV")

return try {
val result: Result<FileDetails, ErrorResponse> =
val result: Result<DocumentInfo, ErrorResponse> =
if (path.fileName.toString().contains(METADATA_FILE_NAME)) {
metadataService.getMetadata(repository, metadataFile).map { it.first }
metadataService.getMetadata(repository, path).map { it.first }
}
else {
repository.putFile(path, deployRequest.content)
Expand All @@ -87,7 +93,9 @@ class MavenFacade internal constructor(

fun deleteFile(repositoryName: String, gav: String): Result<*, ErrorResponse> {
val repository = repositoryService.getRepository(repositoryName) ?: return errorResponse<Any>(NOT_FOUND, "Repository $repositoryName not found")
return repository.removeFile(gav)
val path = gav.toNormalizedPath() ?: return errorResponse<Any>(NOT_FOUND, "Invalid GAV")

return repository.removeFile(path)
}

fun getRepositories(): Collection<Repository> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ package org.panda_lang.reposilite.maven
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.dataformat.xml.XmlMapper
import io.javalin.http.HttpCode
import io.javalin.http.HttpCode.BAD_REQUEST
import org.panda_lang.reposilite.failure.FailureFacade
import org.panda_lang.reposilite.failure.api.ErrorResponse
import org.panda_lang.reposilite.failure.api.errorResponse
import org.panda_lang.reposilite.maven.api.FileDetails
import org.panda_lang.reposilite.maven.api.DocumentInfo
import org.panda_lang.reposilite.maven.api.Repository
import org.panda_lang.reposilite.shared.FilesUtils
import org.panda_lang.reposilite.shared.FilesUtils.getSimpleName
import panda.std.Lazy
import panda.std.Pair
import panda.std.Result
Expand All @@ -32,28 +34,29 @@ import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.Path
import java.util.concurrent.ConcurrentHashMap
import java.util.function.Supplier

// TOFIX: Simplify this trash class
internal class MetadataService(private val failureFacade: FailureFacade) {

private val metadataCache: MutableMap<Path, Pair<FileDetails, String>> = ConcurrentHashMap()
private val metadataCache: MutableMap<Path, Pair<DocumentInfo, String>> = ConcurrentHashMap()

companion object {
private val XML_MAPPER: Lazy<XmlMapper> = Lazy(Supplier {

private val XML_MAPPER = Lazy {
XmlMapper.xmlBuilder()
.serializationInclusion(JsonInclude.Include.NON_NULL)
.defaultUseWrapper(false)
.build()
})
}

}

fun getMetadata(repository: Repository, requested: Path): Result<Pair<FileDetails, String>, ErrorResponse> {
if (requested.fileName.toString() != "maven-metadata.xml") {
return errorResponse(HttpCode.BAD_REQUEST, "Bad request")
fun getMetadata(repository: Repository, requested: Path): Result<Pair<DocumentInfo, String>, ErrorResponse> {
if (requested.getSimpleName() != "maven-metadata.xml") {
return errorResponse(BAD_REQUEST, "Bad request")
}

val cachedContent: Pair<FileDetails, String>? = metadataCache[requested]
val cachedContent = metadataCache[requested]

if (cachedContent != null) {
return Result.ok(cachedContent)
Expand All @@ -62,10 +65,10 @@ internal class MetadataService(private val failureFacade: FailureFacade) {
val artifactDirectory = requested.parent

if (repository.exists(artifactDirectory)) {
return errorResponse(HttpCode.BAD_REQUEST, "Bad request")
return errorResponse(BAD_REQUEST, "Bad request")
}

val versions: Result<List<Path>, ErrorResponse> = MetadataUtils.toSortedVersions(repository, artifactDirectory)
val versions = MetadataUtils.toSortedVersions(repository, artifactDirectory)

if (versions.isErr) {
return versions.projectToError()
Expand All @@ -85,19 +88,19 @@ internal class MetadataService(private val failureFacade: FailureFacade) {
groupId: String,
artifactDirectory: Path,
versions: List<Path>
): Result<Pair<FileDetails, String>, ErrorResponse> {
): Result<Pair<DocumentInfo, String>, ErrorResponse> {
val latest = versions.first()

val versioning = Versioning(
latest.fileName.toString(),
latest.fileName.toString(),
latest.getSimpleName(),
latest.getSimpleName(),
FilesUtils.toNames(versions),
null,
null,
MetadataUtils.toUpdateTime(repository, latest)
)

val metadata = Metadata(groupId, artifactDirectory.fileName.toString(), null, versioning)
val metadata = Metadata(groupId, artifactDirectory.getSimpleName(), null, versioning)
return toMetadataFile(repository, metadataFile, metadata)
}

Expand All @@ -106,9 +109,9 @@ internal class MetadataService(private val failureFacade: FailureFacade) {
metadataFile: Path,
groupId: String,
versionDirectory: Path
): Result<Pair<FileDetails, String>, ErrorResponse> {
): Result<Pair<DocumentInfo, String>, ErrorResponse> {
val artifactDirectory = versionDirectory.parent
val builds: Result<Array<Path>, ErrorResponse> = MetadataUtils.toSortedBuilds(repository, versionDirectory)
val builds = MetadataUtils.toSortedBuilds(repository, versionDirectory)

if (builds.isErr) {
return builds.projectToError()
Expand All @@ -119,7 +122,7 @@ internal class MetadataService(private val failureFacade: FailureFacade) {

val name = artifactDirectory.fileName.toString()
val version = StringUtils.replace(versionDirectory.fileName.toString(), "-SNAPSHOT", StringUtils.EMPTY)
val identifiers = MetadataUtils.toSortedIdentifiers(repository, name, version, builds.get())
val identifiers = MetadataUtils.toSortedIdentifiers(name, version, builds.get())
val latestIdentifier = identifiers.first()
val buildSeparatorIndex = latestIdentifier.lastIndexOf("-")

Expand All @@ -129,13 +132,13 @@ internal class MetadataService(private val failureFacade: FailureFacade) {
val latestTimestamp = latestIdentifier.substring(0, buildSeparatorIndex)
val latestBuildNumber = latestIdentifier.substring(buildSeparatorIndex + 1)
val snapshot = Snapshot(latestTimestamp, latestBuildNumber)
val snapshotVersions: MutableCollection<SnapshotVersion> = ArrayList<SnapshotVersion>(builds.get().size)
val snapshotVersions: MutableCollection<SnapshotVersion> = ArrayList(builds.get().size)

for (identifier in identifiers) {
val buildFiles = MetadataUtils.toBuildFiles(repository, versionDirectory, identifier)

for (buildFile in buildFiles) {
val fileName = buildFile.fileName.toString()
val fileName = buildFile.getSimpleName()
val value = "$version-$identifier"
val updated = MetadataUtils.toUpdateTime(repository, buildFile)
val extension = fileName
Expand All @@ -154,26 +157,20 @@ internal class MetadataService(private val failureFacade: FailureFacade) {
Versioning(fullVersion, fullVersion, listOf(fullVersion), null, null, MetadataUtils.toUpdateTime(repository, latestBuild))
}

return toMetadataFile(repository, metadataFile, Metadata(groupId, name, versionDirectory.fileName.toString(), versioning))
return toMetadataFile(repository, metadataFile, Metadata(groupId, name, versionDirectory.getSimpleName(), versioning))
}

private fun toMetadataFile(repository: Repository, metadataFile: Path, metadata: Metadata): Result<Pair<FileDetails, String>, ErrorResponse> {
private fun toMetadataFile(repository: Repository, metadataFile: Path, metadata: Metadata): Result<Pair<DocumentInfo, String>, ErrorResponse> {
return try {
val serializedMetadata: String = XML_MAPPER.get().writeValueAsString(metadata)
val bytes = serializedMetadata.toByteArray(StandardCharsets.UTF_8)
val result: Result<FileDetails, ErrorResponse> = repository.putFile(metadataFile, bytes)

if (result.isOk) {
FilesUtils.writeFileChecksums(repository, metadataFile, bytes)
metadataCache[metadataFile] = Pair<FileDetails, String>(result.get(), serializedMetadata)
}

result.map { fileDetailsResponse: FileDetails ->
Pair<FileDetails, String>(
fileDetailsResponse,
serializedMetadata
)
}
repository.putFile(metadataFile, bytes)
.map { Pair(it, serializedMetadata) }
.peek {
FilesUtils.writeFileChecksums(repository, metadataFile, bytes)
metadataCache[metadataFile] = it
}
}
catch (ioException: IOException) {
failureFacade.throwException(metadataFile.toAbsolutePath().toString(), ioException)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ package org.panda_lang.reposilite.maven
import org.panda_lang.reposilite.failure.api.ErrorResponse
import org.panda_lang.reposilite.maven.api.Repository
import org.panda_lang.reposilite.shared.FilesUtils.getExtension
import panda.utilities.StringUtils
import org.panda_lang.reposilite.web.toPath
import panda.std.Result
import panda.utilities.StringUtils
import panda.utilities.text.Joiner
import java.io.IOException
import java.nio.file.Path
import java.time.Instant
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.Arrays
import java.util.Locale
import java.util.TreeSet

Expand All @@ -43,7 +43,7 @@ internal object MetadataUtils {
val result = repository.getFiles(directory)

if (result.isOk) {
val paths: MutableCollection<Path> = TreeSet(repository)
val paths: MutableCollection<Path> = TreeSet() // Path comparator may be needed

for (path in result.get()) {
paths.add(directory.resolve(path.getName(0)))
Expand All @@ -56,50 +56,45 @@ internal object MetadataUtils {
}

@JvmStatic
fun toSortedVersions(repository: Repository, directory: Path): Result<List<Path>, ErrorResponse> {
return repository.getFiles(directory)
fun toSortedVersions(repository: Repository, directory: Path): Result<List<Path>, ErrorResponse> =
repository.getFiles(directory)
.map { list ->
list
.filter { path: Path -> path.parent.endsWith(directory) }
.filter { file: Path -> repository.isDirectory(file) }
.sortedWith(repository)
.sorted()
.toList()
}
}
}

internal fun toSortedIdentifiers(repository: Repository, artifact: String, version: String, builds: Array<Path>): List<String> {
return builds
.sortedWith(repository)
internal fun toSortedIdentifiers(artifact: String, version: String, builds: Array<Path>): List<String> =
builds
.sorted()
.map { build -> toIdentifier(artifact, version, build) }
.filterNot { text -> StringUtils.isEmpty(text) }
.distinct()
.toList()
}

internal fun isNotChecksum(path: Path, identifier: String): Boolean {
val name = path.fileName.toString()
return (!name.endsWith(".md5")
&& !name.endsWith(".sha1")
&& (name.contains("$identifier.") || name.contains("$identifier-")))
}
internal fun isNotChecksum(path: Path, identifier: String): Boolean =
path.fileName.toString()
.takeUnless { it.endsWith(".md5") }
?.takeUnless { it.endsWith(".sha1") }
?.takeIf { it.contains("$identifier.") || it.contains("$identifier-") } != null

internal fun toBuildFiles(repository: Repository, directory: Path, identifier: String): List<Path> {
return repository.getFiles(directory).get().asSequence()
internal fun toBuildFiles(repository: Repository, directory: Path, identifier: String): List<Path> =
repository.getFiles(directory).get().asSequence()
.filter { path -> path.parent == directory }
.filter { path -> isNotChecksum(path, identifier) }
.filter { file -> repository.exists(file) }
.sortedWith(repository)
.sorted()
.toList()
}

@JvmStatic
fun toIdentifier(artifact: String, version: String, build: Path): String {
var identifier = build.fileName.toString()
identifier = StringUtils.replace(identifier, "." + getExtension(identifier), StringUtils.EMPTY)
identifier = StringUtils.replace(identifier, "$artifact-", StringUtils.EMPTY)
identifier = StringUtils.replace(identifier, "$version-", StringUtils.EMPTY)
return declassifyIdentifier(identifier)
}
fun toIdentifier(artifact: String, version: String, build: Path): String =
build.fileName.toString()
.let { it.replace("." + getExtension(it), StringUtils.EMPTY) }
.replace("$artifact-", StringUtils.EMPTY)
.replace("$version-", StringUtils.EMPTY)
.let { declassifyIdentifier(it) }

private fun declassifyIdentifier(identifier: String): String {
val occurrences = StringUtils.countOccurrences(identifier, "-")
Expand Down Expand Up @@ -133,19 +128,16 @@ internal object MetadataUtils {
}
}

fun toGroup(elements: Array<String>): String {
return Joiner.on(".")
.join(Arrays.copyOfRange(elements, 0, elements.size)) { value: String -> if (value.contains(".")) value.replace(".", ESCAPE_DOT) else value }
fun toGroup(elements: Array<String>): String =
Joiner.on(".")
.join(elements.copyOfRange(0, elements.size)) { value: String -> if (value.contains(".")) value.replace(".", ESCAPE_DOT) else value }
.toString()
}

internal fun toGroup(elements: Array<String>, toShrink: Int): String {
return toGroup(shrinkGroup(elements, toShrink)).replace(ESCAPE_DOT, ".")
}
internal fun toGroup(elements: Array<String>, toShrink: Int): String =
toGroup(shrinkGroup(elements, toShrink)).replace(ESCAPE_DOT, ".")

private fun shrinkGroup(elements: Array<String>, toShrink: Int): Array<String> {
return Arrays.copyOfRange(elements, 0, elements.size - toShrink)
}
private fun shrinkGroup(elements: Array<String>, toShrink: Int): Array<String> =
elements.copyOfRange(0, elements.size - toShrink)

fun toGroup(metadataFilePath: Path): String {
val builder = StringBuilder()
Expand All @@ -163,15 +155,10 @@ internal object MetadataUtils {
return builder.toString()
}

private fun isBuildNumber(content: String): Boolean {
for (character in content.toCharArray()) {
if (!Character.isDigit(character)) {
return false
}
}

return true
}
private fun isBuildNumber(content: String): Boolean =
content
.any { !Character.isDigit(it) }
.not()

/**
* Process uri applying following changes:
Expand Down Expand Up @@ -202,4 +189,7 @@ internal object MetadataUtils {
return normalizedUri
}

fun String.toNormalizedPath(): Path? =
normalizeUri(this)?.toPath()

}
Loading

0 comments on commit da5dcce

Please sign in to comment.