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

io.javalin.plugin.openapi.annotations.AnnotationApiMapping.kt Maven / Gradle / Ivy

The newest version!
package io.javalin.plugin.openapi.annotations

import io.javalin.plugin.openapi.dsl.DocumentedContent
import io.javalin.plugin.openapi.dsl.DocumentedParameter
import io.javalin.plugin.openapi.dsl.DocumentedResponse
import io.javalin.plugin.openapi.dsl.OpenApiDocumentation
import io.javalin.plugin.openapi.dsl.anyOf
import io.javalin.plugin.openapi.dsl.createUpdater
import io.javalin.plugin.openapi.dsl.oneOf
import io.swagger.v3.oas.models.Operation
import io.swagger.v3.oas.models.parameters.RequestBody
import io.swagger.v3.oas.models.security.SecurityRequirement
import kotlin.reflect.KClass

fun OpenApi.asOpenApiDocumentation(): OpenApiDocumentation {
    val documentation = OpenApiDocumentation()
    val annotation = this

    documentation.isIgnored = annotation.ignore

    documentation.operation { it.applyAnnotation(annotation) }

    annotation.cookies.forEach { documentation.applyParamAnnotation("cookie", it) }
    annotation.headers.forEach { documentation.applyParamAnnotation("header", it) }
    annotation.pathParams.forEach { documentation.applyParamAnnotation("path", it) }
    annotation.queryParams.forEach { documentation.applyParamAnnotation("query", it) }

    annotation.formParams.forEach { documentation.formParam(it.name, it.type.java, it.required) }

    documentation.applyRequestBodyAnnotation(annotation.requestBody)
    documentation.applyComposedRequestBodyAnnotation(annotation.composedRequestBody)

    annotation.fileUploads.forEach { fileUpload ->
        if (fileUpload.isArray) {
            documentation.uploadedFiles(name = fileUpload.name) { it.applyAnnotation(fileUpload) }
        } else {
            documentation.uploadedFile(name = fileUpload.name) { it.applyAnnotation(fileUpload) }
        }
    }
    documentation.applyResponseAnnotations(annotation.responses)

    return documentation
}

private fun resolveNullValueFromContentType(fromType: KClass<*>, contentType: String): KClass {
    return if (fromType == NULL_CLASS::class) {
        if (contentType.startsWith("text/")) {
            // Default for text/html, etc is string
            String::class
        } else {
            // Default for everything else is unit
            Unit::class
        }
    } else {
        fromType
    }
}

private fun Operation.applyAnnotation(annotation: OpenApi) {
    if (annotation.description.isNotNullString()) {
        this.description = annotation.description
    }
    if (annotation.summary.isNotNullString()) {
        this.summary = annotation.summary
    }
    if (annotation.operationId.isNotNullString()) {
        this.operationId = annotation.operationId
    }
    if (annotation.deprecated) {
        this.deprecated = annotation.deprecated
    }
    if (annotation.security.isNotEmpty()) {
        this.applySecurity(annotation.security)
    }
    annotation.tags.forEach { tag -> this.addTagsItem(tag) }
}

private fun RequestBody.applyAnnotation(annotation: OpenApiRequestBody) {
    if (annotation.required) {
        this.required = annotation.required
    }
    if (annotation.description.isNotNullString()) {
        this.description = annotation.description
    }
}

private fun RequestBody.applyAnnotation(annotation: OpenApiComposedRequestBody) {
    if (annotation.required) {
        this.required = annotation.required
    }
    if (annotation.description.isNotNullString()) {
        this.description = annotation.description
    }
}

private fun RequestBody.applyAnnotation(annotation: OpenApiFileUpload) {
    if (annotation.required) {
        this.required = annotation.required
    }
    if (annotation.description.isNotNullString()) {
        this.description = annotation.description
    }
}

private fun OpenApiContent.asDocumentedContent(): DocumentedContent {
    val content = this
    val from = resolveNullValueFromContentType(content.from, content.type)
    return DocumentedContent(
            from = from.java,
            isArray = content.isArray,
            contentType = content.type
    )
}

private fun OpenApiDocumentation.applyRequestBodyAnnotation(requestBody: OpenApiRequestBody) {
    if (requestBody.content.isNotEmpty()) {
        this.body(requestBody.content.map { it.asDocumentedContent() }, createUpdater { it.applyAnnotation(requestBody) })
    }
}

private fun OpenApiDocumentation.applyComposedRequestBodyAnnotation(requestBody: OpenApiComposedRequestBody) {
    val composition = when {
        requestBody.anyOf.isNotEmpty() -> anyOf(*requestBody.anyOf.toList().map { it.asDocumentedContent() }.toTypedArray())
        requestBody.oneOf.isNotEmpty() -> oneOf(*requestBody.oneOf.toList().map { it.asDocumentedContent() }.toTypedArray())
        else -> null
    }
    if (composition != null) {
        this.body(composition, requestBody.contentType, createUpdater { it.applyAnnotation(requestBody) })
    }
}

private fun OpenApiDocumentation.applyResponseAnnotations(responses: Array) {
    val documentation = this
    responses.groupBy { it.status }.forEach { (status, list) ->
        documentation.result(
                documentedResponse = DocumentedResponse(
                        status = status,
                        content = list.flatMap { it.content.toList() }.map { it.asDocumentedContent() }
                ),
                applyUpdates = { responseDocumentation ->
                    val description = list.map { it.description }.filter { it.isNotNullString() }.joinToString(separator = "; ")
                    if (description.isNotBlank()) {
                        responseDocumentation.description = description
                    }
                }
        )
    }
}

private fun OpenApiDocumentation.applyParamAnnotation(`in`: String, param: OpenApiParam) {
    val documentation = this
    documentation.param(
            documentedParameter = createDocumentedParam(`in`, param),
            applyUpdates = { paramDocumentation ->
                if (param.description.isNotNullString()) {
                    paramDocumentation.description = param.description
                }
                if (param.required) {
                    paramDocumentation.required = param.required
                }
                if (param.deprecated) {
                    paramDocumentation.deprecated = param.deprecated
                }
                if (param.allowEmptyValue) {
                    paramDocumentation.allowEmptyValue = param.allowEmptyValue
                }
            },
            isRepeatable = param.isRepeatable
    )
}

private fun Operation.applySecurity(securityArray: Array) {
    if (securityArray.isEmpty()) {
        return
    }

    val operation = this
    var securityRequirement = SecurityRequirement()
    securityArray.forEach {
        securityRequirement = securityRequirement.addList(it.name, it.scopes.toList())
    }
    operation.addSecurityItem(securityRequirement)
}

private fun createDocumentedParam(`in`: String, param: OpenApiParam) = DocumentedParameter(
        `in` = `in`,
        name = param.name,
        type = param.type.java
)

private fun String.isNullString() = this == NULL_STRING
private fun String.isNotNullString() = !this.isNullString()
private fun String.correctNullValue(): String? = if (this.isNullString()) null else this




© 2015 - 2025 Weber Informatics LLC | Privacy Policy