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

com.ancientlightstudios.quarkus.kotlin.openapi.parser.ApiSpecBuilder.kt Maven / Gradle / Ivy

package com.ancientlightstudios.quarkus.kotlin.openapi.parser

import com.ancientlightstudios.quarkus.kotlin.openapi.models.hints.OriginPathHint.originPath
import com.ancientlightstudios.quarkus.kotlin.openapi.models.transformable.*
import com.ancientlightstudios.quarkus.kotlin.openapi.utils.SpecIssue
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.ObjectNode

class ApiSpecBuilder(
    private val spec: TransformableSpec,
    private val requestFilter: RequestFilter,
    private val contentTypeMapper: ContentTypeMapper,
    private val node: ObjectNode
) {

    private val schemaCollector = SchemaCollector()
    private val parseContext = ParseContext(
        extractOpenApiVersion(), node,
        JsonPointer.fromPath("#"), schemaCollector, contentTypeMapper
    )

    fun build() {
        val requests = extractRequests(requestFilter)
        val schemas = extractSchemas()

        // just create a default bundle with all the available requests
        spec.bundles = listOf(TransformableRequestBundle(null, requests))
        spec.schemas = schemas
        spec.version = node.with("info").getTextOrNull("version")
    }

    private fun extractOpenApiVersion() = node.getTextOrNull("openapi")?.let(ApiVersion::fromString)
        ?: SpecIssue("OpenAPI version not specified")

    private fun extractRequests(requestFilter: RequestFilter) = node.with("paths")
        .propertiesAsList()
        .map { (path, pathNode) ->
            pathNode.asObjectNode { "Json object expected for path '$path'" }
                .extractPathRequests(path, requestFilter)
        }
        .flatten()

    private fun ObjectNode.extractPathRequests(path: String, requestFilter: RequestFilter): List {
        val context = parseContext.contextFor(node, "paths", path)

        // now extract all defined operations in this path
        return propertiesAsList()
            // ignore the parameters key, as this is not a valid operation
            .filter { (name, _) -> name != "parameters" }
            // ignore all operations which are not required
            .filter { (operation, _) -> requestFilter.accept(path, RequestMethod.fromString(operation)) }
            .map { (operation, operationNode) ->
                context.contextFor(operationNode, operation).parseAsRequest(path, RequestMethod.fromString(operation)) {
                    // extract default parameters defined next to the operations
                    // this is done for every operation again to avoid side effects later in the transformation
                    withArray("parameters")
                        .mapIndexed { index, itemNode ->
                            context.contextFor(itemNode, "parameters", "$index").parseAsRequestParameter()
                        }
                }
            }
    }

    private fun extractSchemas(): List {
        val result = mutableListOf()
        var current = schemaCollector.nextUnresolvedSchema
        while (current != null) {
            parseContext.contextFor(JsonPointer.fromPath(current.originPath))
                .parseAsSchemaInto(current)
            result.add(current)
            current = schemaCollector.nextUnresolvedSchema
        }

        return result
    }

}

fun JsonNode.parseInto(spec: TransformableSpec, requestFilter: RequestFilter, contentTypeMapper: ContentTypeMapper) {
    asObjectNode { "Json object expected" }.let { ApiSpecBuilder(spec, requestFilter, contentTypeMapper, it).build() }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy