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

io.vrap.codegen.languages.bruno.model.BrunoActionRenderer.kt Maven / Gradle / Ivy

Go to download

RAML API client code generators based on the REST Modeling Framework. https://github.com/vrapio/rest-modeling-framework

The newest version!
package io.vrap.codegen.languages.bruno.model

import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.node.ObjectNode
import io.vrap.codegen.languages.extensions.*
import io.vrap.rmf.codegen.firstUpperCase
import io.vrap.rmf.codegen.io.TemplateFile
import io.vrap.rmf.codegen.rendering.FileProducer
import io.vrap.rmf.codegen.rendering.utils.keepAngleIndent
import io.vrap.rmf.codegen.types.VrapTypeProvider
import io.vrap.rmf.raml.model.modules.Api
import io.vrap.rmf.raml.model.resources.HttpMethod
import io.vrap.rmf.raml.model.resources.Method
import io.vrap.rmf.raml.model.resources.Resource
import io.vrap.rmf.raml.model.types.*
import io.vrap.rmf.raml.model.util.StringCaseFormat
import java.io.IOException

class BrunoActionRenderer(val api: Api, override val vrapTypeProvider: VrapTypeProvider) : EObjectExtensions, FileProducer {

    val offset = 1 + allResourceMethods().count()

    fun allResourceMethods(): List = api.allContainedResources.flatMap { it.methods }

    override fun produceFiles(): List {
        return updateActions(api)
    }

    fun updateActions(api: Api): List {
        val updatableResources = api.allContainedResources.filter { it.getAnnotation("updateable") != null }

        return updatableResources.flatMap { resourceUpdates(it) }
    }

    fun resourceUpdates(resource: Resource): List {
        val updateMethod = resource.getUpdateMethod()
        return updateMethod?.getActions()?.filterNot { objType -> objType.deprecated() }?.mapIndexed { index, objectType ->  renderAction(resource, updateMethod, objectType, index) } ?: return emptyList()
    }

    private fun renderAction(resource: Resource, method: Method, type: ObjectType, index: Int): TemplateFile {
        val url = BrunoUrl(method.resource(), method) { methodResource, name -> when (name) {
            "ID" -> methodResource.resourcePathName.singularize() + "-id"
            "key" -> methodResource.resourcePathName.singularize() + "-key"
            else -> StringCaseFormat.LOWER_HYPHEN_CASE.apply(name)
        }}
        val actionBody = resource.actionExample(type)
        val name = "${type.discriminatorValue.firstUpperCase()}${if (type.markDeprecated()) " (deprecated)" else ""}"
        val content = BrunoRequestRenderer.renderRequest(name, method, url, actionBody, index + offset)

        val relativePath = methodResourcePath(method) + "/Update actions/" + type.discriminatorValue.firstUpperCase() + ".bru"

        return TemplateFile(
                relativePath = relativePath,
                content = content
        )
    }

    private fun Resource.actionExample(type: ObjectType): String {
        val example = getExample(type)
        return """
            |{
            |    "version": {{${this.resourcePathName.singularize()}-version}},
            |    "actions": [
            |        <<${if (example.isNullOrEmpty().not()) example else """
            |        |{
            |        |    "action": "${type.discriminatorValue}"
            |        |}""".trimMargin()}>>
            |    ]
            |}
        """.trimMargin().keepAngleIndent()
    }

    private fun getExample(type: ObjectType): String? {
        var example: String? = null
        var instance: Instance? = null

        if (type.getAnnotation("postman-example") != null) {
            instance = type.getAnnotation("postman-example").value
        } else if (type.examples.size > 0) {
            instance = type.examples[0].value
        }

        if (instance != null) {
            example = instance.toJson()
            try {
                val mapper = ObjectMapper()
                val nodes = mapper.readTree(example) as ObjectNode
                nodes.put("action", type.discriminatorValue)

                example = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(nodes)
                    .split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray().map { s -> "  $s" }
                    .joinToString("\n")
                    .trim { it <= ' ' }
            } catch (e: IOException) {
            }

        }

        return example
    }

    private fun ObjectType.markDeprecated() : Boolean {
        val anno = this.getAnnotation("markDeprecated")
        return (anno != null && (anno.value as BooleanInstance).value)
    }

    private fun Resource.getUpdateMethod(): Method? {
        val byIdResource = this.resources.find { resource -> resource.relativeUri.template == "/{ID}" } ?: return null

        return byIdResource.getMethod(HttpMethod.POST)
    }

    private fun Method.getActions(): List {
        val body = this.getBody("application/json") ?: return emptyList()

        val actions = (body.type as ObjectType).getProperty("actions") ?: return emptyList()

        val actionsType = actions.type as ArrayType
        val updateActions = if (actionsType.items is UnionType) {
            (actionsType.items as UnionType).oneOf[0].subTypes
        } else {
            actionsType.items.subTypes
        }
        val actionItems = updateActions.map { action -> action as ObjectType }.sortedBy { action -> action.discriminatorValue }
        return actionItems
    }


    private fun methodResourcePath(method: Method): String {
        var resourcePathes = resourcePathes(method.resource())

        var directories = resourcePathes.map { it.displayName?.value ?: it.resourcePathName.firstUpperCase() }
        return directories.joinToString("/")
    }

    private fun resourcePathes(resource: Resource): List {
        if (resource.parent is Resource) {
            if (resource.resourcePathName == resource.parent.resourcePathName) {
                return resourcePathes(resource.parent)
            }
            return resourcePathes(resource.parent).plus(resource)
        }
        return listOf(resource)
    }



    fun Instance.toJson(): String {
        var example = ""
        val mapper = ObjectMapper()

        val module = SimpleModule()
        module.addSerializer(ObjectInstance::class.java, ObjectInstanceSerializer())
        module.addSerializer(ArrayInstance::class.java, InstanceSerializer())
        module.addSerializer(IntegerInstance::class.java, InstanceSerializer())
        module.addSerializer(BooleanInstance::class.java, InstanceSerializer())
        module.addSerializer(StringInstance::class.java, InstanceSerializer())
        module.addSerializer(NumberInstance::class.java, InstanceSerializer())
        mapper.registerModule(module)

        if (this is StringInstance) {
            example = this.value
        } else if (this is ObjectInstance) {
            try {
                example = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(this)
            } catch (e: JsonProcessingException) {
            }

        }

        return example
    }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy