All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.testerum.file_service.file.ResourceFileService.kt Maven / Gradle / Ivy

package com.testerum.file_service.file

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import com.fasterxml.jackson.datatype.guava.GuavaModule
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.afterburner.AfterburnerModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.testerum.common_kotlin.createDirectories
import com.testerum.common_kotlin.deleteIfExists
import com.testerum.common_kotlin.deleteRecursivelyIfExists
import com.testerum.common_kotlin.doesNotExist
import com.testerum.common_kotlin.exists
import com.testerum.common_kotlin.getContentOrNull
import com.testerum.common_kotlin.hasExtension
import com.testerum.common_kotlin.isRegularFile
import com.testerum.common_kotlin.smartMoveTo
import com.testerum.common_kotlin.walkAndCollect
import com.testerum.file_service.caches.resolved.resolvers.file_arg_transformer.FileArgTransformer
import com.testerum.file_service.file.util.isCreateResource
import com.testerum.file_service.file.util.isRelocateResource
import com.testerum.model.exception.ValidationException
import com.testerum.model.infrastructure.path.CopyPath
import com.testerum.model.infrastructure.path.Path
import com.testerum.model.infrastructure.path.RenamePath
import com.testerum.model.resources.ResourceContext
import com.testerum.model.resources.ResourceType
import com.testerum.model.resources.rdbms.connection.RdbmsConnectionConfig
import com.testerum.model.util.escape
import com.testerum.model.util.escapeFileOrDirectoryName
import org.slf4j.LoggerFactory
import java.nio.file.Files
import java.nio.file.StandardOpenOption
import java.nio.file.Path as JavaPath

class ResourceFileService {

    companion object {
        private val LOG = LoggerFactory.getLogger(ResourceFileService::class.java)

        private val OBJECT_MAPPER: ObjectMapper = jacksonObjectMapper().apply {
            registerModule(AfterburnerModule())
            registerModule(JavaTimeModule())
            registerModule(GuavaModule())

            enable(SerializationFeature.INDENT_OUTPUT)
            setSerializationInclusion(JsonInclude.Include.NON_NULL)
            disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            enable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID)

            disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
            disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
        }
    }

    fun getResourceByPathAndType(path: Path,
                                 resourceType: ResourceType,
                                 resourcesDir: JavaPath): ResourceContext? {
        val resourceJavaPath = getJavaPath(path, resourceType, resourcesDir)

        val escapedPath = path.escape()
        val fileContent = resourceJavaPath.getContentOrNull()
        if (fileContent == null) {
            println("cannot find resource <<<<$resourceJavaPath>>>> / <<<<${resourceJavaPath.toAbsolutePath().normalize()}>>>>")
            return null
        }

        return ResourceContext(
                path = escapedPath,
                oldPath = escapedPath,
                body = fileContent
        )
    }

    fun getResourceAtPath(path: Path,
                          resourcesDir: JavaPath): ResourceContext? {
        val resourceType = ResourceType.getByFileExtension(path)
                ?: throw java.lang.IllegalArgumentException("unrecognized resource file extension [${path.fileExtension}]]")

        val resourceJavaPath = getJavaPath(path, resourceType, resourcesDir)

        val escapedPath = path.escape()
        val fileContent = resourceJavaPath.getContentOrNull()
                ?: return null

        return ResourceContext(
                path = escapedPath,
                oldPath = escapedPath,
                body = fileContent
        )
    }

    fun save(resourceContext: ResourceContext,
             resourcesDir: JavaPath): ResourceContext {
        val resourceType = ResourceType.getByFileExtension(resourceContext.path)
                ?: throw java.lang.IllegalArgumentException("unrecognized resource file extension [${resourceContext.path.fileExtension}]]")

        validateResource(resourceType, resourceContext, resourcesDir)

        val resourceWithTransformedBody: ResourceContext = resourceContext.copy(
                body = FileArgTransformer.jsonToFileFormat(resourceContext.body, resourceType.typeMeta)
        )

        val oldPath = resourceWithTransformedBody.oldPath
        val newEscapedPath = resourceWithTransformedBody.path.escape()

        val oldResourceFile: JavaPath? = oldPath?.let {
            getJavaPath(oldPath, resourceType, resourcesDir)
        }
        val newResourceFile = getJavaPath(newEscapedPath, resourceType, resourcesDir)

        // handle rename
        oldResourceFile?.smartMoveTo(
                newResourceFile,
                createDestinationExistsException = {
                    ValidationException(
                            globalMessage = "The test at path [$newEscapedPath] already exists",
                            globalHtmlMessage = "The test at path
$newEscapedPath
already exists" ) } ) // write the new resource file newResourceFile.parent?.createDirectories() Files.write( newResourceFile, resourceWithTransformedBody.body.toByteArray(Charsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING ) return resourceContext.copy( path = newEscapedPath, oldPath = newEscapedPath ) } private fun validateResource(resourceType: ResourceType, resourceContext: ResourceContext, resourcesDir: JavaPath) { validatePathConflict(resourceType, resourceContext, resourcesDir) validateRdbmsConnectionConfig(resourceType, resourceContext) } private fun validatePathConflict(resourceType: ResourceType, resourceContext: ResourceContext, resourcesDir: JavaPath) { if (!resourceContext.isCreateResource() && !resourceContext.isRelocateResource()) { return } if (resourceContext.isRelocateResource() && resourceContext.path.toString().equals(resourceContext.oldPath.toString(), true)) { return } if (existResourceAtPath(resourceContext.path, resourceType, resourcesDir)) { throw ValidationException().addFieldValidationError( "name", "a_resource_with_the_same_name_already_exist" ) } } private fun validateRdbmsConnectionConfig(resourceType: ResourceType, resourceContext: ResourceContext) { if (resourceType != ResourceType.RDBMS_CONNECTION) { return } try { OBJECT_MAPPER.readValue(resourceContext.body) } catch (e: Exception) { throw ValidationException( globalMessage = "The following text is not a valid Rdbms Connection Config Json: ${resourceContext.body}", globalHtmlMessage = "The following text is not a valid Rdbms Connection Config Json:
${resourceContext.body}" ) } } private fun existResourceAtPath(path: Path, resourceType: ResourceType, resourcesDir: JavaPath): Boolean { val javaPath = getJavaPath(path, resourceType, resourcesDir) return javaPath.exists && javaPath.isRegularFile } fun delete(path: Path, resourcesDir: JavaPath) { val resourceType = ResourceType.getByFileExtension(path) ?: throw java.lang.IllegalArgumentException("unrecognized resource file extension [${path.fileExtension}]]") val escapedPath = path.escape() val resourceFile = getJavaPath(escapedPath, resourceType, resourcesDir) resourceFile.deleteIfExists() } fun getPathsOfSharedResources(resourceType: ResourceType, resourcesDir: JavaPath): List { val fileTypeRootPath = resourcesDir.resolve(resourceType.relativeRootDir) if (fileTypeRootPath.doesNotExist) { return emptyList() } val javaPaths = fileTypeRootPath.walkAndCollect { it.isRegularFile && it.hasExtension(resourceType.fileExtension) } return javaPaths.map { Path.createInstance( fileTypeRootPath.relativize(it).toString() ) } } fun renameDirectory(renamePath: RenamePath, resourcesDir: JavaPath): Path { val javaPathToRename = resourcesDir.resolve( renamePath.path.escape().toString() ) if (javaPathToRename.doesNotExist) { LOG.warn("ignoring attempt to rename directory that doesn't exist [$javaPathToRename]") return renamePath.path } val escapedNewName = renamePath.newName.escapeFileOrDirectoryName() val javaNewPath = javaPathToRename.resolveSibling(escapedNewName) javaPathToRename.smartMoveTo( javaNewPath, createDestinationExistsException = { ValidationException( globalMessage = "The directory at path [${javaNewPath.toAbsolutePath().normalize()}] already exists", globalHtmlMessage = "The directory at path
${javaNewPath.toAbsolutePath().normalize()}
already exists" ) } ) val javaNewRelativePath = resourcesDir.toAbsolutePath().normalize() .relativize(javaNewPath.toAbsolutePath().normalize()) return Path.createInstance( javaNewRelativePath.toString() ) } fun deleteDirectory(path: Path, resourceType: ResourceType, resourcesDir: JavaPath) { val resourceRootPath = resourcesDir.resolve(resourceType.relativeRootDir) val javaDirToDelete = resourceRootPath.resolve( path.escape().toString() ) javaDirToDelete.deleteRecursivelyIfExists() } // todo: remove duplication with ComposedStepFileService.moveComposedStepDirectoryOrFile fun moveDirectoryOrFile(copyPath: CopyPath, resourceType: ResourceType, resourcesDir: JavaPath): Path { val fileTypeRootPath = resourcesDir.resolve(resourceType.relativeRootDir) val escapedSourceFile = copyPath.copyPath.escape() val escapedDestinationPath = copyPath.destinationPath.escape() val escapedDestinationFile = if (escapedDestinationPath.isFile()) { escapedDestinationPath } else { escapedDestinationPath.copy( fileName = escapedSourceFile.fileName, fileExtension = escapedSourceFile.fileExtension ) } val sourceJavaFile = fileTypeRootPath.resolve( escapedSourceFile.toString() ) val destinationJavaFile = fileTypeRootPath.resolve( escapedDestinationFile.toString() ) sourceJavaFile.smartMoveTo( destinationJavaFile, createDestinationExistsException = { ValidationException( globalMessage = "The file at path [$escapedDestinationFile] already exists", globalHtmlMessage = "The file at path
$escapedDestinationFile
already exists" ) } ) return escapedDestinationFile } private fun getJavaPath(path: Path, resourceType: ResourceType, resourcesDir: JavaPath): JavaPath { return resourcesDir.resolve(resourceType.relativeRootDir) .resolve(path.escape().toString()) .toAbsolutePath() .normalize() } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy