diff --git a/README.md b/README.md index 6f3a12a..3f71ea7 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,10 @@ This library provides a Ktor plugin to document routes, generate an OpenApi Spec - minimally invasive (no immediate change to existing code required) - provides swagger-ui with no initial configuration required - supports most of the [OpenAPI 3.0.3 Specification](https://swagger.io/specification/) -- Authentication (Basic, JWT, ...) - automatic json-schema generation from arbitrary types/classes for bodies and parameters -- no annotations +- provide custom schemas or a custom schema-builder +- external/custom json-schemas for bodies +- protect Swagger-UI and OpenApi-Spec with custom authentication ## Documentation @@ -68,7 +69,7 @@ get("hello", { response { HttpStatusCode.OK to { description = "Successful Request" - body(String::class) { description = "the response" } + body { description = "the response" } } HttpStatusCode.InternalServerError to { description = "Something unexpected happened" @@ -84,15 +85,15 @@ post("math/{operation}", { tags = listOf("test") description = "Performs the given operation on the given values and returns the result" request { - pathParameter("operation", String::class) { + pathParameter("operation") { description = "the math operation to perform. Either 'add' or 'sub'" } - body(MathRequest::class) + body() } response { HttpStatusCode.OK to { description = "The operation was successful" - body(MathResult::class) { + body { description = "The result of the operation" } } diff --git a/build.gradle.kts b/build.gradle.kts index 90b6d11..dd14753 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "io.github.smiley4" -version = "0.5.2" +version = "0.6.0" repositories { mavenCentral() diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt index b773480..0ea348d 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerPlugin.kt @@ -1,22 +1,6 @@ package io.github.smiley4.ktorswaggerui import io.github.smiley4.ktorswaggerui.specbuilder.ApiSpecBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiComponentsBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiContentBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiExampleBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiInfoBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiJsonSchemaBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiOAuthFlowsBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiParametersBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiPathBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiPathsBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiRequestBodyBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiResponsesBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiSchemaBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiSecuritySchemesBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiServersBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiTagsBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.RouteCollector import io.ktor.server.application.ApplicationStarted import io.ktor.server.application.createApplicationPlugin import io.ktor.server.application.hooks.MonitoringEvent @@ -37,7 +21,7 @@ val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration if (application.pluginOrNull(Webjars) == null) { application.install(Webjars) } - apiSpecJson = getBuilder().build(application, pluginConfig) + apiSpecJson = ApiSpecBuilder().build(application, pluginConfig) } SwaggerRouting( @@ -49,48 +33,3 @@ val SwaggerUI = createApplicationPlugin(name = "SwaggerUI", createConfiguration ) { apiSpecJson }.setup(application) } - -private fun getBuilder(): ApiSpecBuilder { - return ApiSpecBuilder( - OApiInfoBuilder(), - OApiServersBuilder(), - OApiTagsBuilder(), - OApiPathsBuilder( - RouteCollector(), - OApiPathBuilder( - OApiParametersBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ) - ), - OApiRequestBodyBuilder( - OApiContentBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ), - OApiExampleBuilder() - ) - ), - OApiResponsesBuilder( - OApiContentBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ), - OApiExampleBuilder() - ), - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ) - ), - ), - ), - OApiComponentsBuilder( - OApiExampleBuilder(), - OApiSecuritySchemesBuilder( - OApiOAuthFlowsBuilder() - ) - ), - ) -} - - diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt index a3e9e7d..98e9806 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/SwaggerUIPluginConfig.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui +import io.github.smiley4.ktorswaggerui.dsl.CustomSchemas import io.github.smiley4.ktorswaggerui.dsl.OpenApiDslMarker import io.github.smiley4.ktorswaggerui.dsl.OpenApiInfo import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse @@ -132,4 +133,13 @@ class SwaggerUIPluginConfig { fun getTags(): List = tags + + private var customSchemas = CustomSchemas() + + fun schemas(block: CustomSchemas.() -> Unit) { + this.customSchemas = CustomSchemas().apply(block) + } + + fun getCustomSchemas() = customSchemas + } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemas.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemas.kt new file mode 100644 index 0000000..d22deb9 --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/CustomSchemas.kt @@ -0,0 +1,60 @@ +package io.github.smiley4.ktorswaggerui.dsl + +import io.swagger.v3.oas.models.media.Schema +import java.lang.reflect.Type + +@OpenApiDslMarker +class CustomSchemas { + + private var jsonSchemaBuilder: ((type: Type) -> String?)? = null + + + /** + * Custom builder for building json-schemas from a given type. Return null to not use this builder for the given type. + */ + fun jsonSchemaBuilder(builder: (type: Type) -> String?) { + jsonSchemaBuilder = builder + } + + fun getJsonSchemaBuilder() = jsonSchemaBuilder + + + private val customSchemas = mutableMapOf() + + + /** + * Define the json-schema for an object/body with the given id + */ + fun json(id: String, provider: () -> String) { + customSchemas[id] = CustomJsonSchema(provider) + } + + + /** + * Define the [Schema] for an object/body with the given id + */ + fun openApi(id: String, provider: () -> Schema) { + customSchemas[id] = CustomOpenApiSchema(provider) + } + + + /** + * Define the external url for an object/body with the given id + */ + fun remote(id: String, url: String) { + customSchemas[id] = RemoteSchema(url) + } + + fun getSchema(id: String): BaseCustomSchema? = customSchemas[id] + + +} + + +sealed class BaseCustomSchema + +class CustomJsonSchema(val provider: () -> String) : BaseCustomSchema() + +class CustomOpenApiSchema(val provider: () -> Schema) : BaseCustomSchema() + +class RemoteSchema(val url: String) : BaseCustomSchema() \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiBody.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiBody.kt index b140cf1..5c8f00a 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiBody.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiBody.kt @@ -15,9 +15,10 @@ class OpenApiBody( ) { /** - * url to a json-spec to use as schema for this body (alternative to 'type') + * id of a custom schema (alternative to 'type') */ - var externalSchemaUrl: String? = null + var customSchemaId: String? = null + /** * A brief description of the request body diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRequest.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRequest.kt index b43c80c..f836797 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRequest.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiRequest.kt @@ -165,9 +165,9 @@ class OpenApiRequest { /** * The body returned with this response */ - fun body(schemaUrl: String, block: OpenApiBody.() -> Unit) { + fun body(customSchemaId: String, block: OpenApiBody.() -> Unit) { body = OpenApiBody(null).apply(block).apply { - externalSchemaUrl = schemaUrl + this.customSchemaId = customSchemaId } } @@ -175,7 +175,7 @@ class OpenApiRequest { /** * The body returned with this response */ - fun body(schemaUrl: String) = body(schemaUrl) {} + fun body(customSchemaId: String) = body(customSchemaId) {} /** diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiResponse.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiResponse.kt index 5f470ae..4d6350e 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiResponse.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/dsl/OpenApiResponse.kt @@ -104,7 +104,7 @@ class OpenApiResponse(val statusCode: String) { */ fun body(schemaUrl: String, block: OpenApiBody.() -> Unit) { body = OpenApiBody(null).apply(block).apply { - externalSchemaUrl = schemaUrl + customSchemaId = schemaUrl } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/ApiSpecBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/ApiSpecBuilder.kt index 9e0111e..d7ca0ec 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/ApiSpecBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/ApiSpecBuilder.kt @@ -3,19 +3,19 @@ package io.github.smiley4.ktorswaggerui.specbuilder import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.ktor.server.application.Application import io.swagger.v3.core.util.Json -import io.swagger.v3.oas.models.Components import io.swagger.v3.oas.models.OpenAPI /** * Build the OpenApi-json for the given application */ -class ApiSpecBuilder( - private val infoBuilder: OApiInfoBuilder, - private val serversBuilder: OApiServersBuilder, - private val tagsBuilder: OApiTagsBuilder, - private val pathsBuilder: OApiPathsBuilder, - private val componentsBuilder: OApiComponentsBuilder -) { +class ApiSpecBuilder { + + private val infoBuilder = OApiInfoBuilder() + private val serversBuilder = OApiServersBuilder() + private val tagsBuilder = OApiTagsBuilder() + private val pathsBuilder = OApiPathsBuilder(RouteCollector()) + private val componentsBuilder = OApiComponentsBuilder() + fun build(application: Application, config: SwaggerUIPluginConfig): String { val componentCtx = ComponentsContext( diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/ComponentsContext.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/ComponentsContext.kt index 774cfcd..03df0e9 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/ComponentsContext.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/ComponentsContext.kt @@ -24,37 +24,64 @@ data class ComponentsContext( /** * Add the given schema for the given type to the components-section. * The schema is an array, only the element type is added to the components-section - * @return the ref-string for the schema + * @return a schema referencing the complete schema (or the original schema if 'schemasInComponents' = false) */ - fun addArraySchema(type: Type, schema: Schema<*>): String { - return when (type) { - is Class<*> -> addSchema(type.componentType, schema.items) - is ParameterizedType -> { - when (val actualTypeArgument = type.actualTypeArguments.firstOrNull()) { - is Class<*> -> addSchema(actualTypeArgument, schema.items) - is WildcardType -> { - addSchema(actualTypeArgument.upperBounds.first(), schema.items) + fun addArraySchema(type: Type, schema: Schema<*>): Schema { + if (this.schemasInComponents) { + val innerSchema: Schema = when (type) { + is Class<*> -> addSchema(type.componentType, schema.items) + is ParameterizedType -> { + when (val actualTypeArgument = type.actualTypeArguments.firstOrNull()) { + is Class<*> -> addSchema(actualTypeArgument, schema.items) + is WildcardType -> { + addSchema(actualTypeArgument.upperBounds.first(), schema.items) + } + else -> throw Exception("Could not add array-schema to components ($type)") } - else -> throw Exception("Could not add array-schema to components ($type)") + } + else -> throw Exception("Could not add array-schema to components ($type)") + } + return Schema().apply { + this.type = "array" + this.items = Schema().apply { + `$ref` = innerSchema.`$ref` } } - else -> throw Exception("Could not add array-schema to components ($type)") + } else { + @Suppress("UNCHECKED_CAST") + return schema as Schema } } /** * Add the given schema for the given type to the components-section - * @return the ref-string for the schema + * @return a schema referencing the complete schema (or the original schema if 'schemasInComponents' = false) + */ + fun addSchema(type: Type, schema: Schema<*>): Schema { + return addSchema(getIdentifyingName(type), schema) + } + + + /** + * Add the given schema for the given type to the components-section + * @return a schema referencing the complete schema (or the original schema if 'schemasInComponents' = false) */ - fun addSchema(type: Type, schema: Schema<*>): String { - val key = getIdentifyingName(type) - if (!schemas.containsKey(key)) { - schemas[key] = schema + fun addSchema(id: String, schema: Schema<*>): Schema { + if (schemasInComponents) { + if (!schemas.containsKey(id)) { + schemas[id] = schema + } + return Schema().apply { + `$ref` = asSchemaRef(id) + } + } else { + @Suppress("UNCHECKED_CAST") + return schema as Schema } - return asSchemaRef(key) } + private fun getIdentifyingName(type: Type): String { return when (type) { is Class<*> -> type.canonicalName diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/JsonToOpenApiSchemaConverter.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/JsonToOpenApiSchemaConverter.kt new file mode 100644 index 0000000..0c165bd --- /dev/null +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/JsonToOpenApiSchemaConverter.kt @@ -0,0 +1,37 @@ +package io.github.smiley4.ktorswaggerui.specbuilder + +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import io.swagger.v3.oas.models.media.Schema + +class JsonToOpenApiSchemaConverter { + + fun toSchema(json: String) = toSchema(ObjectMapper().readTree(json)) + + + fun toSchema(node: JsonNode): Schema { + return Schema().apply { + node["\$schema"]?.let { this.`$schema` = it.asText() } + node["type"]?.let { this.type = it.asText() } + node["items"]?.let { this.items = toSchema(it) } + node["properties"]?.let { this.properties = it.collectFields().associate { prop -> prop.key to toSchema(prop.value) } } + node["allOf"]?.let { this.allOf = it.collectElements().map { prop -> toSchema(prop) } } + node["anyOf"]?.let { this.anyOf = it.collectElements().map { prop -> toSchema(prop) } } + node["required"]?.let { this.required = it.collectElements().map { prop -> prop.asText() } } + node["const"]?.let { this.setConst(it.asText()) } + node["\$defs"]?.let { throw UnsupportedOperationException("'\"defs' in json-schema are not supported") } + node["\$ref"]?.let { throw UnsupportedOperationException("'\"refs' in json-schema are not supported") } + } + } + + + private fun JsonNode.collectFields(): List> { + return this.fields().asSequence().toList() + } + + + private fun JsonNode.collectElements(): List { + return this.elements().asSequence().toList() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiComponentsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiComponentsBuilder.kt index d8bdae1..92e1936 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiComponentsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiComponentsBuilder.kt @@ -6,10 +6,10 @@ import io.swagger.v3.oas.models.Components /** * Builder for the OpenAPI Components Object */ -class OApiComponentsBuilder( - private val exampleBuilder: OApiExampleBuilder, - private val securitySchemesBuilder: OApiSecuritySchemesBuilder -) { +class OApiComponentsBuilder { + + private val exampleBuilder = OApiExampleBuilder() + private val securitySchemesBuilder = OApiSecuritySchemesBuilder() fun build(ctx: ComponentsContext, securitySchemes: List): Components { return Components().apply { diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiContentBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiContentBuilder.kt index 22a915b..8ae4f0f 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiContentBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiContentBuilder.kt @@ -1,7 +1,12 @@ package io.github.smiley4.ktorswaggerui.specbuilder +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.CustomJsonSchema +import io.github.smiley4.ktorswaggerui.dsl.CustomOpenApiSchema +import io.github.smiley4.ktorswaggerui.dsl.CustomSchemas import io.github.smiley4.ktorswaggerui.dsl.OpenApiBody import io.github.smiley4.ktorswaggerui.dsl.OpenApiExample +import io.github.smiley4.ktorswaggerui.dsl.RemoteSchema import io.ktor.http.ContentType import io.swagger.v3.oas.models.media.Content import io.swagger.v3.oas.models.media.MediaType @@ -10,16 +15,18 @@ import io.swagger.v3.oas.models.media.XML import java.lang.reflect.Type /** - * Generator for the OpenAPI Content Object (e.g. request and response bodies) + * Builder for the OpenAPI Content Object (e.g. request and response bodies) */ -class OApiContentBuilder( - private val schemaBuilder: OApiSchemaBuilder, - private val exampleBuilder: OApiExampleBuilder -) { +class OApiContentBuilder { - fun build(body: OpenApiBody, components: ComponentsContext): Content { + private val schemaBuilder = OApiSchemaBuilder() + private val exampleBuilder = OApiExampleBuilder() + private val jsonToSchemaConverter = JsonToOpenApiSchemaConverter() + + + fun build(body: OpenApiBody, components: ComponentsContext, config: SwaggerUIPluginConfig): Content { return Content().apply { - val maybeSchemaObj = buildSchema(body, components) + val maybeSchemaObj = buildSchema(body, components, config) body.getMediaTypes().forEach { mediaType -> if (maybeSchemaObj == null) { addMediaType(mediaType.toString(), MediaType()) @@ -34,26 +41,42 @@ class OApiContentBuilder( } - private fun buildSchema(body: OpenApiBody, components: ComponentsContext): Schema? { - return if (body.externalSchemaUrl != null) { - buildSchemaFromExternal(body.externalSchemaUrl!!) + private fun buildSchema(body: OpenApiBody, components: ComponentsContext, config: SwaggerUIPluginConfig): Schema? { + return if (body.customSchemaId != null) { + buildSchemaFromCustom(body.customSchemaId!!, components, config.getCustomSchemas()) } else { - buildSchemaFromType(body.type, components) + buildSchemaFromType(body.type, components, config) } } - private fun buildSchemaFromType(type: Type?, components: ComponentsContext): Schema? { + private fun buildSchemaFromType(type: Type?, components: ComponentsContext, config: SwaggerUIPluginConfig): Schema? { return type - ?.let { schemaBuilder.build(it, components) } + ?.let { schemaBuilder.build(it, components, config) } ?.let { prepareForXml(type, it) } } - private fun buildSchemaFromExternal(url: String): Schema { - return Schema().apply { - type = "object" - `$ref` = url + private fun buildSchemaFromCustom(customSchemaId: String, components: ComponentsContext, customSchemas: CustomSchemas): Schema { + val custom = customSchemas.getSchema(customSchemaId) + if (custom == null) { + return Schema() + } else { + return when (custom) { + is CustomJsonSchema -> { + val schema = jsonToSchemaConverter.toSchema(custom.provider()) + components.addSchema(customSchemaId, schema) + } + is CustomOpenApiSchema -> { + components.addSchema(customSchemaId, custom.provider()) + } + is RemoteSchema -> { + Schema().apply { + type = "object" + `$ref` = custom.url + } + } + } } } @@ -76,6 +99,7 @@ class OApiContentBuilder( "string" -> ContentType.Text.Plain "object" -> ContentType.Application.Json "array" -> ContentType.Application.Json + null -> ContentType.Application.Json else -> ContentType.Text.Plain } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiJsonSchemaBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiJsonSchemaBuilder.kt index 6448a73..a26411b 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiJsonSchemaBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiJsonSchemaBuilder.kt @@ -1,6 +1,6 @@ package io.github.smiley4.ktorswaggerui.specbuilder -import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.ObjectNode import com.github.victools.jsonschema.generator.Option import com.github.victools.jsonschema.generator.OptionPreset @@ -8,35 +8,36 @@ import com.github.victools.jsonschema.generator.SchemaGenerator import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder import com.github.victools.jsonschema.generator.SchemaVersion import com.github.victools.jsonschema.module.jackson.JacksonModule +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.swagger.v3.oas.models.media.Schema -import io.swagger.v3.parser.util.SchemaTypeUtil.createSchema import java.lang.reflect.Type -import kotlin.reflect.KClass /** * Builder for an OpenAPI Schema Object that describes a json-object (or array) */ class OApiJsonSchemaBuilder { - fun build(type: Type, components: ComponentsContext): Schema { + private val jsonToSchemaConverter = JsonToOpenApiSchemaConverter() + + fun build(type: Type, components: ComponentsContext, config: SwaggerUIPluginConfig): Schema { if (components.schemasInComponents) { - val schema = createSchema(type) + val schema = createSchema(type, config) if (schema.type == "array") { - return arrayRefSchema(components.addArraySchema(type, schema)) + return components.addArraySchema(type, schema) } else { - return refSchema(components.addSchema(type, schema)) + return components.addSchema(type, schema) } } else { - return createSchema(type) + return createSchema(type, config) } } - private fun createSchema(type: Type): Schema { + private fun createSchema(type: Type, config: SwaggerUIPluginConfig): Schema { return if (type is Class<*> && type.isArray) { Schema().apply { this.type = "array" - this.items = createObjectSchema(type.componentType) + this.items = createObjectSchema(type.componentType, config) } } else if (type is Class<*> && type.isEnum) { Schema().apply { @@ -44,49 +45,33 @@ class OApiJsonSchemaBuilder { this.enum = type.enumConstants.map { it.toString() } } } else { - return createObjectSchema(type) + return createObjectSchema(type, config) } } - private fun createObjectSchema(type: Type): Schema { + private fun createObjectSchema(type: Type, config: SwaggerUIPluginConfig): Schema { return if (type is Class<*> && type.isEnum) { Schema().apply { this.type = "string" this.enum = type.enumConstants.map { it.toString() } } } else { - return toSchema(generateJsonSchema(type)) + val jsonSchema = createObjectJsonSchema(type, config) + return jsonToSchemaConverter.toSchema(jsonSchema) } } - - private fun toSchema(node: JsonNode): Schema { - return Schema().apply { - node["\$schema"]?.let { this.`$schema` = it.asText() } - node["type"]?.let { this.type = it.asText() } - node["items"]?.let { this.items = toSchema(it) } - node["properties"]?.let { this.properties = it.collectFields().associate { prop -> prop.key to toSchema(prop.value) } } - node["allOf"]?.let { this.allOf = it.collectElements().map { prop -> toSchema(prop) } } - node["anyOf"]?.let { this.anyOf = it.collectElements().map { prop -> toSchema(prop) } } - node["required"]?.let { this.required = it.collectElements().map { prop -> prop.asText() } } - node["const"]?.let { this.setConst(it.asText()) } - node["\$defs"]?.let { throw UnsupportedOperationException("'\"defs' in json-schema are not supported") } - node["\$ref"]?.let { throw UnsupportedOperationException("'\"refs' in json-schema are not supported") } + private fun createObjectJsonSchema(type: Type, config: SwaggerUIPluginConfig): ObjectNode { + if (config.getCustomSchemas().getJsonSchemaBuilder() != null) { + val jsonSchema = config.getCustomSchemas().getJsonSchemaBuilder()?.let { it(type) } + if (jsonSchema != null) { + return ObjectMapper().readTree(jsonSchema) as ObjectNode + } } + return generateJsonSchema(type) } - - private fun JsonNode.collectFields(): List> { - return this.fields().asSequence().toList() - } - - - private fun JsonNode.collectElements(): List { - return this.elements().asSequence().toList() - } - - private fun generateJsonSchema(type: Type): ObjectNode { val config = SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON) .with(JacksonModule()) @@ -98,21 +83,4 @@ class OApiJsonSchemaBuilder { return SchemaGenerator(config).generateSchema(type) } - - private fun refSchema(key: String): Schema { - return Schema().apply { - `$ref` = key - } - } - - - private fun arrayRefSchema(key: String): Schema { - return Schema().apply { - type = "array" - items = Schema().apply { - `$ref` = key - } - } - } - } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiParametersBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiParametersBuilder.kt index 3053527..2e56315 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiParametersBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiParametersBuilder.kt @@ -1,16 +1,18 @@ package io.github.smiley4.ktorswaggerui.specbuilder +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiRequestParameter import io.swagger.v3.oas.models.parameters.Parameter /** * Builder for OpenAPI Parameters */ -class OApiParametersBuilder( - private val schemaBuilder: OApiSchemaBuilder -) { +class OApiParametersBuilder { - fun build(parameters: List): List { + private val schemaBuilder = OApiSchemaBuilder() + + + fun build(parameters: List, config: SwaggerUIPluginConfig): List { return parameters.map { parameter -> Parameter().apply { `in` = when (parameter.location) { @@ -19,7 +21,7 @@ class OApiParametersBuilder( OpenApiRequestParameter.Location.PATH -> "path" } name = parameter.name - schema = schemaBuilder.build(parameter.type, ComponentsContext.NOOP) + schema = schemaBuilder.build(parameter.type, ComponentsContext.NOOP, config) description = parameter.description required = parameter.required deprecated = parameter.deprecated diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathBuilder.kt index 65087b2..d289d20 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathBuilder.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.specbuilder +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse import io.ktor.http.HttpMethod import io.ktor.http.HttpStatusCode @@ -11,41 +12,36 @@ import io.swagger.v3.oas.models.security.SecurityRequirement /** * Builder for a single OpenAPI Path */ -class OApiPathBuilder( - private val parametersBuilder: OApiParametersBuilder, - private val requestBodyBuilder: OApiRequestBodyBuilder, - private val responsesBuilder: OApiResponsesBuilder -) { +class OApiPathBuilder { - fun build( - route: RouteMeta, - defaultUnauthorizedResponse: OpenApiResponse?, - defaultSecurityScheme: String?, - tagGenerator: ((url: List) -> String?)?, - components: ComponentsContext - ): Pair { + private val parametersBuilder = OApiParametersBuilder() + private val requestBodyBuilder = OApiRequestBodyBuilder() + private val responsesBuilder = OApiResponsesBuilder() + + + fun build(route: RouteMeta, components: ComponentsContext, config: SwaggerUIPluginConfig): Pair { return route.path to PathItem().apply { val operation = Operation().apply { - tags = buildTags(route, tagGenerator) + tags = buildTags(route, config.automaticTagGenerator) summary = route.documentation.summary description = route.documentation.description operationId = route.documentation.operationId - parameters = parametersBuilder.build(route.documentation.getRequest().getParameters()) + parameters = parametersBuilder.build(route.documentation.getRequest().getParameters(), config) route.documentation.getRequest().getBody()?.let { - requestBody = requestBodyBuilder.build(it, components) + requestBody = requestBodyBuilder.build(it, components, config) } responses = ApiResponses().apply { - responsesBuilder.build(route.documentation.getResponses().getResponses(), components).forEach { + responsesBuilder.build(route.documentation.getResponses().getResponses(), components, config).forEach { addApiResponse(it.first, it.second) } - if (shouldAddUnauthorized(route, defaultUnauthorizedResponse)) { - responsesBuilder.build(listOf(defaultUnauthorizedResponse!!), components).forEach { + if (shouldAddUnauthorized(route, config.getDefaultUnauthorizedResponse())) { + responsesBuilder.build(listOf(config.getDefaultUnauthorizedResponse()!!), components, config).forEach { addApiResponse(it.first, it.second) } } } if (route.protected) { - (route.documentation.securitySchemeName ?: defaultSecurityScheme)?.let { schemeName -> + (route.documentation.securitySchemeName ?: config.defaultSecuritySchemeName)?.let { schemeName -> security = mutableListOf( SecurityRequirement().apply { addList(schemeName, emptyList()) diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathsBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathsBuilder.kt index d7338cb..4264406 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathsBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiPathsBuilder.kt @@ -9,14 +9,11 @@ import mu.KotlinLogging /** * Builder for the OpenAPI Paths */ -class OApiPathsBuilder( - private val routeCollector: RouteCollector, - private val pathBuilder: OApiPathBuilder -) { +class OApiPathsBuilder(private val routeCollector: RouteCollector) { + private val pathBuilder = OApiPathBuilder() private val logger = KotlinLogging.logger {} - fun build(config: SwaggerUIPluginConfig, application: Application, components: ComponentsContext): Paths { return Paths().apply { routeCollector.collectRoutes(application) @@ -31,13 +28,7 @@ class OApiPathsBuilder( } .onEach { logger.debug("Configure path: ${it.method.value} ${it.path}") } .map { - pathBuilder.build( - it, - config.getDefaultUnauthorizedResponse(), - config.defaultSecuritySchemeName, - config.automaticTagGenerator, - components - ) + pathBuilder.build(it, components, config) } .forEach { addToPaths(this, it.first, it.second) } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiRequestBodyBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiRequestBodyBuilder.kt index 0462377..0261748 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiRequestBodyBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiRequestBodyBuilder.kt @@ -1,20 +1,21 @@ package io.github.smiley4.ktorswaggerui.specbuilder +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiBody import io.swagger.v3.oas.models.parameters.RequestBody /** * Builder for the OpenAPI Request Body */ -class OApiRequestBodyBuilder( - private val contentBuilder: OApiContentBuilder -) { +class OApiRequestBodyBuilder { - fun build(body: OpenApiBody, components: ComponentsContext): RequestBody { + private val contentBuilder = OApiContentBuilder() + + fun build(body: OpenApiBody, components: ComponentsContext, config: SwaggerUIPluginConfig): RequestBody { return RequestBody().apply { description = body.description required = body.required - content = contentBuilder.build(body, components) + content = contentBuilder.build(body, components, config) } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiResponsesBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiResponsesBuilder.kt index 482faa3..7e1f7d7 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiResponsesBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiResponsesBuilder.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.specbuilder +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse import io.ktor.client.utils.EmptyContent.headers import io.swagger.v3.oas.models.headers.Header @@ -8,24 +9,25 @@ import io.swagger.v3.oas.models.responses.ApiResponse /** * Builder for the OpenAPI Responses */ -class OApiResponsesBuilder( - private val contentBuilder: OApiContentBuilder, - private val schemaBuilder: OApiSchemaBuilder -) { +class OApiResponsesBuilder { - fun build(responses: List, components: ComponentsContext): List> { + private val contentBuilder = OApiContentBuilder() + private val schemaBuilder = OApiSchemaBuilder() + + + fun build(responses: List, components: ComponentsContext, config: SwaggerUIPluginConfig): List> { return responses.map { responseCfg -> responseCfg.statusCode to ApiResponse().apply { description = responseCfg.description responseCfg.getBody()?.let { - content = contentBuilder.build(responseCfg.getBody()!!, components) + content = contentBuilder.build(responseCfg.getBody()!!, components, config) } headers = responseCfg.getHeaders().mapValues { Header().apply { description = it.value.description required = it.value.required deprecated = it.value.deprecated - schema = it.value.type?.let { t -> schemaBuilder.build(t, ComponentsContext.NOOP) } + schema = it.value.type?.let { t -> schemaBuilder.build(t, ComponentsContext.NOOP, config) } } } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiSchemaBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiSchemaBuilder.kt index 8765613..bf8278f 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiSchemaBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiSchemaBuilder.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.specbuilder +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.swagger.v3.oas.models.media.Schema import java.lang.reflect.Type import java.math.BigDecimal @@ -8,11 +9,12 @@ import kotlin.reflect.KClass /** * Builder for an OpenAPI Schema Object */ -class OApiSchemaBuilder( - private val jsonSchemaBuilder: OApiJsonSchemaBuilder -) { +class OApiSchemaBuilder { - fun build(type: Type, components: ComponentsContext): Schema { + private val jsonSchemaBuilder = OApiJsonSchemaBuilder() + + + fun build(type: Type, components: ComponentsContext, config: SwaggerUIPluginConfig): Schema { return Schema().apply { when (type) { Byte::class.java -> { @@ -168,7 +170,7 @@ class OApiSchemaBuilder( } } else -> { - return jsonSchemaBuilder.build(type, components) + return jsonSchemaBuilder.build(type, components, config) } } } diff --git a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiSecuritySchemesBuilder.kt b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiSecuritySchemesBuilder.kt index 2b7fa91..2342aeb 100644 --- a/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiSecuritySchemesBuilder.kt +++ b/src/main/kotlin/io/github/smiley4/ktorswaggerui/specbuilder/OApiSecuritySchemesBuilder.kt @@ -1,4 +1,5 @@ package io.github.smiley4.ktorswaggerui.specbuilder + import io.github.smiley4.ktorswaggerui.dsl.AuthKeyLocation import io.github.smiley4.ktorswaggerui.dsl.AuthScheme import io.github.smiley4.ktorswaggerui.dsl.AuthType @@ -8,9 +9,9 @@ import io.swagger.v3.oas.models.security.SecurityScheme /** * Builder for OpenAPI SecurityScheme-Objects */ -class OApiSecuritySchemesBuilder( - private val authFlowsBuilder: OApiOAuthFlowsBuilder -) { +class OApiSecuritySchemesBuilder { + + private val authFlowsBuilder = OApiOAuthFlowsBuilder() fun build(securitySchemes: List): Map { return mutableMapOf().apply { diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/ExternalJsonSchemaExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomJsonSchemaBuilderExample.kt similarity index 57% rename from src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/ExternalJsonSchemaExample.kt rename to src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomJsonSchemaBuilderExample.kt index c484310..f38b1c7 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/ExternalJsonSchemaExample.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomJsonSchemaBuilderExample.kt @@ -13,74 +13,69 @@ import io.ktor.server.application.call import io.ktor.server.application.install import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty +import io.ktor.server.request.receive import io.ktor.server.response.respond -import io.ktor.server.routing.get -import io.ktor.server.routing.route import io.ktor.server.routing.routing -import kotlin.reflect.KClass +import java.lang.reflect.Type /** - * An example for using external json-schemas + * An example for building custom json-schemas */ fun main() { - embeddedServer(Netty, port = 8080, host = "localhost") { - install(SwaggerUI) { - // don't show the routes providing the json-schemas - pathFilter = { _, url -> url.firstOrNull() != "schemas" } - } + data class MyRequestData( + val someText: String, + val someBoolean: Boolean + ) - routing { - route("schemas") { - get("myRequestData") { - // respond with a json-schema, e.g. loaded from a file - call.respond(HttpStatusCode.OK, buildJsonSchema(MyRequestData::class)) - } - get("myResponseData") { - // respond with a json-schema, e.g. loaded from a file - call.respond(HttpStatusCode.OK, buildJsonSchema(MyResponseData::class)) + data class MyResponseData( + val someText: String, + val someNumber: Long + ) + + + embeddedServer(Netty, port = 8080, host = "localhost") { + + install(SwaggerUI) { + schemas { + jsonSchemaBuilder { type -> + // custom converter from the given 'type' to a json-schema + typeToJsonSchema(type) } } + } + routing { get("something", { request { - body("/schemas/myRequestData") + body() } response { HttpStatusCode.OK to { - body("http://localhost:8080/schemas/myResponseData") + body() } } }) { - call.respond(HttpStatusCode.OK, MyResponseData("Hello", 42)) + val text = call.receive().someText + call.respond(HttpStatusCode.OK, MyResponseData(text, 42)) } - } }.start(wait = true) } -data class MyRequestData( - val someText: String, - val someBoolean: Boolean -) - -data class MyResponseData( - val someText: String, - val someNumber: Long -) - -fun buildJsonSchema(type: KClass<*>): String { +fun typeToJsonSchema(type: Type): String { return SchemaGenerator( SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON) .with(JacksonModule()) .without(Option.DEFINITIONS_FOR_ALL_OBJECTS) .with(Option.INLINE_ALL_SCHEMAS) - .with(Option.ALLOF_CLEANUP_AT_THE_END) .with(Option.EXTRA_OPEN_API_FORMAT_VALUES) + .with(Option.ALLOF_CLEANUP_AT_THE_END) .build() - ).generateSchema(type.java).toString() + ) + .generateSchema(type) + .toString() } - diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomJsonSchemaExample.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomJsonSchemaExample.kt new file mode 100644 index 0000000..7c4dca4 --- /dev/null +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/examples/CustomJsonSchemaExample.kt @@ -0,0 +1,101 @@ +package io.github.smiley4.ktorswaggerui.examples + +import io.github.smiley4.ktorswaggerui.SwaggerUI +import io.github.smiley4.ktorswaggerui.dsl.get +import io.ktor.http.HttpStatusCode +import io.ktor.server.application.call +import io.ktor.server.application.install +import io.ktor.server.engine.embeddedServer +import io.ktor.server.netty.Netty +import io.ktor.server.request.receive +import io.ktor.server.response.respond +import io.ktor.server.response.respondText +import io.ktor.server.routing.routing +import io.swagger.v3.oas.models.media.Schema + +/** + * An example for defining custom json-schemas + */ +fun main() { + + data class MyRequestData( + val someText: String, + val someBoolean: Boolean + ) + + + data class MyResponseData( + val someText: String, + val someNumber: Long + ) + + + embeddedServer(Netty, port = 8080, host = "localhost") { + + install(SwaggerUI) { + // don't show the test-routes providing json-schemas + pathFilter = { _, url -> url.firstOrNull() != "schema" } + schemasInComponentSection + schemas { + // specify a custom json-schema with the id 'myRequestData' + json("myRequestData") { + """ + { + "type": "object", + "properties": { + "someBoolean": { + "type": "boolean" + }, + "someText": { + "type": "string" + } + } + } + """.trimIndent() + } + // specify a remote json-schema with the id 'myRequestData' + remote("myResponseData", "http://localhost:8080/schema/myResponseData") + } + } + + routing { + + get("something", { + request { + // body referencing the custom schema with id 'myRequestData' + body("myRequestData") + } + response { + HttpStatusCode.OK to { + // body referencing the custom schema with id 'myResponseData' + body("myResponseData") + } + } + }) { + val text = call.receive().someText + call.respond(HttpStatusCode.OK, MyResponseData(text, 42)) + } + + // route providing a json-schema + get("schema/myResponseData") { + call.respondText( + """ + { + "type": "object", + "properties": { + "someNumber": { + "type": "integer", + "format": "int64" + }, + "someText": { + "type": "string" + } + } + } + """.trimIndent() + ) + } + } + + }.start(wait = true) +} diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/BuilderUtils.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/BuilderUtils.kt index 2e4a4c8..dd9e647 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/BuilderUtils.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/BuilderUtils.kt @@ -5,168 +5,30 @@ import io.github.smiley4.ktorswaggerui.specbuilder.OApiComponentsBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiContentBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiExampleBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiInfoBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiJsonSchemaBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiOAuthFlowsBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiParametersBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiPathBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiPathsBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiRequestBodyBuilder -import io.github.smiley4.ktorswaggerui.specbuilder.OApiResponsesBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiSchemaBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiSecuritySchemesBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiServersBuilder import io.github.smiley4.ktorswaggerui.specbuilder.OApiTagsBuilder import io.github.smiley4.ktorswaggerui.specbuilder.RouteCollector -fun getApiSpecBuilder(): ApiSpecBuilder { - return ApiSpecBuilder( - OApiInfoBuilder(), - OApiServersBuilder(), - OApiTagsBuilder(), - OApiPathsBuilder( - RouteCollector(), - OApiPathBuilder( - OApiParametersBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ) - ), - OApiRequestBodyBuilder( - OApiContentBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ), - OApiExampleBuilder() - ) - ), - OApiResponsesBuilder( - OApiContentBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ), - OApiExampleBuilder() - ), - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ) - ), - ), - ), - OApiComponentsBuilder( - OApiExampleBuilder(), - OApiSecuritySchemesBuilder( - OApiOAuthFlowsBuilder() - ), - ), - ) -} +fun getOApiInfoBuilder() = OApiInfoBuilder() +fun getOApiComponentsBuilder() = OApiComponentsBuilder() -fun getOApiInfoBuilder(): OApiInfoBuilder { - return OApiInfoBuilder() -} +fun getOApiSchemaBuilder() = OApiSchemaBuilder() -fun getOApiComponentsBuilder(): OApiComponentsBuilder { - return OApiComponentsBuilder( - OApiExampleBuilder(), - OApiSecuritySchemesBuilder( - OApiOAuthFlowsBuilder() - ) - ) -} +fun getOApiExampleBuilder() = OApiExampleBuilder() +fun getOApiContentBuilder() = OApiContentBuilder() -fun getOApiSchemaBuilder(): OApiSchemaBuilder { - return OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ) -} +fun getOApiPathBuilder() = OApiPathBuilder() -fun getOApiExampleBuilder(): OApiExampleBuilder { - return OApiExampleBuilder() -} +fun getOApiPathsBuilder(routeCollector: RouteCollector) = OApiPathsBuilder(routeCollector) -fun getOApiContentBuilder(): OApiContentBuilder { - return OApiContentBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ), - OApiExampleBuilder() - ) -} +fun getOApiSecuritySchemesBuilder() = OApiSecuritySchemesBuilder() -fun getOApiPathBuilder(): OApiPathBuilder { - return OApiPathBuilder( - OApiParametersBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ) - ), - OApiRequestBodyBuilder( - OApiContentBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ), - OApiExampleBuilder() - ) - ), - OApiResponsesBuilder( - OApiContentBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ), - OApiExampleBuilder() - ), - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ) - ), - ) -} +fun getOApiServersBuilder() = OApiServersBuilder() - -fun getOApiPathsBuilder(routeCollector: RouteCollector): OApiPathsBuilder { - return OApiPathsBuilder( - routeCollector, - OApiPathBuilder( - OApiParametersBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ) - ), - OApiRequestBodyBuilder( - OApiContentBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ), - OApiExampleBuilder() - ) - ), - OApiResponsesBuilder( - OApiContentBuilder( - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ), - OApiExampleBuilder() - ), - OApiSchemaBuilder( - OApiJsonSchemaBuilder() - ) - ), - ), - ) -} - -fun getOApiSecuritySchemesBuilder(): OApiSecuritySchemesBuilder { - return OApiSecuritySchemesBuilder( - OApiOAuthFlowsBuilder() - ) -} - -fun getOApiServersBuilder(): OApiServersBuilder { - return OApiServersBuilder() -} - -fun getOApiTagsBuilder(): OApiTagsBuilder { - return OApiTagsBuilder() -} \ No newline at end of file +fun getOApiTagsBuilder() = OApiTagsBuilder() \ No newline at end of file diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ComponentsObjectTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ComponentsObjectTest.kt index b4a0063..27d7117 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ComponentsObjectTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ComponentsObjectTest.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.tests +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiExample import io.github.smiley4.ktorswaggerui.specbuilder.ComponentsContext import io.kotest.core.spec.style.StringSpec @@ -205,7 +206,7 @@ class ComponentsObjectTest : StringSpec({ } private fun buildSchema(type: KClass<*>, context: ComponentsContext): Schema<*> { - return getOApiSchemaBuilder().build(type.java, context) + return getOApiSchemaBuilder().build(type.java, context, SwaggerUIPluginConfig()) } private fun buildExample(name: String, example: Any, context: ComponentsContext): Example { diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ContentObjectTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ContentObjectTest.kt index f50d730..6af5e42 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ContentObjectTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/ContentObjectTest.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.tests +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.dsl.OpenApiBody import io.github.smiley4.ktorswaggerui.specbuilder.ComponentsContext import io.kotest.core.spec.style.StringSpec @@ -99,8 +100,8 @@ class ContentObjectTest : StringSpec({ } } - "test content object with external json-schema" { - val content = buildExternalContentObject("/my/test/schema") + "test content object with custom (remote) json-schema" { + val content = buildCustomContentObject("remote") content shouldBeContent { addMediaType(ContentType.Application.Json.toString(), MediaType().apply { schema = Schema().apply { @@ -111,8 +112,8 @@ class ContentObjectTest : StringSpec({ } } - "test content object with external json-schema and components-section enabled" { - val content = buildExternalContentObject("/my/test/schema", ComponentsContext(true, mutableMapOf(), true, mutableMapOf())) + "test content object with custom (remote) json-schema and components-section enabled" { + val content = buildCustomContentObject("remote", ComponentsContext(true, mutableMapOf(), true, mutableMapOf())) content shouldBeContent { addMediaType(ContentType.Application.Json.toString(), MediaType().apply { schema = Schema().apply { @@ -123,10 +124,61 @@ class ContentObjectTest : StringSpec({ } } + "test content object with custom json-schema" { + val content = buildCustomContentObject("custom") + content shouldBeContent { + addMediaType(ContentType.Application.Json.toString(), MediaType().apply { + schema = Schema().apply { + type = "object" + properties = mapOf( + "someBoolean" to Schema().apply { + type = "boolean" + }, + "someText" to Schema().apply { + type = "string" + } + ) + } + }) + } + } + + "test content object with custom json-schema and components-section enabled" { + val content = buildCustomContentObject("custom", ComponentsContext(true, mutableMapOf(), true, mutableMapOf())) + content shouldBeContent { + addMediaType(ContentType.Application.Json.toString(), MediaType().apply { + schema = Schema().apply { + `$ref` = "#/components/schemas/custom" + } + }) + } + } + }) { companion object { + private fun pluginConfig() = SwaggerUIPluginConfig().apply { + schemas { + remote("remote", "/my/test/schema") + json("custom") { + """ + { + "type": "object", + "properties": { + "someBoolean": { + "type": "boolean" + }, + "someText": { + "type": "string" + } + } + } + """.trimIndent() + } + } + } + private fun buildContentObject(schema: KClass<*>?, builder: OpenApiBody.() -> Unit): Content { return buildContentObject(ComponentsContext.NOOP, schema, builder) } @@ -137,14 +189,11 @@ class ContentObjectTest : StringSpec({ type: KClass<*>?, builder: OpenApiBody.() -> Unit ): Content { - return getOApiContentBuilder().build(OpenApiBody(type?.java).apply(builder), componentCtx) + return getOApiContentBuilder().build(OpenApiBody(type?.java).apply(builder), componentCtx, pluginConfig()) } - private fun buildExternalContentObject( - url: String, - componentCtx: ComponentsContext = ComponentsContext.NOOP, - ): Content { - return getOApiContentBuilder().build(OpenApiBody(null).apply { externalSchemaUrl = url }, componentCtx) + private fun buildCustomContentObject(schemaId: String, componentCtx: ComponentsContext = ComponentsContext.NOOP): Content { + return getOApiContentBuilder().build(OpenApiBody(null).apply { customSchemaId = schemaId }, componentCtx, pluginConfig()) } private data class SimpleBody( diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/JsonSchemaGenerationTests.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/JsonSchemaGenerationTests.kt index 770d4fa..7207e70 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/JsonSchemaGenerationTests.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/JsonSchemaGenerationTests.kt @@ -3,6 +3,7 @@ package io.github.smiley4.ktorswaggerui.tests import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.core.type.TypeReference +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.specbuilder.ComponentsContext import io.github.smiley4.ktorswaggerui.specbuilder.OApiSchemaBuilder import io.kotest.core.spec.style.StringSpec @@ -11,14 +12,14 @@ import io.swagger.v3.oas.models.media.Schema class JsonSchemaGenerationTests : StringSpec({ "generate schema for a simple enum" { - getOApiSchemaBuilder().build(SimpleEnum::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(SimpleEnum::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "string" enum = SimpleEnum.values().map { it.name } } } "generate schema for a list of simple classes" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "object" @@ -35,7 +36,7 @@ class JsonSchemaGenerationTests : StringSpec({ } "generate schema for a simple class" { - getOApiSchemaBuilder().build(SimpleDataClass::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(SimpleDataClass::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "object" properties = mapOf( "text" to Schema().apply { @@ -49,7 +50,7 @@ class JsonSchemaGenerationTests : StringSpec({ } "generate schema for a another class" { - getOApiSchemaBuilder().build(AnotherDataClass::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(AnotherDataClass::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "object" properties = mapOf( "primitiveValue" to Schema().apply { @@ -91,7 +92,7 @@ class JsonSchemaGenerationTests : StringSpec({ } "generate schema for a class with inheritance" { - getOApiSchemaBuilder().build(SubClassA::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(SubClassA::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { allOf = listOf( Schema().apply { type = "object" @@ -118,7 +119,7 @@ class JsonSchemaGenerationTests : StringSpec({ } "generate schema for a class with sub-classes" { - getOApiSchemaBuilder().build(Superclass::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Superclass::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { anyOf = listOf( Schema().apply { allOf = listOf( @@ -173,7 +174,7 @@ class JsonSchemaGenerationTests : StringSpec({ } "generate schema for a class with nested generic type" { - getOApiSchemaBuilder().build(WrapperForClassWithGenerics::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(WrapperForClassWithGenerics::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "object" properties = mapOf( "genericClass" to Schema().apply { @@ -195,7 +196,7 @@ class JsonSchemaGenerationTests : StringSpec({ } "generate schema for a class with generic types" { - getOApiSchemaBuilder().build(getType>(), ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(getType>(), ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "object" properties = mapOf( "genericField" to Schema().apply { diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PathObjectTest.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PathObjectTest.kt index d0dec99..359ae51 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PathObjectTest.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PathObjectTest.kt @@ -1,9 +1,10 @@ package io.github.smiley4.ktorswaggerui.tests +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig +import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse +import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute import io.github.smiley4.ktorswaggerui.specbuilder.ComponentsContext import io.github.smiley4.ktorswaggerui.specbuilder.RouteMeta -import io.github.smiley4.ktorswaggerui.dsl.OpenApiRoute -import io.github.smiley4.ktorswaggerui.dsl.OpenApiResponse import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.ktor.http.HttpMethod @@ -269,10 +270,16 @@ class PathObjectTest : StringSpec({ ): Pair { return getOApiPathBuilder().build( routeMeta(method, path, builder), - defaultUnauthorizedResponse, - defaultSecuritySchemeName, - tagGenerator, - ComponentsContext.NOOP + ComponentsContext.NOOP, + SwaggerUIPluginConfig().apply { + if (defaultUnauthorizedResponse != null) { + this.defaultUnauthorizedResponse { + description = defaultUnauthorizedResponse.description + } + } + this.defaultSecuritySchemeName = defaultSecuritySchemeName + this.automaticTagGenerator = tagGenerator + } ) } @@ -286,10 +293,16 @@ class PathObjectTest : StringSpec({ ): Pair { return getOApiPathBuilder().build( protectedRouteMeta(method, path, builder), - defaultUnauthorizedResponse, - defaultSecuritySchemeName, - tagGenerator, - ComponentsContext.NOOP + ComponentsContext.NOOP, + SwaggerUIPluginConfig().apply { + if (defaultUnauthorizedResponse != null) { + this.defaultUnauthorizedResponse { + description = defaultUnauthorizedResponse.description + } + } + this.defaultSecuritySchemeName = defaultSecuritySchemeName + this.automaticTagGenerator = tagGenerator + } ) } diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PrimitiveArraysSchemaGenerationTests.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PrimitiveArraysSchemaGenerationTests.kt index 74b7531..ebb1976 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PrimitiveArraysSchemaGenerationTests.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PrimitiveArraysSchemaGenerationTests.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.tests +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.specbuilder.ComponentsContext import io.github.smiley4.ktorswaggerui.specbuilder.OApiSchemaBuilder import io.kotest.core.spec.style.StringSpec @@ -9,7 +10,7 @@ import java.math.BigDecimal class PrimitiveArraysSchemaGenerationTests : StringSpec({ "generate schema for byte-array" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -17,7 +18,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ maximum = BigDecimal.valueOf(127) } } - getOApiSchemaBuilder().build(ByteArray::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(ByteArray::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -28,7 +29,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for unsigned byte" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -39,7 +40,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for short-array" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -47,7 +48,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ maximum = BigDecimal.valueOf(32767) } } - getOApiSchemaBuilder().build(ShortArray::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(ShortArray::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -58,7 +59,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for unsigned short" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -69,14 +70,14 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for integer-array" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" format = "int32" } } - getOApiSchemaBuilder().build(IntArray::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(IntArray::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -86,7 +87,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for unsigned integer" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -97,14 +98,14 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for long-array" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" format = "int64" } } - getOApiSchemaBuilder().build(LongArray::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(LongArray::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -114,7 +115,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for unsigned long" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "integer" @@ -124,14 +125,14 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for float-array" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "number" format = "float" } } - getOApiSchemaBuilder().build(FloatArray::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(FloatArray::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "number" @@ -141,14 +142,14 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for double-array" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "number" format = "double" } } - getOApiSchemaBuilder().build(DoubleArray::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(DoubleArray::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "number" @@ -158,7 +159,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for character-array" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "string" @@ -169,7 +170,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for string-array" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "string" @@ -178,7 +179,7 @@ class PrimitiveArraysSchemaGenerationTests : StringSpec({ } "generate schema for boolean-array" { - getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Array::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "array" items = Schema().apply { type = "boolean" diff --git a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PrimitiveSchemaGenerationTests.kt b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PrimitiveSchemaGenerationTests.kt index 2d690bc..a86632e 100644 --- a/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PrimitiveSchemaGenerationTests.kt +++ b/src/test/kotlin/io/github/smiley4/ktorswaggerui/tests/PrimitiveSchemaGenerationTests.kt @@ -1,5 +1,6 @@ package io.github.smiley4.ktorswaggerui.tests +import io.github.smiley4.ktorswaggerui.SwaggerUIPluginConfig import io.github.smiley4.ktorswaggerui.specbuilder.ComponentsContext import io.github.smiley4.ktorswaggerui.specbuilder.OApiSchemaBuilder import io.kotest.core.spec.style.StringSpec @@ -8,7 +9,7 @@ import java.math.BigDecimal class PrimitiveSchemaGenerationTests : StringSpec({ "generate schema for byte" { - getOApiSchemaBuilder().build(Byte::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Byte::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "integer" minimum = BigDecimal.valueOf(-128) maximum = BigDecimal.valueOf(127) @@ -16,7 +17,7 @@ class PrimitiveSchemaGenerationTests : StringSpec({ } "generate schema for unsigned byte" { - getOApiSchemaBuilder().build(UByte::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(UByte::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "integer" minimum = BigDecimal.valueOf(0) maximum = BigDecimal.valueOf(255) @@ -24,7 +25,7 @@ class PrimitiveSchemaGenerationTests : StringSpec({ } "generate schema for short" { - getOApiSchemaBuilder().build(Short::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Short::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "integer" minimum = BigDecimal.valueOf(-32768) maximum = BigDecimal.valueOf(32767) @@ -32,7 +33,7 @@ class PrimitiveSchemaGenerationTests : StringSpec({ } "generate schema for unsigned short" { - getOApiSchemaBuilder().build(UShort::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(UShort::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "integer" minimum = BigDecimal.valueOf(0) maximum = BigDecimal.valueOf(65535) @@ -40,14 +41,14 @@ class PrimitiveSchemaGenerationTests : StringSpec({ } "generate schema for integer" { - getOApiSchemaBuilder().build(Int::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Int::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "integer" format = "int32" } } "generate schema for unsigned integer" { - getOApiSchemaBuilder().build(UInt::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(UInt::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "integer" minimum = BigDecimal.valueOf(0) maximum = BigDecimal.valueOf(4294967295) @@ -55,35 +56,35 @@ class PrimitiveSchemaGenerationTests : StringSpec({ } "generate schema for long" { - getOApiSchemaBuilder().build(Long::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Long::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "integer" format = "int64" } } "generate schema for unsigned long" { - getOApiSchemaBuilder().build(ULong::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(ULong::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "integer" minimum = BigDecimal.valueOf(0) } } "generate schema for float" { - getOApiSchemaBuilder().build(Float::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Float::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "number" format = "float" } } "generate schema for double" { - getOApiSchemaBuilder().build(Double::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Double::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "number" format = "double" } } "generate schema for character" { - getOApiSchemaBuilder().build(Char::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Char::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "string" minLength = 1 maxLength = 1 @@ -91,13 +92,13 @@ class PrimitiveSchemaGenerationTests : StringSpec({ } "generate schema for string" { - getOApiSchemaBuilder().build(String::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(String::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "string" } } "generate schema for boolean" { - getOApiSchemaBuilder().build(Boolean::class.java, ComponentsContext.NOOP) shouldBeSchema { + getOApiSchemaBuilder().build(Boolean::class.java, ComponentsContext.NOOP, SwaggerUIPluginConfig()) shouldBeSchema { type = "boolean" } }