feat: v2-alpha (#112)
There are still some bugs, still some outstanding features, but I don't want to hold this back any longer, that way I can keep the future PRs much more focused
This commit is contained in:
9
kompendium-core/Module.md
Normal file
9
kompendium-core/Module.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Module kompendium-core
|
||||
|
||||
This is where the magic happens. This module houses all the reflective goodness that powers Kompendium.
|
||||
|
||||
It is also the only mandatory client-facing module for a basic setup.
|
||||
|
||||
# Package io.bkbn.kompendium.core
|
||||
|
||||
The root package contains several objects that power Kompendium
|
@ -1,77 +1,33 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
signing
|
||||
id("io.bkbn.sourdough.library")
|
||||
`java-test-fixtures`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation(libs.jackson.module.kotlin)
|
||||
implementation(libs.bundles.ktor)
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
|
||||
testImplementation(libs.ktor.serialization)
|
||||
testImplementation(libs.kotlinx.serialization.json)
|
||||
testImplementation(libs.ktor.jackson)
|
||||
testImplementation(libs.ktor.server.test.host)
|
||||
}
|
||||
// IMPLEMENTATION
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
api(projects.kompendiumOas)
|
||||
api(projects.kompendiumAnnotations)
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "GithubPackages"
|
||||
url = uri("https://maven.pkg.github.com/bkbnio/kompendium")
|
||||
credentials {
|
||||
username = System.getenv("GITHUB_ACTOR")
|
||||
password = System.getenv("GITHUB_TOKEN")
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
create<MavenPublication>("kompendium") {
|
||||
from(components["kotlin"])
|
||||
artifact(tasks.sourcesJar)
|
||||
artifact(tasks.javadocJar)
|
||||
groupId = project.group.toString()
|
||||
artifactId = project.name.toLowerCase()
|
||||
version = project.version.toString()
|
||||
val ktorVersion: String by project
|
||||
val kotestVersion: String by project
|
||||
implementation(group = "io.ktor", name = "ktor-server-core", version = ktorVersion)
|
||||
implementation(group = "io.ktor", name = "ktor-html-builder", version = ktorVersion)
|
||||
|
||||
pom {
|
||||
name.set("Kompendium")
|
||||
description.set("A minimally invasive OpenAPI spec generator for Ktor")
|
||||
url.set("https://github.com/bkbnio/Kompendium")
|
||||
licenses {
|
||||
license {
|
||||
name.set("MIT License")
|
||||
url.set("https://mit-license.org/")
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("bkbnio")
|
||||
name.set("Ryan Brink")
|
||||
email.set("admin@bkbn.io")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection.set("scm:git:git://github.com/bkbnio/Kompendium.git")
|
||||
developerConnection.set("scm:git:ssh://github.com/bkbnio/Kompendium.git")
|
||||
url.set("https://github.com/bkbnio/Kompendium.git")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
implementation(group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin", version = "2.13.0")
|
||||
|
||||
signing {
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
useInMemoryPgpKeys(signingKey, signingPassword)
|
||||
sign(publishing.publications)
|
||||
// TEST FIXTURES
|
||||
|
||||
testFixturesApi(group = "io.kotest", name = "kotest-runner-junit5-jvm", version = kotestVersion)
|
||||
testFixturesApi(group = "io.kotest", name = "kotest-assertions-core-jvm", version = kotestVersion)
|
||||
testFixturesApi(group = "io.kotest", name = "kotest-property-jvm", version = kotestVersion)
|
||||
testFixturesApi(group = "io.kotest", name = "kotest-assertions-json-jvm", version = kotestVersion)
|
||||
testFixturesApi(group = "io.kotest", name = "kotest-assertions-ktor-jvm", version = "4.4.3")
|
||||
|
||||
testFixturesApi(group = "io.ktor", name = "ktor-server-core", version = ktorVersion)
|
||||
testFixturesApi(group = "io.ktor", name = "ktor-server-test-host", version = ktorVersion)
|
||||
testFixturesApi(group = "io.ktor", name = "ktor-jackson", version = ktorVersion)
|
||||
testFixturesApi(group = "io.ktor", name = "ktor-serialization", version = ktorVersion)
|
||||
|
||||
testFixturesApi(group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.3.1")
|
||||
}
|
||||
|
@ -1,49 +0,0 @@
|
||||
package io.bkbn.kompendium
|
||||
|
||||
import io.bkbn.kompendium.models.meta.ErrorMap
|
||||
import io.bkbn.kompendium.models.meta.SchemaMap
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpec
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecInfo
|
||||
import io.bkbn.kompendium.models.oas.TypedSchema
|
||||
import io.bkbn.kompendium.path.IPathCalculator
|
||||
import io.bkbn.kompendium.path.PathCalculator
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.RouteSelector
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Maintains all state for the Kompendium library
|
||||
*/
|
||||
object Kompendium {
|
||||
|
||||
var errorMap: ErrorMap = emptyMap()
|
||||
var cache: SchemaMap = emptyMap()
|
||||
|
||||
var openApiSpec = OpenApiSpec(
|
||||
info = OpenApiSpecInfo(),
|
||||
servers = mutableListOf(),
|
||||
paths = mutableMapOf()
|
||||
)
|
||||
|
||||
fun calculatePath(route: Route) = PathCalculator.calculate(route)
|
||||
|
||||
fun resetSchema() {
|
||||
openApiSpec = OpenApiSpec(
|
||||
info = OpenApiSpecInfo(),
|
||||
servers = mutableListOf(),
|
||||
paths = mutableMapOf()
|
||||
)
|
||||
cache = emptyMap()
|
||||
}
|
||||
|
||||
fun addCustomTypeSchema(clazz: KClass<*>, schema: TypedSchema) {
|
||||
cache = cache.plus(clazz.simpleName!! to schema)
|
||||
}
|
||||
|
||||
fun <T : RouteSelector> addCustomRouteHandler(
|
||||
selector: KClass<T>,
|
||||
handler: IPathCalculator.(Route, String) -> String
|
||||
) {
|
||||
PathCalculator.addCustomRouteHandler(selector, handler)
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package io.bkbn.kompendium
|
||||
|
||||
import io.ktor.routing.Route
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* Functions are considered preflight when they are used to intercept a method ahead of running.
|
||||
*/
|
||||
object KompendiumPreFlight {
|
||||
|
||||
/**
|
||||
* Performs all content analysis on the types provided to a notarized route and adds it to the top level spec
|
||||
* @param TParam
|
||||
* @param TReq
|
||||
* @param TResp
|
||||
* @param block The function to execute, provided type information of the parameters above
|
||||
* @return [Route]
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> methodNotarizationPreFlight(
|
||||
block: (KType, KType, KType) -> Route
|
||||
): Route {
|
||||
val requestType = typeOf<TReq>()
|
||||
val responseType = typeOf<TResp>()
|
||||
val paramType = typeOf<TParam>()
|
||||
addToCache(paramType, requestType, responseType)
|
||||
Kompendium.openApiSpec.components.schemas.putAll(Kompendium.cache)
|
||||
return block.invoke(paramType, requestType, responseType)
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs all content analysis on the types provided to a notarized error and adds them to the top level spec.
|
||||
* @param TErr
|
||||
* @param TResp
|
||||
* @param block The function to execute, provided type information of the parameters above
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified TErr : Throwable, reified TResp : Any> errorNotarizationPreFlight(
|
||||
block: (KType, KType) -> Unit
|
||||
) {
|
||||
val errorType = typeOf<TErr>()
|
||||
val responseType = typeOf<TResp>()
|
||||
addToCache(typeOf<Unit>(), typeOf<Unit>(), responseType)
|
||||
Kompendium.openApiSpec.components.schemas.putAll(Kompendium.cache)
|
||||
return block.invoke(errorType, responseType)
|
||||
}
|
||||
|
||||
fun addToCache(paramType: KType, requestType: KType, responseType: KType) {
|
||||
Kompendium.cache = Kontent.generateKontent(requestType, Kompendium.cache)
|
||||
Kompendium.cache = Kontent.generateKontent(responseType, Kompendium.cache)
|
||||
Kompendium.cache = Kontent.generateParameterKontent(paramType, Kompendium.cache)
|
||||
}
|
||||
}
|
@ -1,290 +0,0 @@
|
||||
package io.bkbn.kompendium
|
||||
|
||||
import io.bkbn.kompendium.annotations.UndeclaredField
|
||||
import io.bkbn.kompendium.models.meta.SchemaMap
|
||||
import io.bkbn.kompendium.models.oas.AnyOfReferencedSchema
|
||||
import io.bkbn.kompendium.models.oas.ArraySchema
|
||||
import io.bkbn.kompendium.models.oas.DictionarySchema
|
||||
import io.bkbn.kompendium.models.oas.EnumSchema
|
||||
import io.bkbn.kompendium.models.oas.FormatSchema
|
||||
import io.bkbn.kompendium.models.oas.ObjectSchema
|
||||
import io.bkbn.kompendium.models.oas.ReferencedSchema
|
||||
import io.bkbn.kompendium.models.oas.SimpleSchema
|
||||
import io.bkbn.kompendium.util.Helpers.COMPONENT_SLUG
|
||||
import io.bkbn.kompendium.util.Helpers.genericNameAdapter
|
||||
import io.bkbn.kompendium.util.Helpers.getReferenceSlug
|
||||
import io.bkbn.kompendium.util.Helpers.getSimpleSlug
|
||||
import io.bkbn.kompendium.util.Helpers.logged
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import java.util.UUID
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* Responsible for generating the schema map that is used to power all object references across the API Spec.
|
||||
*/
|
||||
object Kontent {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
/**
|
||||
* Analyzes a type [T] for its top-level and any nested schemas, and adds them to a [SchemaMap], if provided
|
||||
* @param T type to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
* @return an updated schema map containing all type information for [T]
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified T> generateKontent(
|
||||
cache: SchemaMap = emptyMap()
|
||||
): SchemaMap {
|
||||
val kontentType = typeOf<T>()
|
||||
return generateKTypeKontent(kontentType, cache)
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a [KType] for its top-level and any nested schemas, and adds them to a [SchemaMap], if provided
|
||||
* @param type [KType] to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
* @return an updated schema map containing all type information for [KType] type
|
||||
*/
|
||||
fun generateKontent(
|
||||
type: KType,
|
||||
cache: SchemaMap = emptyMap()
|
||||
): SchemaMap {
|
||||
var newCache = cache
|
||||
gatherSubTypes(type).forEach {
|
||||
newCache = generateKTypeKontent(it, newCache)
|
||||
}
|
||||
return newCache
|
||||
}
|
||||
|
||||
private fun gatherSubTypes(type: KType): List<KType> {
|
||||
val classifier = type.classifier as KClass<*>
|
||||
return if (classifier.isSealed) {
|
||||
classifier.sealedSubclasses.map {
|
||||
it.createType(type.arguments)
|
||||
}
|
||||
} else {
|
||||
listOf(type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze a type [T], but filters out the top-level type
|
||||
* @param T type to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
* @return an updated schema map containing all type information for [T]
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified T> generateParameterKontent(
|
||||
cache: SchemaMap = emptyMap()
|
||||
): SchemaMap {
|
||||
val kontentType = typeOf<T>()
|
||||
return generateKTypeKontent(kontentType, cache)
|
||||
.filterNot { (slug, _) -> slug == (kontentType.classifier as KClass<*>).simpleName }
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze a type but filters out the top-level type
|
||||
* @param type to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
* @return an updated schema map containing all type information for [T]
|
||||
*/
|
||||
fun generateParameterKontent(
|
||||
type: KType,
|
||||
cache: SchemaMap = emptyMap()
|
||||
): SchemaMap {
|
||||
return generateKTypeKontent(type, cache)
|
||||
.filterNot { (slug, _) -> slug == (type.classifier as KClass<*>).simpleName }
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively fills schema map depending on [KType] classifier
|
||||
* @param type [KType] to parse
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
fun generateKTypeKontent(
|
||||
type: KType,
|
||||
cache: SchemaMap = emptyMap()
|
||||
): SchemaMap = logged(object {}.javaClass.enclosingMethod.name, mapOf("cache" to cache)) {
|
||||
logger.debug("Parsing Kontent of $type")
|
||||
when (val clazz = type.classifier as KClass<*>) {
|
||||
Unit::class -> cache
|
||||
Int::class -> cache.plus(clazz.simpleName!! to FormatSchema("int32", "integer"))
|
||||
Long::class -> cache.plus(clazz.simpleName!! to FormatSchema("int64", "integer"))
|
||||
Double::class -> cache.plus(clazz.simpleName!! to FormatSchema("double", "number"))
|
||||
Float::class -> cache.plus(clazz.simpleName!! to FormatSchema("float", "number"))
|
||||
String::class -> cache.plus(clazz.simpleName!! to SimpleSchema("string"))
|
||||
Boolean::class -> cache.plus(clazz.simpleName!! to SimpleSchema("boolean"))
|
||||
UUID::class -> cache.plus(clazz.simpleName!! to FormatSchema("uuid", "string"))
|
||||
BigDecimal::class -> cache.plus(clazz.simpleName!! to FormatSchema("double", "number"))
|
||||
BigInteger::class -> cache.plus(clazz.simpleName!! to FormatSchema("int64", "integer"))
|
||||
ByteArray::class -> cache.plus(clazz.simpleName!! to FormatSchema("byte", "string"))
|
||||
else -> when {
|
||||
clazz.isSubclassOf(Collection::class) -> handleCollectionType(type, clazz, cache)
|
||||
clazz.isSubclassOf(Enum::class) -> handleEnumType(clazz, cache)
|
||||
clazz.isSubclassOf(Map::class) -> handleMapType(type, clazz, cache)
|
||||
else -> handleComplexType(type, clazz, cache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In the event of an object type, this method will parse out individual fields to recursively aggregate object map.
|
||||
* @param clazz Class of the object to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
// TODO Fix as part of this issue https://github.com/bkbnio/kompendium/issues/80
|
||||
@Suppress("LongMethod", "ComplexMethod")
|
||||
private fun handleComplexType(type: KType, clazz: KClass<*>, cache: SchemaMap): SchemaMap {
|
||||
// This needs to be simple because it will be stored under it's appropriate reference component implicitly
|
||||
val slug = type.getSimpleSlug()
|
||||
// Only analyze if component has not already been stored in the cache
|
||||
return when (cache.containsKey(slug)) {
|
||||
true -> {
|
||||
logger.debug("Cache already contains $slug, returning cache untouched")
|
||||
cache
|
||||
}
|
||||
false -> {
|
||||
logger.debug("$slug was not found in cache, generating now")
|
||||
var newCache = cache
|
||||
// Grabs any type parameters as a zip with the corresponding type argument
|
||||
val typeMap = clazz.typeParameters.zip(type.arguments).toMap()
|
||||
// associates each member with a Pair of prop name to property schema
|
||||
val fieldMap = clazz.memberProperties.associate { prop ->
|
||||
logger.debug("Analyzing $prop in class $clazz")
|
||||
// Grab the field of the current property
|
||||
val field = prop.javaField?.type?.kotlin ?: error("Unable to parse field type from $prop")
|
||||
logger.debug("Detected field $field")
|
||||
// Yoinks any generic types from the type map should the field be a generic
|
||||
val yoinkBaseType = if (typeMap.containsKey(prop.returnType.classifier)) {
|
||||
logger.debug("Generic type detected")
|
||||
typeMap[prop.returnType.classifier]?.type!!
|
||||
} else {
|
||||
prop.returnType
|
||||
}
|
||||
// converts the base type to a class
|
||||
val yoinkedClassifier = yoinkBaseType.classifier as KClass<*>
|
||||
// in the event of a sealed class, grab all sealed subclasses and create a type from the base args
|
||||
val yoinkedTypes = if (yoinkedClassifier.isSealed) {
|
||||
yoinkedClassifier.sealedSubclasses.map { it.createType(yoinkBaseType.arguments) }
|
||||
} else {
|
||||
listOf(yoinkBaseType)
|
||||
}
|
||||
// if the most up-to-date cache does not contain the content for this field, generate it and add to cache
|
||||
if (!newCache.containsKey(field.simpleName)) {
|
||||
logger.debug("Cache was missing ${field.simpleName}, adding now")
|
||||
yoinkedTypes.forEach {
|
||||
newCache = generateKTypeKontent(it, newCache)
|
||||
}
|
||||
}
|
||||
// TODO This in particular is worthy of a refactor... just not very well written
|
||||
// builds the appropriate property schema based on the property return type
|
||||
val propSchema = if (typeMap.containsKey(prop.returnType.classifier)) {
|
||||
if (yoinkedClassifier.isSealed) {
|
||||
val refs = yoinkedClassifier.sealedSubclasses
|
||||
.map { it.createType(yoinkBaseType.arguments) }
|
||||
.map { ReferencedSchema(it.getReferenceSlug()) }
|
||||
AnyOfReferencedSchema(refs)
|
||||
} else {
|
||||
ReferencedSchema(typeMap[prop.returnType.classifier]?.type!!.getReferenceSlug())
|
||||
}
|
||||
} else {
|
||||
if (yoinkedClassifier.isSealed) {
|
||||
val refs = yoinkedClassifier.sealedSubclasses
|
||||
.map { it.createType(yoinkBaseType.arguments) }
|
||||
.map { ReferencedSchema(it.getReferenceSlug()) }
|
||||
AnyOfReferencedSchema(refs)
|
||||
} else {
|
||||
ReferencedSchema(field.getReferenceSlug(prop))
|
||||
}
|
||||
}
|
||||
Pair(prop.name, propSchema)
|
||||
}
|
||||
logger.debug("Looking for undeclared fields")
|
||||
val undeclaredFieldMap = clazz.annotations.filterIsInstance<UndeclaredField>().associate {
|
||||
val undeclaredType = it.clazz.createType()
|
||||
newCache = generateKontent(undeclaredType, newCache)
|
||||
it.field to ReferencedSchema(undeclaredType.getReferenceSlug())
|
||||
}
|
||||
logger.debug("$slug contains $fieldMap")
|
||||
val schema = ObjectSchema(fieldMap.plus(undeclaredFieldMap))
|
||||
logger.debug("$slug schema: $schema")
|
||||
newCache.plus(slug to schema)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for when an [Enum] is encountered
|
||||
* @param clazz Class of the object to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
private fun handleEnumType(clazz: KClass<*>, cache: SchemaMap): SchemaMap {
|
||||
val options = clazz.java.enumConstants.map { it.toString() }.toSet()
|
||||
return cache.plus(clazz.simpleName!! to EnumSchema(options))
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for when a [Map] is encountered
|
||||
* @param type Map type information
|
||||
* @param clazz Map class information
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
private fun handleMapType(type: KType, clazz: KClass<*>, cache: SchemaMap): SchemaMap {
|
||||
logger.debug("Map detected for $type, generating schema and appending to cache")
|
||||
val (keyType, valType) = type.arguments.map { it.type }
|
||||
logger.debug("Obtained map types -> key: $keyType and value: $valType")
|
||||
if (keyType?.classifier != String::class) {
|
||||
error("Invalid Map $type: OpenAPI dictionaries must have keys of type String")
|
||||
}
|
||||
val valClass = valType?.classifier as KClass<*>
|
||||
val valClassName = valClass.simpleName
|
||||
val referenceName = genericNameAdapter(type, clazz)
|
||||
val valueReference = when (valClass.isSealed) {
|
||||
true -> {
|
||||
val subTypes = gatherSubTypes(valType)
|
||||
AnyOfReferencedSchema(subTypes.map {
|
||||
ReferencedSchema(("$COMPONENT_SLUG/${it.getSimpleSlug()}"))
|
||||
})
|
||||
}
|
||||
false -> ReferencedSchema("$COMPONENT_SLUG/$valClassName")
|
||||
}
|
||||
val schema = DictionarySchema(additionalProperties = valueReference)
|
||||
val updatedCache = generateKontent(valType, cache)
|
||||
return updatedCache.plus(referenceName to schema)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for when a [Collection] is encountered
|
||||
* @param type Collection type information
|
||||
* @param clazz Collection class information
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
private fun handleCollectionType(type: KType, clazz: KClass<*>, cache: SchemaMap): SchemaMap {
|
||||
logger.debug("Collection detected for $type, generating schema and appending to cache")
|
||||
val collectionType = type.arguments.first().type!!
|
||||
val collectionClass = collectionType.classifier as KClass<*>
|
||||
logger.debug("Obtained collection class: $collectionClass")
|
||||
val referenceName = genericNameAdapter(type, clazz)
|
||||
val valueReference = when (collectionClass.isSealed) {
|
||||
true -> {
|
||||
val subTypes = gatherSubTypes(collectionType)
|
||||
AnyOfReferencedSchema(subTypes.map {
|
||||
ReferencedSchema(("$COMPONENT_SLUG/${it.getSimpleSlug()}"))
|
||||
})
|
||||
}
|
||||
false -> ReferencedSchema("$COMPONENT_SLUG/${collectionClass.simpleName}")
|
||||
}
|
||||
val schema = ArraySchema(items = valueReference)
|
||||
val updatedCache = generateKontent(collectionType, cache)
|
||||
return updatedCache.plus(referenceName to schema)
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
package io.bkbn.kompendium
|
||||
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.features.StatusPages
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.method
|
||||
import io.ktor.util.pipeline.PipelineContext
|
||||
import io.ktor.util.pipeline.PipelineInterceptor
|
||||
import io.bkbn.kompendium.KompendiumPreFlight.errorNotarizationPreFlight
|
||||
import io.bkbn.kompendium.MethodParser.parseErrorInfo
|
||||
import io.bkbn.kompendium.MethodParser.parseMethodInfo
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo.GetInfo
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo.PostInfo
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo.PutInfo
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo.DeleteInfo
|
||||
import io.bkbn.kompendium.models.meta.ResponseInfo
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecPathItem
|
||||
|
||||
/**
|
||||
* Notarization methods are the primary way that a Ktor API using Kompendium differentiates
|
||||
* from a default Ktor application. On instantiation, a notarized route, provided with the proper metadata,
|
||||
* will reflectively analyze all pertinent data to build a corresponding OpenAPI entry.
|
||||
*/
|
||||
object Notarized {
|
||||
|
||||
/**
|
||||
* Notarization for an HTTP GET request
|
||||
* @param TParam The class containing all parameter fields.
|
||||
* Each field must be annotated with @[io.bkbn.kompendium.annotations.KompendiumField]
|
||||
* @param TResp Class detailing the expected API response
|
||||
* @param info Route metadata
|
||||
*/
|
||||
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedGet(
|
||||
info: GetInfo<TParam, TResp>,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route =
|
||||
KompendiumPreFlight.methodNotarizationPreFlight<TParam, Unit, TResp>() { paramType, requestType, responseType ->
|
||||
val path = Kompendium.calculatePath(this)
|
||||
Kompendium.openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
Kompendium.openApiSpec.paths[path]?.get = parseMethodInfo(info, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Get) { handle(body) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Notarization for an HTTP POST request
|
||||
* @param TParam The class containing all parameter fields.
|
||||
* Each field must be annotated with @[io.bkbn.kompendium.annotations.KompendiumField]
|
||||
* @param TReq Class detailing the expected API request body
|
||||
* @param TResp Class detailing the expected API response
|
||||
* @param info Route metadata
|
||||
*/
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPost(
|
||||
info: PostInfo<TParam, TReq, TResp>,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route =
|
||||
KompendiumPreFlight.methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||
val path = Kompendium.calculatePath(this)
|
||||
Kompendium.openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
Kompendium.openApiSpec.paths[path]?.post = parseMethodInfo(info, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Post) { handle(body) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Notarization for an HTTP Delete request
|
||||
* @param TParam The class containing all parameter fields.
|
||||
* Each field must be annotated with @[io.bkbn.kompendium.annotations.KompendiumField]
|
||||
* @param TReq Class detailing the expected API request body
|
||||
* @param TResp Class detailing the expected API response
|
||||
* @param info Route metadata
|
||||
*/
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPut(
|
||||
info: PutInfo<TParam, TReq, TResp>,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>,
|
||||
): Route =
|
||||
KompendiumPreFlight.methodNotarizationPreFlight<TParam, TReq, TResp>() { paramType, requestType, responseType ->
|
||||
val path = Kompendium.calculatePath(this)
|
||||
Kompendium.openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
Kompendium.openApiSpec.paths[path]?.put =
|
||||
parseMethodInfo(info, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Put) { handle(body) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Notarization for an HTTP POST request
|
||||
* @param TParam The class containing all parameter fields.
|
||||
* Each field must be annotated with @[io.bkbn.kompendium.annotations.KompendiumField]
|
||||
* @param TResp Class detailing the expected API response
|
||||
* @param info Route metadata
|
||||
*/
|
||||
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedDelete(
|
||||
info: DeleteInfo<TParam, TResp>,
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route =
|
||||
KompendiumPreFlight.methodNotarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
||||
val path = Kompendium.calculatePath(this)
|
||||
Kompendium.openApiSpec.paths.getOrPut(path) { OpenApiSpecPathItem() }
|
||||
Kompendium.openApiSpec.paths[path]?.delete = parseMethodInfo(info, paramType, requestType, responseType)
|
||||
return method(HttpMethod.Delete) { handle(body) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Notarization for a handled exception response
|
||||
* @param TErr The [Throwable] that is being handled
|
||||
* @param TResp Class detailing the expected API response when handled
|
||||
* @param info Response metadata
|
||||
*/
|
||||
inline fun <reified TErr : Throwable, reified TResp : Any> StatusPages.Configuration.notarizedException(
|
||||
info: ResponseInfo<TResp>,
|
||||
noinline handler: suspend PipelineContext<Unit, ApplicationCall>.(TErr) -> Unit
|
||||
) = errorNotarizationPreFlight<TErr, TResp>() { errorType, responseType ->
|
||||
info.parseErrorInfo(errorType, responseType)
|
||||
exception(handler)
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package io.bkbn.kompendium.annotations
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
annotation class KompendiumField(val name: String)
|
@ -1,17 +0,0 @@
|
||||
package io.bkbn.kompendium.annotations
|
||||
|
||||
/**
|
||||
* Used to indicate that a field in a data class represents an OpenAPI parameter
|
||||
* @param type The type of parameter, must be valid [ParamType]
|
||||
* @param description Description of the parameter to include in OpenAPI Spec
|
||||
*/
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
annotation class KompendiumParam(val type: ParamType, val description: String = "")
|
||||
|
||||
enum class ParamType {
|
||||
COOKIE,
|
||||
HEADER,
|
||||
PATH,
|
||||
QUERY
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package io.bkbn.kompendium.annotations
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
@Repeatable
|
||||
annotation class UndeclaredField(val field: String, val clazz: KClass<*>)
|
@ -0,0 +1,55 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import io.bkbn.kompendium.core.metadata.SchemaMap
|
||||
import io.bkbn.kompendium.oas.OpenApiSpec
|
||||
import io.bkbn.kompendium.oas.schema.TypedSchema
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.ApplicationCallPipeline
|
||||
import io.ktor.application.ApplicationFeature
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.request.path
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.util.AttributeKey
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class Kompendium(val config: Configuration) {
|
||||
|
||||
class Configuration {
|
||||
lateinit var spec: OpenApiSpec
|
||||
|
||||
var cache: SchemaMap = emptyMap()
|
||||
var specRoute = "/openapi.json"
|
||||
|
||||
// TODO Add tests for this!!
|
||||
fun addCustomTypeSchema(clazz: KClass<*>, schema: TypedSchema) {
|
||||
cache = cache.plus(clazz.simpleName!! to schema)
|
||||
}
|
||||
|
||||
// TODO Add tests for this!!
|
||||
var om: ObjectMapper = ObjectMapper()
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
.enable(SerializationFeature.INDENT_OUTPUT)
|
||||
|
||||
fun specToJson(): String = om.writeValueAsString(spec)
|
||||
}
|
||||
|
||||
companion object Feature : ApplicationFeature<Application, Configuration, Kompendium> {
|
||||
override val key: AttributeKey<Kompendium> = AttributeKey("Kompendium")
|
||||
override fun install(pipeline: Application, configure: Configuration.() -> Unit): Kompendium {
|
||||
val configuration = Configuration().apply(configure)
|
||||
|
||||
pipeline.intercept(ApplicationCallPipeline.Call) {
|
||||
if (call.request.path() == configuration.specRoute) {
|
||||
call.respondText { configuration.specToJson() }
|
||||
call.response.status(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
|
||||
return Kompendium(configuration)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.ktor.application.feature
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.application
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* Functions are considered preflight when they are used to intercept a method ahead of running.
|
||||
*/
|
||||
object KompendiumPreFlight {
|
||||
|
||||
/**
|
||||
* Performs all content analysis on the types provided to a notarized route and adds it to the top level spec
|
||||
* @param TParam
|
||||
* @param TReq
|
||||
* @param TResp
|
||||
* @param block The function to execute, provided type information of the parameters above
|
||||
* @return [Route]
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.methodNotarizationPreFlight(
|
||||
block: (KType, KType, KType) -> Route
|
||||
): Route {
|
||||
val feature = this.application.feature(Kompendium)
|
||||
val requestType = typeOf<TReq>()
|
||||
val responseType = typeOf<TResp>()
|
||||
val paramType = typeOf<TParam>()
|
||||
addToCache(paramType, requestType, responseType, feature)
|
||||
return block.invoke(paramType, requestType, responseType)
|
||||
}
|
||||
|
||||
fun addToCache(paramType: KType, requestType: KType, responseType: KType, feature: Kompendium) {
|
||||
feature.config.cache = Kontent.generateKontent(requestType, feature.config.cache)
|
||||
feature.config.cache = Kontent.generateKontent(responseType, feature.config.cache)
|
||||
feature.config.cache = Kontent.generateKontent(paramType, feature.config.cache)
|
||||
}
|
||||
}
|
@ -0,0 +1,457 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.annotations.Field
|
||||
import io.bkbn.kompendium.annotations.FreeFormObject
|
||||
import io.bkbn.kompendium.annotations.UndeclaredField
|
||||
import io.bkbn.kompendium.annotations.constraint.Format
|
||||
import io.bkbn.kompendium.annotations.constraint.MaxItems
|
||||
import io.bkbn.kompendium.annotations.constraint.MaxLength
|
||||
import io.bkbn.kompendium.annotations.constraint.MaxProperties
|
||||
import io.bkbn.kompendium.annotations.constraint.Maximum
|
||||
import io.bkbn.kompendium.annotations.constraint.MinItems
|
||||
import io.bkbn.kompendium.annotations.constraint.MinLength
|
||||
import io.bkbn.kompendium.annotations.constraint.MinProperties
|
||||
import io.bkbn.kompendium.annotations.constraint.Minimum
|
||||
import io.bkbn.kompendium.annotations.constraint.MultipleOf
|
||||
import io.bkbn.kompendium.annotations.constraint.Pattern
|
||||
import io.bkbn.kompendium.annotations.constraint.UniqueItems
|
||||
import io.bkbn.kompendium.core.metadata.SchemaMap
|
||||
import io.bkbn.kompendium.core.metadata.TypeMap
|
||||
import io.bkbn.kompendium.core.util.Helpers.genericNameAdapter
|
||||
import io.bkbn.kompendium.core.util.Helpers.getSimpleSlug
|
||||
import io.bkbn.kompendium.core.util.Helpers.logged
|
||||
import io.bkbn.kompendium.core.util.Helpers.toNumber
|
||||
import io.bkbn.kompendium.oas.schema.AnyOfSchema
|
||||
import io.bkbn.kompendium.oas.schema.ArraySchema
|
||||
import io.bkbn.kompendium.oas.schema.ComponentSchema
|
||||
import io.bkbn.kompendium.oas.schema.DictionarySchema
|
||||
import io.bkbn.kompendium.oas.schema.EnumSchema
|
||||
import io.bkbn.kompendium.oas.schema.FormattedSchema
|
||||
import io.bkbn.kompendium.oas.schema.FreeFormSchema
|
||||
import io.bkbn.kompendium.oas.schema.ObjectSchema
|
||||
import io.bkbn.kompendium.oas.schema.SimpleSchema
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KClassifier
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.isSubclassOf
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import kotlin.reflect.typeOf
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Responsible for generating the schema map that is used to power all object references across the API Spec.
|
||||
*/
|
||||
object Kontent {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
/**
|
||||
* Analyzes a type [T] for its top-level and any nested schemas, and adds them to a [SchemaMap], if provided
|
||||
* @param T type to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
* @return an updated schema map containing all type information for [T]
|
||||
*/
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
inline fun <reified T> generateKontent(
|
||||
cache: SchemaMap = emptyMap()
|
||||
): SchemaMap {
|
||||
val kontentType = typeOf<T>()
|
||||
return generateKTypeKontent(kontentType, cache)
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a [KType] for its top-level and any nested schemas, and adds them to a [SchemaMap], if provided
|
||||
* @param type [KType] to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
* @return an updated schema map containing all type information for [KType] type
|
||||
*/
|
||||
fun generateKontent(
|
||||
type: KType,
|
||||
cache: SchemaMap = emptyMap()
|
||||
): SchemaMap {
|
||||
var newCache = cache
|
||||
gatherSubTypes(type).forEach {
|
||||
newCache = generateKTypeKontent(it, newCache)
|
||||
}
|
||||
return newCache
|
||||
}
|
||||
|
||||
private fun gatherSubTypes(type: KType): List<KType> {
|
||||
val classifier = type.classifier as KClass<*>
|
||||
return if (classifier.isSealed) {
|
||||
classifier.sealedSubclasses.map {
|
||||
it.createType(type.arguments)
|
||||
}
|
||||
} else {
|
||||
listOf(type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively fills schema map depending on [KType] classifier
|
||||
* @param type [KType] to parse
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
fun generateKTypeKontent(
|
||||
type: KType,
|
||||
cache: SchemaMap = emptyMap(),
|
||||
): SchemaMap = logged(object {}.javaClass.enclosingMethod.name, mapOf("cache" to cache)) {
|
||||
logger.debug("Parsing Kontent of $type")
|
||||
when (val clazz = type.classifier as KClass<*>) {
|
||||
Unit::class -> cache
|
||||
Int::class -> cache.plus(clazz.simpleName!! to FormattedSchema("int32", "integer"))
|
||||
Long::class -> cache.plus(clazz.simpleName!! to FormattedSchema("int64", "integer"))
|
||||
Double::class -> cache.plus(clazz.simpleName!! to FormattedSchema("double", "number"))
|
||||
Float::class -> cache.plus(clazz.simpleName!! to FormattedSchema("float", "number"))
|
||||
String::class -> cache.plus(clazz.simpleName!! to SimpleSchema("string"))
|
||||
Boolean::class -> cache.plus(clazz.simpleName!! to SimpleSchema("boolean"))
|
||||
UUID::class -> cache.plus(clazz.simpleName!! to FormattedSchema("uuid", "string"))
|
||||
BigDecimal::class -> cache.plus(clazz.simpleName!! to FormattedSchema("double", "number"))
|
||||
BigInteger::class -> cache.plus(clazz.simpleName!! to FormattedSchema("int64", "integer"))
|
||||
ByteArray::class -> cache.plus(clazz.simpleName!! to FormattedSchema("byte", "string"))
|
||||
else -> when {
|
||||
clazz.isSubclassOf(Collection::class) -> handleCollectionType(type, clazz, cache)
|
||||
clazz.isSubclassOf(Enum::class) -> handleEnumType(clazz, cache)
|
||||
clazz.isSubclassOf(Map::class) -> handleMapType(type, clazz, cache)
|
||||
else -> handleComplexType(type, clazz, cache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In the event of an object type, this method will parse out individual fields to recursively aggregate object map.
|
||||
* @param clazz Class of the object to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
@Suppress("LongMethod", "ComplexMethod")
|
||||
private fun handleComplexType(type: KType, clazz: KClass<*>, cache: SchemaMap): SchemaMap {
|
||||
// This needs to be simple because it will be stored under its appropriate reference component implicitly
|
||||
val slug = type.getSimpleSlug()
|
||||
// Only analyze if component has not already been stored in the cache
|
||||
return when (cache.containsKey(slug)) {
|
||||
true -> {
|
||||
logger.debug("Cache already contains $slug, returning cache untouched")
|
||||
cache
|
||||
}
|
||||
false -> {
|
||||
logger.debug("$slug was not found in cache, generating now")
|
||||
var newCache = cache
|
||||
// Grabs any type parameters mapped to the corresponding type argument(s)
|
||||
val typeMap: TypeMap = clazz.typeParameters.zip(type.arguments).toMap()
|
||||
// associates each member with a Pair of prop name to property schema
|
||||
val fieldMap = clazz.memberProperties.associate { prop ->
|
||||
logger.debug("Analyzing $prop in class $clazz")
|
||||
// Grab the field of the current property
|
||||
val field = prop.javaField?.type?.kotlin ?: error("Unable to parse field type from $prop")
|
||||
// Short circuit if data is free form
|
||||
val freeForm = prop.findAnnotation<FreeFormObject>()
|
||||
var name = prop.name
|
||||
|
||||
// todo add method to clean up
|
||||
when (freeForm) {
|
||||
null -> {
|
||||
val baseType = scanForGeneric(typeMap, prop)
|
||||
val baseClazz = baseType.classifier as KClass<*>
|
||||
val allTypes = scanForSealed(baseClazz, baseType)
|
||||
newCache = updateCache(newCache, field, allTypes)
|
||||
var propSchema = constructComponentSchema(
|
||||
typeMap = typeMap,
|
||||
prop = prop,
|
||||
fieldClazz = field,
|
||||
clazz = baseClazz,
|
||||
type = baseType,
|
||||
cache = newCache
|
||||
)
|
||||
// todo move to helper
|
||||
prop.findAnnotation<Field>()?.let { fieldOverrides ->
|
||||
if (fieldOverrides.description.isNotBlank()) {
|
||||
propSchema = propSchema.setDescription(fieldOverrides.description)
|
||||
}
|
||||
if (fieldOverrides.name.isNotBlank()) {
|
||||
name = fieldOverrides.name
|
||||
}
|
||||
}
|
||||
Pair(name, propSchema)
|
||||
}
|
||||
else -> {
|
||||
val minProperties = prop.findAnnotation<MinProperties>()
|
||||
val maxProperties = prop.findAnnotation<MaxProperties>()
|
||||
val schema =
|
||||
FreeFormSchema(minProperties = minProperties?.properties, maxProperties = maxProperties?.properties)
|
||||
Pair(name, schema)
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug("Looking for undeclared fields")
|
||||
val undeclaredFieldMap = clazz.annotations.filterIsInstance<UndeclaredField>().associate {
|
||||
val undeclaredType = it.clazz.createType()
|
||||
newCache = generateKontent(undeclaredType, newCache)
|
||||
it.field to newCache[undeclaredType.getSimpleSlug()]!!
|
||||
}
|
||||
logger.debug("$slug contains $fieldMap")
|
||||
var schema = ObjectSchema(fieldMap.plus(undeclaredFieldMap))
|
||||
val requiredParams = clazz.primaryConstructor?.parameters?.filterNot { it.isOptional } ?: emptyList()
|
||||
if (requiredParams.isNotEmpty()) {
|
||||
schema = schema.copy(required = requiredParams.map { it.name!! })
|
||||
}
|
||||
logger.debug("$slug schema: $schema")
|
||||
newCache.plus(slug to schema)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the type information provided and adds any missing data to the schema map
|
||||
*/
|
||||
private fun updateCache(cache: SchemaMap, clazz: KClass<*>, types: List<KType>): SchemaMap {
|
||||
var newCache = cache
|
||||
if (!cache.containsKey(clazz.simpleName)) {
|
||||
logger.debug("Cache was missing ${clazz.simpleName}, adding now")
|
||||
types.forEach {
|
||||
newCache = generateKTypeKontent(it, newCache)
|
||||
}
|
||||
}
|
||||
return newCache
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans a class for sealed subclasses. If found, returns a list with all children. Otherwise, returns
|
||||
* the base type
|
||||
*/
|
||||
private fun scanForSealed(clazz: KClass<*>, type: KType): List<KType> = if (clazz.isSealed) {
|
||||
clazz.sealedSubclasses.map { it.createType(type.arguments) }
|
||||
} else {
|
||||
listOf(type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Yoinks any generic types from the type map should the field be a generic
|
||||
*/
|
||||
private fun scanForGeneric(typeMap: TypeMap, prop: KProperty1<*, *>): KType =
|
||||
if (typeMap.containsKey(prop.returnType.classifier)) {
|
||||
logger.debug("Generic type detected")
|
||||
typeMap[prop.returnType.classifier]?.type!!
|
||||
} else {
|
||||
prop.returnType
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a [ComponentSchema]
|
||||
*/
|
||||
private fun constructComponentSchema(
|
||||
typeMap: TypeMap,
|
||||
clazz: KClass<*>,
|
||||
fieldClazz: KClass<*>,
|
||||
prop: KProperty1<*, *>,
|
||||
type: KType,
|
||||
cache: SchemaMap
|
||||
): ComponentSchema =
|
||||
when (typeMap.containsKey(prop.returnType.classifier)) {
|
||||
true -> handleGenericProperty(typeMap, clazz, type, prop.returnType.classifier, cache)
|
||||
false -> handleStandardProperty(clazz, fieldClazz, prop, type, cache)
|
||||
}.scanForConstraints(clazz, prop)
|
||||
|
||||
private fun ComponentSchema.scanForConstraints(clazz: KClass<*>, prop: KProperty1<*, *>): ComponentSchema =
|
||||
when (this) {
|
||||
is AnyOfSchema -> AnyOfSchema(anyOf.map { it.scanForConstraints(clazz, prop) })
|
||||
is ArraySchema -> scanForConstraints(prop)
|
||||
is DictionarySchema -> this // TODO Anything here?
|
||||
is EnumSchema -> scanForConstraints(prop)
|
||||
is FormattedSchema -> scanForConstraints(prop)
|
||||
is FreeFormSchema -> this // todo anything here?
|
||||
is ObjectSchema -> scanForConstraints(clazz, prop)
|
||||
is SimpleSchema -> scanForConstraints(prop)
|
||||
}
|
||||
|
||||
private fun ArraySchema.scanForConstraints(prop: KProperty1<*, *>): ArraySchema {
|
||||
val minItems = prop.findAnnotation<MinItems>()
|
||||
val maxItems = prop.findAnnotation<MaxItems>()
|
||||
val uniqueItems = prop.findAnnotation<UniqueItems>()
|
||||
|
||||
return this.copy(
|
||||
minItems = minItems?.items,
|
||||
maxItems = maxItems?.items,
|
||||
uniqueItems = uniqueItems?.let { true }
|
||||
)
|
||||
}
|
||||
|
||||
private fun EnumSchema.scanForConstraints(prop: KProperty1<*, *>): EnumSchema {
|
||||
if (prop.returnType.isMarkedNullable) {
|
||||
return this.copy(nullable = true)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun FormattedSchema.scanForConstraints(prop: KProperty1<*, *>): FormattedSchema {
|
||||
val minimum = prop.findAnnotation<Minimum>()
|
||||
val maximum = prop.findAnnotation<Maximum>()
|
||||
val multipleOf = prop.findAnnotation<MultipleOf>()
|
||||
|
||||
var schema = this
|
||||
|
||||
if (prop.returnType.isMarkedNullable) {
|
||||
schema = schema.copy(nullable = true)
|
||||
}
|
||||
|
||||
return schema.copy(
|
||||
minimum = minimum?.min?.toNumber(),
|
||||
maximum = maximum?.max?.toNumber(),
|
||||
exclusiveMinimum = minimum?.exclusive,
|
||||
exclusiveMaximum = maximum?.exclusive,
|
||||
multipleOf = multipleOf?.multiple?.toNumber(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun SimpleSchema.scanForConstraints(prop: KProperty1<*, *>): SimpleSchema {
|
||||
val minLength = prop.findAnnotation<MinLength>()
|
||||
val maxLength = prop.findAnnotation<MaxLength>()
|
||||
val pattern = prop.findAnnotation<Pattern>()
|
||||
val format = prop.findAnnotation<Format>()
|
||||
|
||||
var schema = this
|
||||
|
||||
if (prop.returnType.isMarkedNullable) {
|
||||
schema = schema.copy(nullable = true)
|
||||
}
|
||||
|
||||
return schema.copy(
|
||||
minLength = minLength?.length,
|
||||
maxLength = maxLength?.length,
|
||||
pattern = pattern?.pattern,
|
||||
format = format?.format
|
||||
)
|
||||
}
|
||||
|
||||
private fun ObjectSchema.scanForConstraints(clazz: KClass<*>, prop: KProperty1<*, *>): ObjectSchema {
|
||||
val requiredParams = clazz.primaryConstructor?.parameters?.filterNot { it.isOptional } ?: emptyList()
|
||||
var schema = this
|
||||
|
||||
if (requiredParams.isNotEmpty()) {
|
||||
schema = schema.copy(required = requiredParams.map { it.name!! })
|
||||
}
|
||||
|
||||
if (prop.returnType.isMarkedNullable) {
|
||||
schema = schema.copy(nullable = true)
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
/**
|
||||
* If a field has no type parameters, build its [ComponentSchema] without referencing the [TypeMap]
|
||||
*/
|
||||
private fun handleStandardProperty(
|
||||
clazz: KClass<*>,
|
||||
fieldClazz: KClass<*>,
|
||||
prop: KProperty1<*, *>,
|
||||
type: KType,
|
||||
cache: SchemaMap
|
||||
): ComponentSchema = if (clazz.isSealed) {
|
||||
val refs = clazz.sealedSubclasses
|
||||
.map { it.createType(type.arguments) }
|
||||
.map { cache[it.getSimpleSlug()] ?: error("$it not found in cache") }
|
||||
AnyOfSchema(refs)
|
||||
} else {
|
||||
val slug = fieldClazz.getSimpleSlug(prop)
|
||||
cache[slug] ?: error("$slug not found in cache")
|
||||
}
|
||||
|
||||
/**
|
||||
* If a field has type parameters, leverage the constructed [TypeMap] to construct the [ComponentSchema]
|
||||
*/
|
||||
private fun handleGenericProperty(
|
||||
typeMap: TypeMap,
|
||||
clazz: KClass<*>,
|
||||
type: KType,
|
||||
classifier: KClassifier?,
|
||||
cache: SchemaMap
|
||||
): ComponentSchema = if (clazz.isSealed) {
|
||||
val refs = clazz.sealedSubclasses
|
||||
.map { it.createType(type.arguments) }
|
||||
.map { it.getSimpleSlug() }
|
||||
.map { cache[it] ?: error("$it not available in cache") }
|
||||
AnyOfSchema(refs)
|
||||
} else {
|
||||
val slug = typeMap[classifier]?.type!!.getSimpleSlug()
|
||||
cache[slug] ?: error("$slug not found in cache")
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for when an [Enum] is encountered
|
||||
* @param clazz Class of the object to analyze
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
private fun handleEnumType(clazz: KClass<*>, cache: SchemaMap): SchemaMap {
|
||||
val options = clazz.java.enumConstants.map { it.toString() }.toSet()
|
||||
return cache.plus(clazz.simpleName!! to EnumSchema(options))
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for when a [Map] is encountered
|
||||
* @param type Map type information
|
||||
* @param clazz Map class information
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
private fun handleMapType(type: KType, clazz: KClass<*>, cache: SchemaMap): SchemaMap {
|
||||
logger.debug("Map detected for $type, generating schema and appending to cache")
|
||||
val (keyType, valType) = type.arguments.map { it.type }
|
||||
logger.debug("Obtained map types -> key: $keyType and value: $valType")
|
||||
if (keyType?.classifier != String::class) {
|
||||
error("Invalid Map $type: OpenAPI dictionaries must have keys of type String")
|
||||
}
|
||||
var updatedCache = generateKTypeKontent(valType!!, cache)
|
||||
val valClass = valType.classifier as KClass<*>
|
||||
val valClassName = valClass.simpleName
|
||||
val referenceName = genericNameAdapter(type, clazz)
|
||||
val valueReference = when (valClass.isSealed) {
|
||||
true -> {
|
||||
val subTypes = gatherSubTypes(valType)
|
||||
AnyOfSchema(subTypes.map {
|
||||
updatedCache = generateKTypeKontent(it, updatedCache)
|
||||
updatedCache[it.getSimpleSlug()] ?: error("${it.getSimpleSlug()} not found")
|
||||
})
|
||||
}
|
||||
false -> updatedCache[valClassName] ?: error("$valClassName not found")
|
||||
}
|
||||
val schema = DictionarySchema(additionalProperties = valueReference)
|
||||
updatedCache = generateKontent(valType, updatedCache)
|
||||
return updatedCache.plus(referenceName to schema)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for when a [Collection] is encountered
|
||||
* @param type Collection type information
|
||||
* @param clazz Collection class information
|
||||
* @param cache Existing schema map to append to
|
||||
*/
|
||||
private fun handleCollectionType(type: KType, clazz: KClass<*>, cache: SchemaMap): SchemaMap {
|
||||
logger.debug("Collection detected for $type, generating schema and appending to cache")
|
||||
val collectionType = type.arguments.first().type!!
|
||||
val collectionClass = collectionType.classifier as KClass<*>
|
||||
logger.debug("Obtained collection class: $collectionClass")
|
||||
val referenceName = genericNameAdapter(type, clazz)
|
||||
var updatedCache = generateKTypeKontent(collectionType, cache)
|
||||
val valueReference = when (collectionClass.isSealed) {
|
||||
true -> {
|
||||
val subTypes = gatherSubTypes(collectionType)
|
||||
AnyOfSchema(subTypes.map {
|
||||
updatedCache = generateKTypeKontent(it, cache)
|
||||
updatedCache[it.getSimpleSlug()] ?: error("${it.getSimpleSlug()} not found")
|
||||
})
|
||||
}
|
||||
false -> updatedCache[collectionClass.simpleName] ?: error("${collectionClass.simpleName} not found")
|
||||
}
|
||||
val schema = ArraySchema(items = valueReference)
|
||||
updatedCache = generateKontent(collectionType, cache)
|
||||
return updatedCache.plus(referenceName to schema)
|
||||
}
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
package io.bkbn.kompendium
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.annotations.KompendiumParam
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo
|
||||
import io.bkbn.kompendium.models.meta.RequestInfo
|
||||
import io.bkbn.kompendium.models.meta.ResponseInfo
|
||||
import io.bkbn.kompendium.models.oas.ExampleWrapper
|
||||
import io.bkbn.kompendium.models.oas.OpenApiAnyOf
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecMediaType
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecParameter
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecPathItemOperation
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecReferencable
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecReferenceObject
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecRequest
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecResponse
|
||||
import io.bkbn.kompendium.util.Helpers
|
||||
import io.bkbn.kompendium.util.Helpers.getReferenceSlug
|
||||
import io.bkbn.kompendium.util.Helpers.getSimpleSlug
|
||||
import java.util.Locale
|
||||
import java.util.UUID
|
||||
import io.bkbn.kompendium.annotations.Param
|
||||
import io.bkbn.kompendium.core.Kontent.generateKontent
|
||||
import io.bkbn.kompendium.core.metadata.ExceptionInfo
|
||||
import io.bkbn.kompendium.core.metadata.RequestInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.MethodInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.PostInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.PutInfo
|
||||
import io.bkbn.kompendium.core.util.Helpers
|
||||
import io.bkbn.kompendium.core.util.Helpers.capitalized
|
||||
import io.bkbn.kompendium.core.util.Helpers.getSimpleSlug
|
||||
import io.bkbn.kompendium.oas.path.PathOperation
|
||||
import io.bkbn.kompendium.oas.payload.MediaType
|
||||
import io.bkbn.kompendium.oas.payload.Parameter
|
||||
import io.bkbn.kompendium.oas.payload.Request
|
||||
import io.bkbn.kompendium.oas.payload.Response
|
||||
import io.bkbn.kompendium.oas.schema.AnyOfSchema
|
||||
import io.bkbn.kompendium.oas.schema.ObjectSchema
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KParameter
|
||||
import kotlin.reflect.KProperty
|
||||
@ -26,7 +26,8 @@ import kotlin.reflect.full.createType
|
||||
import kotlin.reflect.full.findAnnotation
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.full.primaryConstructor
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import java.util.Locale
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* The MethodParser is responsible for converting route metadata and types into an OpenAPI compatible data class.
|
||||
@ -45,29 +46,19 @@ object MethodParser {
|
||||
info: MethodInfo<*, *>,
|
||||
paramType: KType,
|
||||
requestType: KType,
|
||||
responseType: KType
|
||||
) = OpenApiSpecPathItemOperation(
|
||||
responseType: KType,
|
||||
feature: Kompendium
|
||||
) = PathOperation(
|
||||
summary = info.summary,
|
||||
description = info.description,
|
||||
operationId = info.operationId,
|
||||
tags = info.tags,
|
||||
deprecated = info.deprecated,
|
||||
parameters = paramType.toParameterSpec(),
|
||||
responses = responseType.toResponseSpec(info.responseInfo)?.let { mapOf(it) }.let {
|
||||
when (it) {
|
||||
null -> {
|
||||
val throwables = parseThrowables(info.canThrow)
|
||||
when (throwables.isEmpty()) {
|
||||
true -> null
|
||||
false -> throwables
|
||||
}
|
||||
}
|
||||
else -> it.plus(parseThrowables(info.canThrow))
|
||||
}
|
||||
},
|
||||
parameters = paramType.toParameterSpec(feature),
|
||||
responses = parseResponse(responseType, info.responseInfo, feature).plus(parseExceptions(info.canThrow, feature)),
|
||||
requestBody = when (info) {
|
||||
is MethodInfo.PutInfo<*, *, *> -> requestType.toRequestSpec(info.requestInfo)
|
||||
is MethodInfo.PostInfo<*, *, *> -> requestType.toRequestSpec(info.requestInfo)
|
||||
is PutInfo<*, *, *> -> requestType.toRequestSpec(info.requestInfo, feature)
|
||||
is PostInfo<*, *, *> -> requestType.toRequestSpec(info.requestInfo, feature)
|
||||
else -> null
|
||||
},
|
||||
security = if (info.securitySchemes.isNotEmpty()) listOf(
|
||||
@ -76,57 +67,55 @@ object MethodParser {
|
||||
) else null
|
||||
)
|
||||
|
||||
/**
|
||||
* Adds the error to the [Kompendium.errorMap] for reference in notarized routes.
|
||||
* @param errorType [KType] of the throwable being handled
|
||||
* @param responseType [KType] the type of the response sent in event of error
|
||||
*/
|
||||
fun ResponseInfo<*>.parseErrorInfo(
|
||||
errorType: KType,
|
||||
responseType: KType
|
||||
) {
|
||||
Kompendium.errorMap = Kompendium.errorMap.plus(errorType to responseType.toResponseSpec(this))
|
||||
private fun parseResponse(
|
||||
responseType: KType,
|
||||
responseInfo: ResponseInfo<*>?,
|
||||
feature: Kompendium
|
||||
): Map<Int, Response<*>> = responseType.toResponseSpec(responseInfo, feature)?.let { mapOf(it) }.orEmpty()
|
||||
|
||||
private fun parseExceptions(
|
||||
exceptionInfo: Set<ExceptionInfo<*>>,
|
||||
feature: Kompendium,
|
||||
): Map<Int, Response<*>> = exceptionInfo.associate { info ->
|
||||
feature.config.cache = generateKontent(info.responseType, feature.config.cache)
|
||||
val response = Response(
|
||||
description = info.description,
|
||||
content = feature.resolveContent(info.responseType, info.mediaTypes, info.examples)
|
||||
)
|
||||
Pair(info.status.value, response)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses possible errors thrown by a route
|
||||
* @param throwables Set of classes that can be thrown
|
||||
* @return Mapping of status codes to their corresponding error spec
|
||||
*/
|
||||
private fun parseThrowables(throwables: Set<KClass<*>>): Map<Int, OpenApiSpecReferencable> = throwables.mapNotNull {
|
||||
Kompendium.errorMap[it.createType()]
|
||||
}.toMap()
|
||||
|
||||
/**
|
||||
* Converts a [KType] to an [OpenApiSpecRequest]
|
||||
* Converts a [KType] to an [Request]
|
||||
* @receiver [KType] to convert
|
||||
* @param requestInfo request metadata
|
||||
* @return Will return a generated [OpenApiSpecRequest] if requestInfo is not null
|
||||
* @return Will return a generated [Request] if requestInfo is not null
|
||||
*/
|
||||
private fun KType.toRequestSpec(requestInfo: RequestInfo<*>?): OpenApiSpecRequest<*>? =
|
||||
private fun KType.toRequestSpec(requestInfo: RequestInfo<*>?, feature: Kompendium): Request<*>? =
|
||||
when (requestInfo) {
|
||||
null -> null
|
||||
else -> {
|
||||
OpenApiSpecRequest(
|
||||
Request(
|
||||
description = requestInfo.description,
|
||||
content = resolveContent(this, requestInfo.mediaTypes, requestInfo.examples) ?: mapOf()
|
||||
content = feature.resolveContent(this, requestInfo.mediaTypes, requestInfo.examples) ?: mapOf(),
|
||||
required = requestInfo.required
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a [KType] to a pairing of http status code to [OpenApiSpecRequest]
|
||||
* Converts a [KType] to a pairing of http status code to [Response]
|
||||
* @receiver [KType] to convert
|
||||
* @param responseInfo response metadata
|
||||
* @return Will return a generated [Pair] if responseInfo is not null
|
||||
*/
|
||||
private fun KType.toResponseSpec(responseInfo: ResponseInfo<*>?): Pair<Int, OpenApiSpecResponse<*>>? =
|
||||
private fun KType.toResponseSpec(responseInfo: ResponseInfo<*>?, feature: Kompendium): Pair<Int, Response<*>>? =
|
||||
when (responseInfo) {
|
||||
null -> null
|
||||
else -> {
|
||||
val specResponse = OpenApiSpecResponse(
|
||||
val specResponse = Response(
|
||||
description = responseInfo.description,
|
||||
content = resolveContent(this, responseInfo.mediaTypes, responseInfo.examples)
|
||||
content = feature.resolveContent(this, responseInfo.mediaTypes, responseInfo.examples)
|
||||
)
|
||||
Pair(responseInfo.status.value, specResponse)
|
||||
}
|
||||
@ -139,27 +128,27 @@ object MethodParser {
|
||||
* @param examples Mapping of named examples of valid bodies.
|
||||
* @return Named mapping of media types.
|
||||
*/
|
||||
private fun <F> resolveContent(
|
||||
private fun <F> Kompendium.resolveContent(
|
||||
type: KType,
|
||||
mediaTypes: List<String>,
|
||||
examples: Map<String, F>
|
||||
): Map<String, OpenApiSpecMediaType<F>>? {
|
||||
): Map<String, MediaType<F>>? {
|
||||
val classifier = type.classifier as KClass<*>
|
||||
return if (type != Helpers.UNIT_TYPE && mediaTypes.isNotEmpty()) {
|
||||
mediaTypes.associateWith {
|
||||
val schema = if (classifier.isSealed) {
|
||||
val refs = classifier.sealedSubclasses
|
||||
.map { it.createType(type.arguments) }
|
||||
.map { it.getReferenceSlug() }
|
||||
.map { OpenApiSpecReferenceObject(it) }
|
||||
OpenApiAnyOf(refs)
|
||||
.map { it.getSimpleSlug() }
|
||||
.map { config.cache[it] ?: error("$it not available") }
|
||||
AnyOfSchema(refs)
|
||||
} else {
|
||||
val ref = type.getReferenceSlug()
|
||||
OpenApiSpecReferenceObject(ref)
|
||||
val ref = type.getSimpleSlug()
|
||||
config.cache[ref] ?: error("$ref not available")
|
||||
}
|
||||
OpenApiSpecMediaType(
|
||||
MediaType(
|
||||
schema = schema,
|
||||
examples = examples.mapValues { (_, v) -> ExampleWrapper(v) }.ifEmpty { null }
|
||||
examples = examples.mapValues { (_, v) -> MediaType.Example(v) }.ifEmpty { null }
|
||||
)
|
||||
}
|
||||
} else null
|
||||
@ -167,29 +156,28 @@ object MethodParser {
|
||||
|
||||
/**
|
||||
* Parses a type for all parameter information. All fields in the receiver
|
||||
* must be annotated with [io.bkbn.kompendium.annotations.KompendiumParam].
|
||||
* must be annotated with [io.bkbn.kompendium.annotations.Param].
|
||||
* @receiver type
|
||||
* @return list of valid parameter specs as detailed by the [KType] members
|
||||
* @throws [IllegalStateException] if the class could not be parsed properly
|
||||
*/
|
||||
private fun KType.toParameterSpec(): List<OpenApiSpecParameter> {
|
||||
private fun KType.toParameterSpec(feature: Kompendium): List<Parameter> {
|
||||
val clazz = classifier as KClass<*>
|
||||
return clazz.memberProperties.filter { prop ->
|
||||
prop.findAnnotation<KompendiumParam>() != null
|
||||
prop.findAnnotation<Param>() != null
|
||||
}.map { prop ->
|
||||
val field = prop.javaField?.type?.kotlin
|
||||
?: error("Unable to parse field type from $prop")
|
||||
val anny = prop.findAnnotation<KompendiumParam>()
|
||||
val wrapperSchema = feature.config.cache[this.getSimpleSlug()]!! as ObjectSchema
|
||||
val anny = prop.findAnnotation<Param>()
|
||||
?: error("Field ${prop.name} is not annotated with KompendiumParam")
|
||||
val schema = Kompendium.cache[field.getSimpleSlug(prop)]
|
||||
val schema = wrapperSchema.properties[prop.name]
|
||||
?: error("Could not find component type for $prop")
|
||||
val defaultValue = getDefaultParameterValue(clazz, prop)
|
||||
OpenApiSpecParameter(
|
||||
Parameter(
|
||||
name = prop.name,
|
||||
`in` = anny.type.name.lowercase(Locale.getDefault()),
|
||||
schema = schema.addDefault(defaultValue),
|
||||
description = anny.description.ifBlank { null },
|
||||
required = !prop.returnType.isMarkedNullable
|
||||
description = schema.description,
|
||||
required = !prop.returnType.isMarkedNullable && defaultValue == null
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -215,7 +203,7 @@ object MethodParser {
|
||||
.associateWith { defaultValueInjector(it) }
|
||||
val instance = constructor.callBy(values)
|
||||
val methods = clazz.java.methods
|
||||
val getterName = "get${prop.name.capitalize()}"
|
||||
val getterName = "get${prop.name.capitalized()}"
|
||||
val getterFunction = methods.find { it.name == getterName }
|
||||
?: error("Could not associate ${prop.name} with a getter")
|
||||
return getterFunction.invoke(instance)
|
@ -0,0 +1,114 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.annotations.Param
|
||||
import io.bkbn.kompendium.core.KompendiumPreFlight.methodNotarizationPreFlight
|
||||
import io.bkbn.kompendium.core.MethodParser.parseMethodInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.DeleteInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.GetInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.PostInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.PutInfo
|
||||
import io.bkbn.kompendium.oas.path.Path
|
||||
import io.bkbn.kompendium.oas.path.PathOperation
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.feature
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.application
|
||||
import io.ktor.routing.method
|
||||
import io.ktor.util.pipeline.PipelineInterceptor
|
||||
|
||||
/**
|
||||
* Notarization methods are the primary way that a Ktor API using Kompendium differentiates
|
||||
* from a default Ktor application. On instantiation, a notarized route, provided with the proper metadata,
|
||||
* will reflectively analyze all pertinent data to build a corresponding OpenAPI entry.
|
||||
*/
|
||||
object Notarized {
|
||||
|
||||
/**
|
||||
* Notarization for an HTTP GET request
|
||||
* @param TParam The class containing all parameter fields. Each field must be annotated with @[Param]
|
||||
* @param TResp Class detailing the expected API response
|
||||
* @param info Route metadata
|
||||
* @param postProcess Adds an optional callback hook to perform manual overrides on the generated [PathOperation]
|
||||
*/
|
||||
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedGet(
|
||||
info: GetInfo<TParam, TResp>,
|
||||
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route = methodNotarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
||||
val feature = this.application.feature(Kompendium)
|
||||
val path = calculateRoutePath()
|
||||
feature.config.spec.paths.getOrPut(path) { Path() }
|
||||
val baseInfo = parseMethodInfo(info, paramType, requestType, responseType, feature)
|
||||
feature.config.spec.paths[path]?.get = postProcess(baseInfo)
|
||||
return method(HttpMethod.Get) { handle(body) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Notarization for an HTTP POST request
|
||||
* @param TParam The class containing all parameter fields. Each field must be annotated with @[Param]
|
||||
* @param TReq Class detailing the expected API request body
|
||||
* @param TResp Class detailing the expected API response
|
||||
* @param info Route metadata
|
||||
* @param postProcess Adds an optional callback hook to perform manual overrides on the generated [PathOperation]
|
||||
*/
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPost(
|
||||
info: PostInfo<TParam, TReq, TResp>,
|
||||
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route = methodNotarizationPreFlight<TParam, TReq, TResp> { paramType, requestType, responseType ->
|
||||
val feature = this.application.feature(Kompendium)
|
||||
val path = calculateRoutePath()
|
||||
feature.config.spec.paths.getOrPut(path) { Path() }
|
||||
val baseInfo = parseMethodInfo(info, paramType, requestType, responseType, feature)
|
||||
feature.config.spec.paths[path]?.post = postProcess(baseInfo)
|
||||
return method(HttpMethod.Post) { handle(body) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Notarization for an HTTP Delete request
|
||||
* @param TParam The class containing all parameter fields. Each field must be annotated with @[Param]
|
||||
* @param TReq Class detailing the expected API request body
|
||||
* @param TResp Class detailing the expected API response
|
||||
* @param info Route metadata
|
||||
* @param postProcess Adds an optional callback hook to perform manual overrides on the generated [PathOperation]
|
||||
*/
|
||||
inline fun <reified TParam : Any, reified TReq : Any, reified TResp : Any> Route.notarizedPut(
|
||||
info: PutInfo<TParam, TReq, TResp>,
|
||||
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>,
|
||||
): Route = methodNotarizationPreFlight<TParam, TReq, TResp> { paramType, requestType, responseType ->
|
||||
val feature = this.application.feature(Kompendium)
|
||||
val path = calculateRoutePath()
|
||||
feature.config.spec.paths.getOrPut(path) { Path() }
|
||||
val baseInfo = parseMethodInfo(info, paramType, requestType, responseType, feature)
|
||||
feature.config.spec.paths[path]?.put = postProcess(baseInfo)
|
||||
return method(HttpMethod.Put) { handle(body) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Notarization for an HTTP POST request
|
||||
* @param TParam The class containing all parameter fields. Each field must be annotated with @[Param]
|
||||
* @param TResp Class detailing the expected API response
|
||||
* @param info Route metadata
|
||||
* @param postProcess Adds an optional callback hook to perform manual overrides on the generated [PathOperation]
|
||||
*/
|
||||
inline fun <reified TParam : Any, reified TResp : Any> Route.notarizedDelete(
|
||||
info: DeleteInfo<TParam, TResp>,
|
||||
postProcess: (PathOperation) -> PathOperation = { p -> p },
|
||||
noinline body: PipelineInterceptor<Unit, ApplicationCall>
|
||||
): Route = methodNotarizationPreFlight<TParam, Unit, TResp> { paramType, requestType, responseType ->
|
||||
val feature = this.application.feature(Kompendium)
|
||||
val path = calculateRoutePath()
|
||||
feature.config.spec.paths.getOrPut(path) { Path() }
|
||||
val baseInfo = parseMethodInfo(info, paramType, requestType, responseType, feature)
|
||||
feature.config.spec.paths[path]?.delete = postProcess(baseInfo)
|
||||
return method(HttpMethod.Delete) { handle(body) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the built-in Ktor route path [Route.toString] but cuts out any meta route such as authentication... anything
|
||||
* that matches the RegEx pattern `/\\(.+\\)`
|
||||
*/
|
||||
fun Route.calculateRoutePath() = toString().replace(Regex("/\\(.+\\)"), "")
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import kotlin.reflect.KType
|
||||
|
||||
data class ExceptionInfo<TResp : Any>(
|
||||
val responseType: KType,
|
||||
val status: HttpStatusCode,
|
||||
val description: String,
|
||||
val mediaTypes: List<String> = listOf("application/json"),
|
||||
val examples: Map<String, TResp> = emptyMap()
|
||||
)
|
@ -1,4 +1,4 @@
|
||||
package io.bkbn.kompendium.models.meta
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
data class RequestInfo<TReq>(
|
||||
val description: String,
|
@ -1,4 +1,4 @@
|
||||
package io.bkbn.kompendium.models.meta
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.ktor.http.HttpStatusCode
|
||||
|
@ -0,0 +1,5 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import io.bkbn.kompendium.oas.schema.ComponentSchema
|
||||
|
||||
typealias SchemaMap = Map<String, ComponentSchema>
|
@ -0,0 +1,6 @@
|
||||
package io.bkbn.kompendium.core.metadata
|
||||
|
||||
import kotlin.reflect.KTypeParameter
|
||||
import kotlin.reflect.KTypeProjection
|
||||
|
||||
typealias TypeMap = Map<KTypeParameter, KTypeProjection>
|
@ -0,0 +1,16 @@
|
||||
package io.bkbn.kompendium.core.metadata.method
|
||||
|
||||
import io.bkbn.kompendium.core.metadata.ExceptionInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
|
||||
data class DeleteInfo<TParam, TResp>(
|
||||
override val responseInfo: ResponseInfo<TResp>,
|
||||
override val summary: String,
|
||||
override val description: String? = null,
|
||||
override val tags: Set<String> = emptySet(),
|
||||
override val deprecated: Boolean = false,
|
||||
override val securitySchemes: Set<String> = emptySet(),
|
||||
override val canThrow: Set<ExceptionInfo<*>> = emptySet(),
|
||||
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||
override val operationId: String? = null
|
||||
) : MethodInfo<TParam, TResp>
|
@ -0,0 +1,16 @@
|
||||
package io.bkbn.kompendium.core.metadata.method
|
||||
|
||||
import io.bkbn.kompendium.core.metadata.ExceptionInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
|
||||
data class GetInfo<TParam, TResp>(
|
||||
override val responseInfo: ResponseInfo<TResp>,
|
||||
override val summary: String,
|
||||
override val description: String? = null,
|
||||
override val tags: Set<String> = emptySet(),
|
||||
override val deprecated: Boolean = false,
|
||||
override val securitySchemes: Set<String> = emptySet(),
|
||||
override val canThrow: Set<ExceptionInfo<*>> = emptySet(),
|
||||
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||
override val operationId: String? = null
|
||||
) : MethodInfo<TParam, TResp>
|
@ -0,0 +1,24 @@
|
||||
package io.bkbn.kompendium.core.metadata.method
|
||||
|
||||
import io.bkbn.kompendium.core.metadata.ExceptionInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
|
||||
sealed interface MethodInfo<TParam, TResp> {
|
||||
val summary: String
|
||||
val description: String?
|
||||
get() = null
|
||||
val tags: Set<String>
|
||||
get() = emptySet()
|
||||
val deprecated: Boolean
|
||||
get() = false
|
||||
val securitySchemes: Set<String>
|
||||
get() = emptySet()
|
||||
val canThrow: Set<ExceptionInfo<*>>
|
||||
get() = emptySet()
|
||||
val responseInfo: ResponseInfo<TResp>
|
||||
// TODO Is this even used anywhere?
|
||||
val parameterExamples: Map<String, TParam>
|
||||
get() = emptyMap()
|
||||
val operationId: String?
|
||||
get() = null
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package io.bkbn.kompendium.core.metadata.method
|
||||
|
||||
import io.bkbn.kompendium.core.metadata.ExceptionInfo
|
||||
import io.bkbn.kompendium.core.metadata.RequestInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
|
||||
data class PostInfo<TParam, TReq, TResp>(
|
||||
val requestInfo: RequestInfo<TReq>?,
|
||||
override val responseInfo: ResponseInfo<TResp>,
|
||||
override val summary: String,
|
||||
override val description: String? = null,
|
||||
override val tags: Set<String> = emptySet(),
|
||||
override val deprecated: Boolean = false,
|
||||
override val securitySchemes: Set<String> = emptySet(),
|
||||
override val canThrow: Set<ExceptionInfo<*>> = emptySet(),
|
||||
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||
override val operationId: String? = null
|
||||
) : MethodInfo<TParam, TResp>
|
@ -0,0 +1,18 @@
|
||||
package io.bkbn.kompendium.core.metadata.method
|
||||
|
||||
import io.bkbn.kompendium.core.metadata.ExceptionInfo
|
||||
import io.bkbn.kompendium.core.metadata.RequestInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
|
||||
data class PutInfo<TParam, TReq, TResp>(
|
||||
val requestInfo: RequestInfo<TReq>,
|
||||
override val responseInfo: ResponseInfo<TResp>,
|
||||
override val summary: String,
|
||||
override val description: String? = null,
|
||||
override val tags: Set<String> = emptySet(),
|
||||
override val deprecated: Boolean = false,
|
||||
override val securitySchemes: Set<String> = emptySet(),
|
||||
override val canThrow: Set<ExceptionInfo<*>> = emptySet(),
|
||||
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||
override val operationId: String? = null
|
||||
) : MethodInfo<TParam, TResp>
|
@ -1,4 +1,4 @@
|
||||
package io.bkbn.kompendium.routes
|
||||
package io.bkbn.kompendium.core.routes
|
||||
|
||||
import io.ktor.application.call
|
||||
import io.ktor.html.respondHtml
|
||||
@ -13,20 +13,19 @@ import kotlinx.html.script
|
||||
import kotlinx.html.style
|
||||
import kotlinx.html.title
|
||||
import kotlinx.html.unsafe
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpec
|
||||
|
||||
/**
|
||||
* Provides an out-of-the-box route to view docs using ReDoc
|
||||
* @param oas spec to reference
|
||||
* @param pageTitle Webpage title you wish to be displayed on your docs
|
||||
* @param specUrl url to point ReDoc to the OpenAPI json document
|
||||
*/
|
||||
fun Routing.redoc(oas: OpenApiSpec, specUrl: String = "/openapi.json") {
|
||||
fun Routing.redoc(pageTitle: String = "Docs", specUrl: String = "/openapi.json") {
|
||||
route("/docs") {
|
||||
get {
|
||||
call.respondHtml {
|
||||
head {
|
||||
title {
|
||||
+"${oas.info.title}"
|
||||
+"$pageTitle"
|
||||
}
|
||||
meta {
|
||||
charset = "utf-8"
|
@ -1,6 +1,5 @@
|
||||
package io.bkbn.kompendium.util
|
||||
package io.bkbn.kompendium.core.util
|
||||
|
||||
import io.bkbn.kompendium.util.Helpers.getReferenceSlug
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
@ -8,17 +7,18 @@ import kotlin.reflect.KType
|
||||
import kotlin.reflect.full.createType
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.util.Locale
|
||||
|
||||
object Helpers {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
|
||||
const val COMPONENT_SLUG = "#/components/schemas"
|
||||
private const val COMPONENT_SLUG = "#/components/schemas"
|
||||
|
||||
val UNIT_TYPE by lazy { Unit::class.createType() }
|
||||
|
||||
/**
|
||||
* Higher order function that takes a map of names to objects and will log their state ahead of function invocation
|
||||
* Higher order function that takes a map of names to object and will log their state ahead of function invocation
|
||||
* along with the result of the function invocation
|
||||
*/
|
||||
fun <T> logged(functionName: String, entities: Map<String, Any>, block: () -> T): T {
|
||||
@ -70,4 +70,16 @@ object Helpers {
|
||||
.map { it.simpleName }
|
||||
return classNames.joinToString(separator = "-", prefix = "${clazz.simpleName}-")
|
||||
}
|
||||
|
||||
fun String.capitalized() = replaceFirstChar {
|
||||
if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString()
|
||||
}
|
||||
|
||||
fun String.toNumber(): Number {
|
||||
return try {
|
||||
this.toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
this.toDouble()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package io.bkbn.kompendium.models.meta
|
||||
|
||||
import kotlin.reflect.KType
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecResponse
|
||||
|
||||
typealias ErrorMap = Map<KType, Pair<Int, OpenApiSpecResponse<*>>?>
|
@ -1,104 +0,0 @@
|
||||
package io.bkbn.kompendium.models.meta
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
sealed class MethodInfo<TParam, TResp>(
|
||||
open val summary: String,
|
||||
open val description: String? = null,
|
||||
open val tags: Set<String> = emptySet(),
|
||||
open val deprecated: Boolean = false,
|
||||
open val securitySchemes: Set<String> = emptySet(),
|
||||
open val canThrow: Set<KClass<*>> = emptySet(),
|
||||
open val responseInfo: ResponseInfo<TResp>? = null,
|
||||
open val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||
open val operationId: String? = null
|
||||
) {
|
||||
|
||||
data class GetInfo<TParam, TResp>(
|
||||
override val responseInfo: ResponseInfo<TResp>? = null,
|
||||
override val summary: String,
|
||||
override val description: String? = null,
|
||||
override val tags: Set<String> = emptySet(),
|
||||
override val deprecated: Boolean = false,
|
||||
override val securitySchemes: Set<String> = emptySet(),
|
||||
override val canThrow: Set<KClass<*>> = emptySet(),
|
||||
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||
override val operationId: String? = null
|
||||
) : MethodInfo<TParam, TResp>(
|
||||
summary = summary,
|
||||
description = description,
|
||||
tags = tags,
|
||||
deprecated = deprecated,
|
||||
securitySchemes = securitySchemes,
|
||||
canThrow = canThrow,
|
||||
responseInfo = responseInfo,
|
||||
parameterExamples = parameterExamples,
|
||||
operationId = operationId
|
||||
)
|
||||
|
||||
data class PostInfo<TParam, TReq, TResp>(
|
||||
val requestInfo: RequestInfo<TReq>? = null,
|
||||
override val responseInfo: ResponseInfo<TResp>? = null,
|
||||
override val summary: String,
|
||||
override val description: String? = null,
|
||||
override val tags: Set<String> = emptySet(),
|
||||
override val deprecated: Boolean = false,
|
||||
override val securitySchemes: Set<String> = emptySet(),
|
||||
override val canThrow: Set<KClass<*>> = emptySet(),
|
||||
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||
override val operationId: String? = null
|
||||
) : MethodInfo<TParam, TResp>(
|
||||
summary = summary,
|
||||
description = description,
|
||||
tags = tags,
|
||||
deprecated = deprecated,
|
||||
securitySchemes = securitySchemes,
|
||||
canThrow = canThrow,
|
||||
responseInfo = responseInfo,
|
||||
parameterExamples = parameterExamples,
|
||||
operationId = operationId
|
||||
)
|
||||
|
||||
data class PutInfo<TParam, TReq, TResp>(
|
||||
val requestInfo: RequestInfo<TReq>? = null,
|
||||
override val responseInfo: ResponseInfo<TResp>? = null,
|
||||
override val summary: String,
|
||||
override val description: String? = null,
|
||||
override val tags: Set<String> = emptySet(),
|
||||
override val deprecated: Boolean = false,
|
||||
override val securitySchemes: Set<String> = emptySet(),
|
||||
override val canThrow: Set<KClass<*>> = emptySet(),
|
||||
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||
override val operationId: String? = null
|
||||
) : MethodInfo<TParam, TResp>(
|
||||
summary = summary,
|
||||
description = description,
|
||||
tags = tags,
|
||||
deprecated = deprecated,
|
||||
securitySchemes = securitySchemes,
|
||||
canThrow = canThrow,
|
||||
parameterExamples = parameterExamples,
|
||||
operationId = operationId
|
||||
)
|
||||
|
||||
data class DeleteInfo<TParam, TResp>(
|
||||
override val responseInfo: ResponseInfo<TResp>? = null,
|
||||
override val summary: String,
|
||||
override val description: String? = null,
|
||||
override val tags: Set<String> = emptySet(),
|
||||
override val deprecated: Boolean = false,
|
||||
override val securitySchemes: Set<String> = emptySet(),
|
||||
override val canThrow: Set<KClass<*>> = emptySet(),
|
||||
override val parameterExamples: Map<String, TParam> = emptyMap(),
|
||||
override val operationId: String? = null
|
||||
) : MethodInfo<TParam, TResp>(
|
||||
summary = summary,
|
||||
description = description,
|
||||
tags = tags,
|
||||
deprecated = deprecated,
|
||||
securitySchemes = securitySchemes,
|
||||
canThrow = canThrow,
|
||||
parameterExamples = parameterExamples,
|
||||
operationId = operationId
|
||||
)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package io.bkbn.kompendium.models.meta
|
||||
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecComponentSchema
|
||||
|
||||
typealias SchemaMap = Map<String, OpenApiSpecComponentSchema>
|
@ -1,14 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
data class OpenApiSpec(
|
||||
val openapi: String = "3.0.3",
|
||||
val info: OpenApiSpecInfo,
|
||||
// TODO Needs to default to server object with url of `/`
|
||||
val servers: MutableList<OpenApiSpecServer> = mutableListOf(),
|
||||
val paths: MutableMap<String, OpenApiSpecPathItem> = mutableMapOf(),
|
||||
val components: OpenApiSpecComponents = OpenApiSpecComponents(),
|
||||
// todo needs to reference objects in the components -> security scheme 🤔
|
||||
val security: MutableList<Map<String, List<String>>> = mutableListOf(),
|
||||
val tags: MutableList<OpenApiSpecTag> = mutableListOf(),
|
||||
val externalDocs: OpenApiSpecExternalDocumentation? = null
|
||||
)
|
@ -1,42 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
sealed class OpenApiSpecComponentSchema(open val default: Any? = null) {
|
||||
fun addDefault(default: Any?): OpenApiSpecComponentSchema = when (this) {
|
||||
is AnyOfReferencedSchema -> error("Cannot add default to anyOf reference")
|
||||
is ReferencedSchema -> this.copy(default = default)
|
||||
is ObjectSchema -> this.copy(default = default)
|
||||
is DictionarySchema -> this.copy(default = default)
|
||||
is EnumSchema -> this.copy(default = default)
|
||||
is SimpleSchema -> this.copy(default = default)
|
||||
is FormatSchema -> this.copy(default = default)
|
||||
is ArraySchema -> this.copy(default = default)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class TypedSchema(open val type: String, override val default: Any? = null) : OpenApiSpecComponentSchema(default)
|
||||
|
||||
data class ReferencedSchema(val `$ref`: String, override val default: Any? = null) : OpenApiSpecComponentSchema(default)
|
||||
data class AnyOfReferencedSchema(val anyOf: List<ReferencedSchema>) : OpenApiSpecComponentSchema()
|
||||
|
||||
data class ObjectSchema(
|
||||
val properties: Map<String, OpenApiSpecComponentSchema>,
|
||||
override val default: Any? = null
|
||||
) : TypedSchema("object", default)
|
||||
|
||||
data class DictionarySchema(
|
||||
val additionalProperties: OpenApiSpecComponentSchema,
|
||||
override val default: Any? = null
|
||||
) : TypedSchema("object", default)
|
||||
|
||||
data class EnumSchema(
|
||||
val `enum`: Set<String>, override val default: Any? = null
|
||||
) : TypedSchema("string", default)
|
||||
|
||||
data class SimpleSchema(override val type: String, override val default: Any? = null) : TypedSchema(type, default)
|
||||
|
||||
data class FormatSchema(val format: String, override val type: String, override val default: Any? = null) :
|
||||
TypedSchema(type, default)
|
||||
|
||||
data class ArraySchema(val items: OpenApiSpecComponentSchema, override val default: Any? = null) :
|
||||
TypedSchema("array", default)
|
||||
|
@ -1,7 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
// TODO I *think* the only thing I need here is the security https://swagger.io/specification/#components-object
|
||||
data class OpenApiSpecComponents(
|
||||
val schemas: MutableMap<String, OpenApiSpecComponentSchema> = mutableMapOf(),
|
||||
val securitySchemes: MutableMap<String, OpenApiSpecSchemaSecurity> = mutableMapOf()
|
||||
)
|
@ -1,8 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
import java.net.URI
|
||||
|
||||
data class OpenApiSpecExternalDocumentation(
|
||||
val url: URI,
|
||||
val description: String?
|
||||
)
|
@ -1,12 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
import java.net.URI
|
||||
|
||||
data class OpenApiSpecInfo(
|
||||
var title: String? = null,
|
||||
var version: String? = null,
|
||||
var description: String? = null,
|
||||
var termsOfService: URI? = null,
|
||||
var contact: OpenApiSpecInfoContact? = null,
|
||||
var license: OpenApiSpecInfoLicense? = null
|
||||
)
|
@ -1,9 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
import java.net.URI
|
||||
|
||||
data class OpenApiSpecInfoContact(
|
||||
var name: String,
|
||||
var url: URI? = null,
|
||||
var email: String? = null // TODO Enforce email?
|
||||
)
|
@ -1,8 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
import java.net.URI
|
||||
|
||||
data class OpenApiSpecInfoLicense(
|
||||
var name: String,
|
||||
var url: URI? = null
|
||||
)
|
@ -1,10 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
data class OpenApiSpecLink(
|
||||
val operationRef: String?, // todo mutually exclusive with operationId
|
||||
val operationId: String?,
|
||||
val parameters: Map<String, String>, // todo sheesh https://swagger.io/specification/#link-object
|
||||
val requestBody: String, // todo same
|
||||
val description: String?,
|
||||
val server: OpenApiSpecServer?
|
||||
)
|
@ -1,8 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
data class OpenApiSpecMediaType<T>(
|
||||
val schema: OpenApiSpecReferencable,
|
||||
val examples: Map<String, ExampleWrapper<T>>? = null
|
||||
)
|
||||
|
||||
data class ExampleWrapper<T>(val value: T)
|
@ -1,10 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
import java.net.URI
|
||||
|
||||
data class OpenApiSpecOAuthFlow(
|
||||
val authorizationUrl: URI? = null,
|
||||
val tokenUrl: URI? = null,
|
||||
val refreshUrl: URI? = null,
|
||||
val scopes: Map<String, String>? = null
|
||||
)
|
@ -1,5 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
data class OpenApiSpecOAuthFlows(
|
||||
val implicit: OpenApiSpecOAuthFlow?,
|
||||
)
|
@ -1,14 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
data class OpenApiSpecPathItem(
|
||||
var get: OpenApiSpecPathItemOperation? = null,
|
||||
var put: OpenApiSpecPathItemOperation? = null,
|
||||
var post: OpenApiSpecPathItemOperation? = null,
|
||||
var delete: OpenApiSpecPathItemOperation? = null,
|
||||
var options: OpenApiSpecPathItemOperation? = null,
|
||||
var head: OpenApiSpecPathItemOperation? = null,
|
||||
var patch: OpenApiSpecPathItemOperation? = null,
|
||||
var trace: OpenApiSpecPathItemOperation? = null,
|
||||
var servers: List<OpenApiSpecServer>? = null,
|
||||
var parameters: List<OpenApiSpecReferencable>? = null
|
||||
)
|
@ -1,19 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
data class OpenApiSpecPathItemOperation(
|
||||
var tags: Set<String> = emptySet(),
|
||||
var summary: String? = null,
|
||||
var description: String? = null,
|
||||
var externalDocs: OpenApiSpecExternalDocumentation? = null,
|
||||
var operationId: String? = null,
|
||||
var parameters: List<OpenApiSpecReferencable>? = null,
|
||||
var requestBody: OpenApiSpecReferencable? = null,
|
||||
// TODO How to enforce `default` requirement 🧐
|
||||
var responses: Map<Int, OpenApiSpecReferencable>? = null,
|
||||
var callbacks: Map<String, OpenApiSpecReferencable>? = null,
|
||||
var deprecated: Boolean = false,
|
||||
// todo big yikes... also needs to reference objects in the security scheme 🤔
|
||||
var security: List<Map<String, List<String>>>? = null,
|
||||
var servers: List<OpenApiSpecServer>? = null,
|
||||
var `x-codegen-request-body-name`: String? = null
|
||||
)
|
@ -1,31 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
sealed interface OpenApiSpecReferencable
|
||||
|
||||
data class OpenApiAnyOf(val anyOf: List<OpenApiSpecReferenceObject>) : OpenApiSpecReferencable
|
||||
data class OpenApiSpecReferenceObject(val `$ref`: String) : OpenApiSpecReferencable
|
||||
|
||||
data class OpenApiSpecResponse<T>(
|
||||
val description: String? = null,
|
||||
val headers: Map<String, OpenApiSpecReferencable>? = null,
|
||||
val content: Map<String, OpenApiSpecMediaType<T>>? = null,
|
||||
val links: Map<String, OpenApiSpecReferencable>? = null
|
||||
) : OpenApiSpecReferencable
|
||||
|
||||
data class OpenApiSpecParameter(
|
||||
val name: String,
|
||||
val `in`: String, // TODO Enum? "query", "header", "path" or "cookie"
|
||||
val schema: OpenApiSpecComponentSchema,
|
||||
val description: String? = null,
|
||||
val required: Boolean = true,
|
||||
val deprecated: Boolean = false,
|
||||
val allowEmptyValue: Boolean? = null,
|
||||
val style: String? = null,
|
||||
val explode: Boolean? = null
|
||||
) : OpenApiSpecReferencable
|
||||
|
||||
data class OpenApiSpecRequest<T>(
|
||||
val description: String?,
|
||||
val content: Map<String, OpenApiSpecMediaType<T>>,
|
||||
val required: Boolean = false
|
||||
) : OpenApiSpecReferencable
|
@ -1,11 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
data class OpenApiSpecSchemaSecurity(
|
||||
val type: String? = null, // TODO Enum? "apiKey", "http", "oauth2", "openIdConnect"
|
||||
val name: String? = null,
|
||||
val `in`: String? = null,
|
||||
val scheme: String? = null,
|
||||
val flows: OpenApiSpecOAuthFlows? = null,
|
||||
val bearerFormat: String? = null,
|
||||
val description: String? = null,
|
||||
)
|
@ -1,9 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
import java.net.URI
|
||||
|
||||
data class OpenApiSpecServer(
|
||||
val url: URI,
|
||||
val description: String? = null,
|
||||
var variables: Map<String, OpenApiSpecServerVariable>? = null
|
||||
)
|
@ -1,7 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
data class OpenApiSpecServerVariable(
|
||||
val `enum`: Set<String>, // todo enforce not empty
|
||||
val default: String,
|
||||
val description: String?
|
||||
)
|
@ -1,7 +0,0 @@
|
||||
package io.bkbn.kompendium.models.oas
|
||||
|
||||
data class OpenApiSpecTag(
|
||||
val name: String,
|
||||
val description: String? = null,
|
||||
val externalDocs: OpenApiSpecExternalDocumentation? = null
|
||||
)
|
@ -1,13 +0,0 @@
|
||||
package io.bkbn.kompendium.path
|
||||
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.RouteSelector
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
interface IPathCalculator {
|
||||
fun calculate(route: Route?, tail: String = ""): String
|
||||
fun <T : RouteSelector> addCustomRouteHandler(
|
||||
selector: KClass<T>,
|
||||
handler: IPathCalculator.(Route, String) -> String
|
||||
)
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package io.bkbn.kompendium.path
|
||||
|
||||
import io.ktor.routing.PathSegmentConstantRouteSelector
|
||||
import io.ktor.routing.PathSegmentParameterRouteSelector
|
||||
import io.ktor.routing.RootRouteSelector
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.RouteSelector
|
||||
import io.ktor.routing.TrailingSlashRouteSelector
|
||||
import io.ktor.util.InternalAPI
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Responsible for calculating a url path from a provided [Route]
|
||||
*/
|
||||
@OptIn(InternalAPI::class)
|
||||
internal object PathCalculator: IPathCalculator {
|
||||
|
||||
private val pathHandler: RouteHandlerMap = mutableMapOf()
|
||||
|
||||
init {
|
||||
addCustomRouteHandler(RootRouteSelector::class) { _, tail -> tail }
|
||||
addCustomRouteHandler(PathSegmentParameterRouteSelector::class) { route, tail ->
|
||||
val newTail = "/${route.selector}$tail"
|
||||
calculate(route.parent, newTail)
|
||||
}
|
||||
addCustomRouteHandler(PathSegmentConstantRouteSelector::class) { route, tail ->
|
||||
val newTail = "/${route.selector}$tail"
|
||||
calculate(route.parent, newTail)
|
||||
}
|
||||
addCustomRouteHandler(TrailingSlashRouteSelector::class) { route, tail ->
|
||||
val newTail = tail.ifBlank { "/" }
|
||||
calculate(route.parent, newTail)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(InternalAPI::class)
|
||||
override fun calculate(
|
||||
route: Route?,
|
||||
tail: String
|
||||
): String = when (route) {
|
||||
null -> tail
|
||||
else -> when (pathHandler.containsKey(route.selector::class)) {
|
||||
true -> pathHandler[route.selector::class]!!.invoke(this, route, tail)
|
||||
else -> error("No handler has been registered for ${route.selector}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun <T : RouteSelector> addCustomRouteHandler(
|
||||
selector: KClass<T>,
|
||||
handler: IPathCalculator.(Route, String) -> String
|
||||
) {
|
||||
pathHandler[selector] = handler
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package io.bkbn.kompendium.path
|
||||
|
||||
import io.ktor.routing.Route
|
||||
import io.ktor.routing.RouteSelector
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
typealias RouteHandlerMap = MutableMap<KClass<out RouteSelector>, IPathCalculator.(Route, String) -> String>
|
@ -1,31 +0,0 @@
|
||||
package io.bkbn.kompendium.routes
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpec
|
||||
import io.ktor.application.call
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.Routing
|
||||
import io.ktor.routing.get
|
||||
import io.ktor.routing.route
|
||||
|
||||
/**
|
||||
* Provides an out-of-the-box route to return the generated [OpenApiSpec]
|
||||
* @param oas spec that is returned
|
||||
* @param om provider for Jackson
|
||||
*/
|
||||
fun Routing.openApi(
|
||||
oas: OpenApiSpec,
|
||||
om: ObjectMapper = objectMapper
|
||||
) {
|
||||
route("/openapi.json") {
|
||||
get {
|
||||
call.respondText { om.writeValueAsString(oas) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val objectMapper = ObjectMapper()
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
.enable(SerializationFeature.INDENT_OUTPUT)
|
@ -1,643 +0,0 @@
|
||||
package io.bkbn.kompendium
|
||||
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.routing.routing
|
||||
import io.ktor.server.testing.handleRequest
|
||||
import io.ktor.server.testing.withTestApplication
|
||||
import java.net.URI
|
||||
import kotlin.test.AfterTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecInfo
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecInfoContact
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecInfoLicense
|
||||
import io.bkbn.kompendium.models.oas.OpenApiSpecServer
|
||||
import io.bkbn.kompendium.routes.openApi
|
||||
import io.bkbn.kompendium.routes.redoc
|
||||
import io.bkbn.kompendium.util.TestHelpers.getFileSnapshot
|
||||
import io.bkbn.kompendium.util.complexType
|
||||
import io.bkbn.kompendium.util.jacksonConfigModule
|
||||
import io.bkbn.kompendium.util.emptyGet
|
||||
import io.bkbn.kompendium.util.genericPolymorphicResponse
|
||||
import io.bkbn.kompendium.util.genericPolymorphicResponseMultipleImpls
|
||||
import io.bkbn.kompendium.util.headerParameter
|
||||
import io.bkbn.kompendium.util.kotlinxConfigModule
|
||||
import io.bkbn.kompendium.util.nestedUnderRootModule
|
||||
import io.bkbn.kompendium.util.nonRequiredParamsGet
|
||||
import io.bkbn.kompendium.util.notarizedDeleteModule
|
||||
import io.bkbn.kompendium.util.notarizedGetModule
|
||||
import io.bkbn.kompendium.util.notarizedGetWithMultipleThrowables
|
||||
import io.bkbn.kompendium.util.notarizedGetWithNotarizedException
|
||||
import io.bkbn.kompendium.util.notarizedPostModule
|
||||
import io.bkbn.kompendium.util.notarizedPutModule
|
||||
import io.bkbn.kompendium.util.pathParsingTestModule
|
||||
import io.bkbn.kompendium.util.polymorphicCollectionResponse
|
||||
import io.bkbn.kompendium.util.polymorphicInterfaceResponse
|
||||
import io.bkbn.kompendium.util.polymorphicMapResponse
|
||||
import io.bkbn.kompendium.util.polymorphicResponse
|
||||
import io.bkbn.kompendium.util.primitives
|
||||
import io.bkbn.kompendium.util.returnsList
|
||||
import io.bkbn.kompendium.util.rootModule
|
||||
import io.bkbn.kompendium.util.simpleGenericResponse
|
||||
import io.bkbn.kompendium.util.statusPageModule
|
||||
import io.bkbn.kompendium.util.statusPageMultiExceptions
|
||||
import io.bkbn.kompendium.util.trailingSlash
|
||||
import io.bkbn.kompendium.util.undeclaredType
|
||||
import io.bkbn.kompendium.util.withDefaultParameter
|
||||
import io.bkbn.kompendium.util.withExamples
|
||||
import io.bkbn.kompendium.util.withOperationId
|
||||
|
||||
internal class KompendiumTest {
|
||||
|
||||
@AfterTest
|
||||
fun `reset kompendium`() {
|
||||
Kompendium.resetSchema()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Kompendium can be instantiated with no details`() {
|
||||
assertEquals(Kompendium.openApiSpec.openapi, "3.0.3", "Kompendium has a default spec version of 3.0.3")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized Get records all expected information`() {
|
||||
withTestApplication({
|
||||
kotlinxConfigModule()
|
||||
docs()
|
||||
notarizedGetModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("notarized_get.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized Get does not interrupt the pipeline`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
notarizedGetModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/test").response.content
|
||||
|
||||
// expect
|
||||
val expected = "hey dude ‼️ congratz on the get request"
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized Post records all expected information`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
notarizedPostModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("notarized_post.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized post does not interrupt the pipeline`() {
|
||||
withTestApplication({
|
||||
kotlinxConfigModule()
|
||||
docs()
|
||||
notarizedPostModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Post, "/test").response.content
|
||||
|
||||
// expect
|
||||
val expected = "hey dude ✌️ congratz on the post request"
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized Put records all expected information`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
notarizedPutModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("notarized_put.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized put does not interrupt the pipeline`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
notarizedPutModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Put, "/test").response.content
|
||||
|
||||
// expect
|
||||
val expected = "hey pal 🌝 whatcha doin' here?"
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized delete records all expected information`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
notarizedDeleteModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("notarized_delete.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Notarized delete does not interrupt the pipeline`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
notarizedDeleteModule()
|
||||
}) {
|
||||
// do
|
||||
val status = handleRequest(HttpMethod.Delete, "/test").response.status()
|
||||
|
||||
// expect
|
||||
assertEquals(HttpStatusCode.NoContent, status, "No content status should be received")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Path parser stores the expected path`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
pathParsingTestModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("path_parser.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can notarize the root route`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
rootModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("root_route.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can call the root route`() {
|
||||
withTestApplication({
|
||||
kotlinxConfigModule()
|
||||
docs()
|
||||
rootModule()
|
||||
}) {
|
||||
// do
|
||||
val result = handleRequest(HttpMethod.Get, "/").response.content
|
||||
|
||||
// expect
|
||||
val expected = "☎️🏠🌲"
|
||||
assertEquals(expected, result, "Should be the same")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Nested under root module does not append trailing slash`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
nestedUnderRootModule()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("nested_under_root.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can notarize a trailing slash route`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
trailingSlash()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("trailing_slash.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can call a trailing slash route`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
trailingSlash()
|
||||
}) {
|
||||
// do
|
||||
val result = handleRequest(HttpMethod.Get, "/test/").response.content
|
||||
|
||||
// expect
|
||||
val expected = "🙀👾"
|
||||
assertEquals(expected, result, "Should be the same")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can notarize a complex type`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
complexType()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("complex_type.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can notarize primitives`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
primitives()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("notarized_primitives.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can notarize a top level list response`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
returnsList()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("response_list.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can notarize route with no request params and no response body`() {
|
||||
withTestApplication({
|
||||
kotlinxConfigModule()
|
||||
docs()
|
||||
emptyGet()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("no_request_params_and_no_response_body.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can notarize route with non-required params`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
nonRequiredParamsGet()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("non_required_params.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can add operationId`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
withOperationId()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("notarized_get_with_operation_id.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Generates the expected redoc`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
returnsList()
|
||||
}) {
|
||||
// do
|
||||
val html = handleRequest(HttpMethod.Get, "/docs").response.content
|
||||
|
||||
// expected
|
||||
val expected = getFileSnapshot("redoc.html")
|
||||
assertEquals(expected, html)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Generates additional responses when passed a throwable`() {
|
||||
withTestApplication({
|
||||
statusPageModule()
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
notarizedGetWithNotarizedException()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("notarized_get_with_exception_response.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Generates additional responses when passed multiple throwables`() {
|
||||
withTestApplication({
|
||||
statusPageMultiExceptions()
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
notarizedGetWithMultipleThrowables()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("notarized_get_with_multiple_exception_responses.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can generate example response and request bodies`() {
|
||||
withTestApplication({
|
||||
kotlinxConfigModule()
|
||||
docs()
|
||||
withExamples()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("example_req_and_resp.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can generate a default parameter value`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
withDefaultParameter()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("query_with_default_parameter.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can generate a polymorphic response type`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
polymorphicResponse()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("polymorphic_response.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can generate a collection with polymorphic response type`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
polymorphicCollectionResponse()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("polymorphic_list_response.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can generate a map with a polymorphic response type`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
polymorphicMapResponse()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("polymorphic_map_response.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can generate a polymorphic response from a sealed interface`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
polymorphicInterfaceResponse()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("sealed_interface_response.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can generate a response type with a generic type`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
simpleGenericResponse()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("generic_response.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can generate a polymorphic response type with generics`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
genericPolymorphicResponse()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("polymorphic_response_with_generics.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Absolute Psycho Inheritance Test`() {
|
||||
withTestApplication({
|
||||
kotlinxConfigModule()
|
||||
docs()
|
||||
genericPolymorphicResponseMultipleImpls()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("crazy_polymorphic_example.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can add an undeclared field`() {
|
||||
withTestApplication({
|
||||
kotlinxConfigModule()
|
||||
docs()
|
||||
undeclaredType()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("undeclared_field.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can add a custom header parameter with a name override`() {
|
||||
withTestApplication({
|
||||
jacksonConfigModule()
|
||||
docs()
|
||||
headerParameter()
|
||||
}) {
|
||||
// do
|
||||
val json = handleRequest(HttpMethod.Get, "/openapi.json").response.content
|
||||
|
||||
// expect
|
||||
val expected = getFileSnapshot("override_parameter_name.json").trim()
|
||||
assertEquals(expected, json, "The received json spec should match the expected content")
|
||||
}
|
||||
}
|
||||
|
||||
private val oas = Kompendium.openApiSpec.copy(
|
||||
info = OpenApiSpecInfo(
|
||||
title = "Test API",
|
||||
version = "1.33.7",
|
||||
description = "An amazing, fully-ish 😉 generated API spec",
|
||||
termsOfService = URI("https://example.com"),
|
||||
contact = OpenApiSpecInfoContact(
|
||||
name = "Homer Simpson",
|
||||
email = "chunkylover53@aol.com",
|
||||
url = URI("https://gph.is/1NPUDiM")
|
||||
),
|
||||
license = OpenApiSpecInfoLicense(
|
||||
name = "MIT",
|
||||
url = URI("https://github.com/bkbnio/kompendium/blob/main/LICENSE")
|
||||
)
|
||||
),
|
||||
servers = mutableListOf(
|
||||
OpenApiSpecServer(
|
||||
url = URI("https://myawesomeapi.com"),
|
||||
description = "Production instance of my API"
|
||||
),
|
||||
OpenApiSpecServer(
|
||||
url = URI("https://staging.myawesomeapi.com"),
|
||||
description = "Where the fun stuff happens"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private fun Application.docs() {
|
||||
routing {
|
||||
openApi(oas)
|
||||
redoc(oas)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
package io.bkbn.kompendium
|
||||
|
||||
import java.util.UUID
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
import io.bkbn.kompendium.Kontent.generateKontent
|
||||
import io.bkbn.kompendium.Kontent.generateParameterKontent
|
||||
import io.bkbn.kompendium.models.oas.DictionarySchema
|
||||
import io.bkbn.kompendium.models.oas.FormatSchema
|
||||
import io.bkbn.kompendium.models.oas.ObjectSchema
|
||||
import io.bkbn.kompendium.models.oas.ReferencedSchema
|
||||
import io.bkbn.kompendium.util.*
|
||||
|
||||
@ExperimentalStdlibApi
|
||||
internal class KontentTest {
|
||||
|
||||
@Test
|
||||
fun `Unit returns empty map on generate`() {
|
||||
// do
|
||||
val result = generateKontent<Unit>()
|
||||
|
||||
// expect
|
||||
assertTrue { result.isEmpty() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Primitive types return a single map result`() {
|
||||
// do
|
||||
val result = generateKontent<Long>()
|
||||
|
||||
// expect
|
||||
assertEquals(1, result.count(), "Should have a single result")
|
||||
assertEquals(FormatSchema("int64", "integer"), result["Long"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Object with BigDecimal and BigInteger types`() {
|
||||
// do
|
||||
val result = generateKontent<TestBigNumberModel>()
|
||||
|
||||
// expect
|
||||
assertEquals(3, result.count())
|
||||
assertTrue { result.containsKey(TestBigNumberModel::class.simpleName) }
|
||||
assertEquals(FormatSchema("double", "number"), result["BigDecimal"])
|
||||
assertEquals(FormatSchema("int64", "integer"), result["BigInteger"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Object with ByteArray type`() {
|
||||
// do
|
||||
val result = generateKontent<TestByteArrayModel>()
|
||||
|
||||
// expect
|
||||
assertEquals(2, result.count())
|
||||
assertTrue { result.containsKey(TestByteArrayModel::class.simpleName) }
|
||||
assertEquals(FormatSchema("byte", "string"), result["ByteArray"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Objects reference their base types in the cache`() {
|
||||
// do
|
||||
val result = generateKontent<TestSimpleModel>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
assertEquals(3, result.count())
|
||||
assertTrue { result.containsKey(TestSimpleModel::class.simpleName) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generation works for nested object types`() {
|
||||
// do
|
||||
val result = generateKontent<TestNestedModel>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
assertEquals(4, result.count())
|
||||
assertTrue { result.containsKey(TestNestedModel::class.simpleName) }
|
||||
assertTrue { result.containsKey(TestSimpleModel::class.simpleName) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generation does not repeat for cached items`() {
|
||||
// when
|
||||
val clazz = TestNestedModel::class
|
||||
val initialCache = generateKontent<TestNestedModel>()
|
||||
|
||||
// do
|
||||
val result = generateKontent<TestSimpleModel>(initialCache)
|
||||
|
||||
// expect TODO Spy to check invocation count?
|
||||
assertNotNull(result)
|
||||
assertEquals(4, result.count())
|
||||
assertTrue { result.containsKey(clazz.simpleName) }
|
||||
assertTrue { result.containsKey(TestSimpleModel::class.simpleName) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generation allows for enum fields`() {
|
||||
// do
|
||||
val result = generateKontent<TestSimpleWithEnums>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
assertEquals(3, result.count())
|
||||
assertTrue { result.containsKey(TestSimpleWithEnums::class.simpleName) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generation allows for map fields`() {
|
||||
// do
|
||||
val result = generateKontent<TestSimpleWithMap>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
assertEquals(5, result.count())
|
||||
assertTrue { result.containsKey("Map-String-TestSimpleModel") }
|
||||
assertTrue { result.containsKey(TestSimpleWithMap::class.simpleName) }
|
||||
|
||||
val os = result[TestSimpleWithMap::class.simpleName] as ObjectSchema
|
||||
val expectedRef = ReferencedSchema("#/components/schemas/Map-String-TestSimpleModel")
|
||||
assertEquals(expectedRef, os.properties["b"])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `map fields that are not string result in error`() {
|
||||
// expect
|
||||
assertFailsWith<IllegalStateException> { generateKontent<TestInvalidMap>() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generation allows for collection fields`() {
|
||||
// do
|
||||
val result = generateKontent<TestSimpleWithList>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
assertEquals(6, result.count())
|
||||
assertTrue { result.containsKey("List-TestSimpleModel") }
|
||||
assertTrue { result.containsKey(TestSimpleWithList::class.simpleName) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can parse enum list as a field`() {
|
||||
// do
|
||||
val result = generateKontent<TestSimpleWithEnumList>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UUID schema support`() {
|
||||
// do
|
||||
val result = generateKontent<TestWithUUID>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
assertEquals(2, result.count())
|
||||
assertTrue { result.containsKey(UUID::class.simpleName) }
|
||||
assertTrue { result.containsKey(TestWithUUID::class.simpleName) }
|
||||
val expectedSchema = result[UUID::class.simpleName] as FormatSchema
|
||||
assertEquals(FormatSchema("uuid", "string"), expectedSchema)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Generate top level list response`() {
|
||||
// do
|
||||
val result = generateKontent<List<TestSimpleModel>>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Can handle a complex type`() {
|
||||
// do
|
||||
val result = generateKontent<ComplexRequest>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
assertEquals(7, result.count())
|
||||
assertTrue { result.containsKey("Map-String-CrazyItem") }
|
||||
val ds = result["Map-String-CrazyItem"] as DictionarySchema
|
||||
val rs = ds.additionalProperties as ReferencedSchema
|
||||
assertEquals(ReferencedSchema("#/components/schemas/CrazyItem"), rs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Parameter kontent filters out top level declaration`() {
|
||||
// do
|
||||
val result = generateParameterKontent<TestSimpleModel>()
|
||||
|
||||
// expect
|
||||
assertNotNull(result)
|
||||
assertEquals(2, result.count())
|
||||
assertFalse { result.containsKey(TestSimpleModel::class.simpleName) }
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,276 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.apiFunctionalityTest
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.getFileSnapshot
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTest
|
||||
import io.bkbn.kompendium.core.util.complexType
|
||||
import io.bkbn.kompendium.core.util.constrainedDoubleInfo
|
||||
import io.bkbn.kompendium.core.util.constrainedIntInfo
|
||||
import io.bkbn.kompendium.core.util.defaultField
|
||||
import io.bkbn.kompendium.core.util.defaultParameter
|
||||
import io.bkbn.kompendium.core.util.exclusiveMinMax
|
||||
import io.bkbn.kompendium.core.util.formattedParam
|
||||
import io.bkbn.kompendium.core.util.freeFormObject
|
||||
import io.bkbn.kompendium.core.util.genericPolymorphicResponse
|
||||
import io.bkbn.kompendium.core.util.genericPolymorphicResponseMultipleImpls
|
||||
import io.bkbn.kompendium.core.util.headerParameter
|
||||
import io.bkbn.kompendium.core.util.minMaxArray
|
||||
import io.bkbn.kompendium.core.util.minMaxFreeForm
|
||||
import io.bkbn.kompendium.core.util.minMaxString
|
||||
import io.bkbn.kompendium.core.util.multipleOfDouble
|
||||
import io.bkbn.kompendium.core.util.multipleOfInt
|
||||
import io.bkbn.kompendium.core.util.nestedUnderRootModule
|
||||
import io.bkbn.kompendium.core.util.nonRequiredParamsGet
|
||||
import io.bkbn.kompendium.core.util.notarizedDeleteModule
|
||||
import io.bkbn.kompendium.core.util.notarizedGetModule
|
||||
import io.bkbn.kompendium.core.util.notarizedGetWithGenericErrorResponse
|
||||
import io.bkbn.kompendium.core.util.notarizedGetWithMultipleThrowables
|
||||
import io.bkbn.kompendium.core.util.notarizedGetWithNotarizedException
|
||||
import io.bkbn.kompendium.core.util.notarizedGetWithPolymorphicErrorResponse
|
||||
import io.bkbn.kompendium.core.util.notarizedPostModule
|
||||
import io.bkbn.kompendium.core.util.notarizedPutModule
|
||||
import io.bkbn.kompendium.core.util.nullableField
|
||||
import io.bkbn.kompendium.core.util.overrideFieldInfo
|
||||
import io.bkbn.kompendium.core.util.pathParsingTestModule
|
||||
import io.bkbn.kompendium.core.util.polymorphicCollectionResponse
|
||||
import io.bkbn.kompendium.core.util.polymorphicInterfaceResponse
|
||||
import io.bkbn.kompendium.core.util.polymorphicMapResponse
|
||||
import io.bkbn.kompendium.core.util.polymorphicResponse
|
||||
import io.bkbn.kompendium.core.util.primitives
|
||||
import io.bkbn.kompendium.core.util.regexString
|
||||
import io.bkbn.kompendium.core.util.requiredParameter
|
||||
import io.bkbn.kompendium.core.util.returnsList
|
||||
import io.bkbn.kompendium.core.util.rootModule
|
||||
import io.bkbn.kompendium.core.util.simpleGenericResponse
|
||||
import io.bkbn.kompendium.core.util.trailingSlash
|
||||
import io.bkbn.kompendium.core.util.undeclaredType
|
||||
import io.bkbn.kompendium.core.util.uniqueArray
|
||||
import io.bkbn.kompendium.core.util.withDefaultParameter
|
||||
import io.bkbn.kompendium.core.util.withExamples
|
||||
import io.bkbn.kompendium.core.util.withOperationId
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.HttpStatusCode
|
||||
|
||||
class KompendiumTest : DescribeSpec({
|
||||
describe("Notarized Open API Metadata Tests") {
|
||||
it("Can notarize a get request") {
|
||||
// act
|
||||
openApiTest("notarized_get.json") { notarizedGetModule() }
|
||||
}
|
||||
it("Can notarize a post request") {
|
||||
// act
|
||||
openApiTest("notarized_post.json") { notarizedPostModule() }
|
||||
}
|
||||
it("Can notarize a put request") {
|
||||
// act
|
||||
openApiTest("notarized_put.json") { notarizedPutModule() }
|
||||
}
|
||||
it("Can notarize a delete request") {
|
||||
// act
|
||||
openApiTest("notarized_delete.json") { notarizedDeleteModule() }
|
||||
}
|
||||
it("Can notarize a complex type") {
|
||||
// act
|
||||
openApiTest("complex_type.json") { complexType() }
|
||||
}
|
||||
it("Can notarize primitives") {
|
||||
// act
|
||||
openApiTest("notarized_primitives.json") { primitives() }
|
||||
}
|
||||
it("Can notarize a top level list response") {
|
||||
// act
|
||||
openApiTest("response_list.json") { returnsList() }
|
||||
}
|
||||
it("Can notarize a route with non-required params") {
|
||||
// act
|
||||
openApiTest("non_required_params.json") { nonRequiredParamsGet() }
|
||||
}
|
||||
}
|
||||
describe("Notarized Ktor Functionality Tests") {
|
||||
it("Can notarized a get request and return the expected result") {
|
||||
// act
|
||||
apiFunctionalityTest("hey dude ‼️ congratz on the get request") { notarizedGetModule() }
|
||||
}
|
||||
it("Can notarize a post request and return the expected result") {
|
||||
// act
|
||||
apiFunctionalityTest(
|
||||
"hey dude ✌️ congratz on the post request",
|
||||
httpMethod = HttpMethod.Post
|
||||
) { notarizedPostModule() }
|
||||
}
|
||||
it("Can notarize a put request and return the expected result") {
|
||||
// act
|
||||
apiFunctionalityTest("hey pal 🌝 whatcha doin' here?", httpMethod = HttpMethod.Put) { notarizedPutModule() }
|
||||
}
|
||||
it("Can notarize a delete request and return the expected result") {
|
||||
// act
|
||||
apiFunctionalityTest(
|
||||
null,
|
||||
httpMethod = HttpMethod.Delete,
|
||||
expectedStatusCode = HttpStatusCode.NoContent
|
||||
) { notarizedDeleteModule() }
|
||||
}
|
||||
it("Can notarize the root route and return the expected result") {
|
||||
// act
|
||||
apiFunctionalityTest("☎️🏠🌲", "/") { rootModule() }
|
||||
}
|
||||
it("Can notarize a trailing slash route and return the expected result") {
|
||||
// act
|
||||
apiFunctionalityTest("🙀👾", "/test/") { trailingSlash() }
|
||||
}
|
||||
}
|
||||
describe("Route Parsing") {
|
||||
it("Can parse a simple path and store it under the expected route") {
|
||||
// act
|
||||
openApiTest("path_parser.json") { pathParsingTestModule() }
|
||||
}
|
||||
it("Can notarize the root route") {
|
||||
// act
|
||||
openApiTest("root_route.json") { rootModule() }
|
||||
}
|
||||
it("Can notarize a route under the root module without appending trailing slash") {
|
||||
// act
|
||||
openApiTest("nested_under_root.json") { nestedUnderRootModule() }
|
||||
}
|
||||
it("Can notarize a route with a trailing slash") {
|
||||
// act
|
||||
openApiTest("trailing_slash.json") { trailingSlash() }
|
||||
}
|
||||
}
|
||||
describe("Exceptions") {
|
||||
it("Can add an exception status code to a response") {
|
||||
// act
|
||||
openApiTest("notarized_get_with_exception_response.json") { notarizedGetWithNotarizedException() }
|
||||
}
|
||||
it("Can support multiple response codes") {
|
||||
// act
|
||||
openApiTest("notarized_get_with_multiple_exception_responses.json") { notarizedGetWithMultipleThrowables() }
|
||||
}
|
||||
it("Can add a polymorphic exception response") {
|
||||
// act
|
||||
openApiTest("polymorphic_error_status_codes.json") { notarizedGetWithPolymorphicErrorResponse() }
|
||||
}
|
||||
it("Can add a generic exception response") {
|
||||
// act
|
||||
openApiTest("generic_exception.json") { notarizedGetWithGenericErrorResponse() }
|
||||
}
|
||||
}
|
||||
describe("Examples") {
|
||||
it("Can generate example response and request bodies") {
|
||||
// act
|
||||
openApiTest("example_req_and_resp.json") { withExamples() }
|
||||
}
|
||||
}
|
||||
describe("Defaults") {
|
||||
it("Can generate a default parameter values") {
|
||||
// act
|
||||
openApiTest("query_with_default_parameter.json") { withDefaultParameter() }
|
||||
}
|
||||
}
|
||||
describe("Required Fields") {
|
||||
it("Marks a parameter required if there is no default and it is not marked nullable") {
|
||||
openApiTest("required_param.json") { requiredParameter() }
|
||||
}
|
||||
it("Does not mark a parameter as required if a default value is provided") {
|
||||
openApiTest("default_param.json") { defaultParameter() }
|
||||
}
|
||||
it("Does not mark a field as required if a default value is provided") {
|
||||
openApiTest("default_field.json") { defaultField() }
|
||||
}
|
||||
it("Marks a field as nullable when expected") {
|
||||
openApiTest("nullable_field.json") { nullableField() }
|
||||
}
|
||||
}
|
||||
describe("Polymorphism and Generics") {
|
||||
it("can generate a polymorphic response type") {
|
||||
// act
|
||||
openApiTest("polymorphic_response.json") { polymorphicResponse() }
|
||||
}
|
||||
it("Can generate a collection with polymorphic response type") {
|
||||
// act
|
||||
openApiTest("polymorphic_list_response.json") { polymorphicCollectionResponse() }
|
||||
}
|
||||
it("Can generate a map with a polymorphic response type") {
|
||||
// act
|
||||
openApiTest("polymorphic_map_response.json") { polymorphicMapResponse() }
|
||||
}
|
||||
it("Can generate a polymorphic response from a sealed interface") {
|
||||
// act
|
||||
openApiTest("sealed_interface_response.json") { polymorphicInterfaceResponse() }
|
||||
}
|
||||
it("Can generate a response type with a generic type") {
|
||||
// act
|
||||
openApiTest("generic_response.json") { simpleGenericResponse() }
|
||||
}
|
||||
it("Can generate a polymorphic response type with generics") {
|
||||
// act
|
||||
openApiTest("polymorphic_response_with_generics.json") { genericPolymorphicResponse() }
|
||||
}
|
||||
it("Can handle an absolutely psycho inheritance test") {
|
||||
// act
|
||||
openApiTest("crazy_polymorphic_example.json") { genericPolymorphicResponseMultipleImpls() }
|
||||
}
|
||||
}
|
||||
describe("Miscellaneous") {
|
||||
it("Can generate the necessary ReDoc home page") {
|
||||
// act
|
||||
apiFunctionalityTest(getFileSnapshot("redoc.html"), "/docs") { returnsList() }
|
||||
}
|
||||
it("Can add an operation id to a notarized route") {
|
||||
// act
|
||||
openApiTest("notarized_get_with_operation_id.json") { withOperationId() }
|
||||
}
|
||||
it("Can add an undeclared field") {
|
||||
// act
|
||||
openApiTest("undeclared_field.json") { undeclaredType() }
|
||||
}
|
||||
it("Can add a custom header parameter with a name override") {
|
||||
// act
|
||||
openApiTest("override_parameter_name.json") { headerParameter() }
|
||||
}
|
||||
it("Can override field values via annotation") {
|
||||
openApiTest("field_override.json") { overrideFieldInfo() }
|
||||
}
|
||||
}
|
||||
describe("Constraints") {
|
||||
it("Can set a minimum and maximum integer value") {
|
||||
openApiTest("min_max_int_field.json") { constrainedIntInfo() }
|
||||
}
|
||||
it("Can set a minimum and maximum double value") {
|
||||
openApiTest("min_max_double_field.json") { constrainedDoubleInfo() }
|
||||
}
|
||||
it("Can set an exclusive min and exclusive max integer value") {
|
||||
openApiTest("exclusive_min_max.json") { exclusiveMinMax() }
|
||||
}
|
||||
it("Can add a custom format to a string field") {
|
||||
openApiTest("formatted_param_type.json") { formattedParam() }
|
||||
}
|
||||
it("Can set a minimum and maximum length on a string field") {
|
||||
openApiTest("min_max_string.json") { minMaxString() }
|
||||
}
|
||||
it("Can set a custom regex pattern on a string field") {
|
||||
openApiTest("regex_string.json") { regexString() }
|
||||
}
|
||||
it("Can set a minimum and maximum item count on an array field") {
|
||||
openApiTest("min_max_array.json") { minMaxArray() }
|
||||
}
|
||||
it("Can set a unique items constraint on an array field") {
|
||||
openApiTest("unique_array.json") { uniqueArray() }
|
||||
}
|
||||
it("Can set a multiple-of constraint on an int field") {
|
||||
openApiTest("multiple_of_int.json") { multipleOfInt() }
|
||||
}
|
||||
it("Can set a multiple of constraint on an double field") {
|
||||
openApiTest("multiple_of_double.json") { multipleOfDouble() }
|
||||
}
|
||||
it("Can set a minimum and maximum number of properties on a free-form type") {
|
||||
openApiTest("min_max_free_form.json") { minMaxFreeForm() }
|
||||
}
|
||||
}
|
||||
describe("Free Form") {
|
||||
it("Can create a free-form field") {
|
||||
openApiTest("free_form_object.json") { freeFormObject() }
|
||||
}
|
||||
}
|
||||
})
|
@ -0,0 +1,173 @@
|
||||
package io.bkbn.kompendium.core
|
||||
|
||||
import io.bkbn.kompendium.core.Kontent.generateKontent
|
||||
import io.bkbn.kompendium.core.fixtures.ComplexRequest
|
||||
import io.bkbn.kompendium.core.fixtures.TestBigNumberModel
|
||||
import io.bkbn.kompendium.core.fixtures.TestByteArrayModel
|
||||
import io.bkbn.kompendium.core.fixtures.TestInvalidMap
|
||||
import io.bkbn.kompendium.core.fixtures.TestNestedModel
|
||||
import io.bkbn.kompendium.core.fixtures.TestSimpleModel
|
||||
import io.bkbn.kompendium.core.fixtures.TestSimpleWithEnumList
|
||||
import io.bkbn.kompendium.core.fixtures.TestSimpleWithEnums
|
||||
import io.bkbn.kompendium.core.fixtures.TestSimpleWithList
|
||||
import io.bkbn.kompendium.core.fixtures.TestSimpleWithMap
|
||||
import io.bkbn.kompendium.core.fixtures.TestWithUUID
|
||||
import io.bkbn.kompendium.oas.schema.DictionarySchema
|
||||
import io.bkbn.kompendium.oas.schema.FormattedSchema
|
||||
import io.bkbn.kompendium.oas.schema.ObjectSchema
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
import io.kotest.matchers.maps.beEmpty
|
||||
import io.kotest.matchers.maps.shouldContainKey
|
||||
import io.kotest.matchers.maps.shouldHaveKey
|
||||
import io.kotest.matchers.maps.shouldHaveSize
|
||||
import io.kotest.matchers.maps.shouldNotHaveKey
|
||||
import io.kotest.matchers.should
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import java.util.UUID
|
||||
|
||||
class KontentTest : DescribeSpec({
|
||||
describe("Kontent analysis") {
|
||||
it("Can return an empty map when passed Unit") {
|
||||
// act
|
||||
val result = generateKontent<Unit>()
|
||||
|
||||
// assert
|
||||
result should beEmpty()
|
||||
}
|
||||
it("Can return a single map result when analyzing a primitive") {
|
||||
// act
|
||||
val result = generateKontent<Long>()
|
||||
|
||||
// assert
|
||||
result shouldHaveSize 1
|
||||
result["Long"] shouldBe FormattedSchema("int64", "integer")
|
||||
}
|
||||
it("Can handle BigDecimal and BigInteger Types") {
|
||||
// act
|
||||
val result = generateKontent<TestBigNumberModel>()
|
||||
|
||||
// assert
|
||||
result shouldHaveSize 3
|
||||
result shouldContainKey TestBigNumberModel::class.simpleName!!
|
||||
result["BigDecimal"] shouldBe FormattedSchema("double", "number")
|
||||
result["BigInteger"] shouldBe FormattedSchema("int64", "integer")
|
||||
}
|
||||
it("Can handle ByteArray type") {
|
||||
// act
|
||||
val result = generateKontent<TestByteArrayModel>()
|
||||
|
||||
// assert
|
||||
result shouldHaveSize 2
|
||||
result shouldContainKey TestByteArrayModel::class.simpleName!!
|
||||
result["ByteArray"] shouldBe FormattedSchema("byte", "string")
|
||||
}
|
||||
it("Allows objects to reference their base type in the cache") {
|
||||
// act
|
||||
val result = generateKontent<TestSimpleModel>()
|
||||
|
||||
// assert
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 3
|
||||
result shouldContainKey TestSimpleModel::class.simpleName!!
|
||||
}
|
||||
it("Can generate cache for nested object types") {
|
||||
// act
|
||||
val result = generateKontent<TestNestedModel>()
|
||||
|
||||
// assert
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 4
|
||||
result shouldContainKey TestNestedModel::class.simpleName!!
|
||||
result shouldContainKey TestSimpleModel::class.simpleName!!
|
||||
}
|
||||
it("Does not repeat generation for cached items") {
|
||||
// arrange
|
||||
val clazz = TestNestedModel::class
|
||||
val initialCache = generateKontent<TestNestedModel>()
|
||||
|
||||
// act
|
||||
val result = generateKontent<TestSimpleModel>(initialCache)
|
||||
|
||||
// assert TODO Spy to check invocation count?
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 4
|
||||
result shouldContainKey clazz.simpleName!!
|
||||
result shouldContainKey TestSimpleModel::class.simpleName!!
|
||||
}
|
||||
it("allows for generation of enum types") {
|
||||
// act
|
||||
val result = generateKontent<TestSimpleWithEnums>()
|
||||
|
||||
// assert
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 3
|
||||
result shouldContainKey TestSimpleWithEnums::class.simpleName!!
|
||||
}
|
||||
it("Allows for generation of map fields") {
|
||||
// act
|
||||
val result = generateKontent<TestSimpleWithMap>()
|
||||
|
||||
// assert
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 5
|
||||
result shouldContainKey "Map-String-TestSimpleModel"
|
||||
result shouldContainKey TestSimpleWithMap::class.simpleName!!
|
||||
result[TestSimpleWithMap::class.simpleName] as ObjectSchema shouldNotBe null // TODO Improve
|
||||
}
|
||||
it("Throws an error if a map is of an invalid type") {
|
||||
// assert
|
||||
shouldThrow<IllegalStateException> { generateKontent<TestInvalidMap>() }
|
||||
}
|
||||
it("Can generate for collection fields") {
|
||||
// act
|
||||
val result = generateKontent<TestSimpleWithList>()
|
||||
|
||||
// assert
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 6
|
||||
result shouldContainKey "List-TestSimpleModel"
|
||||
result shouldContainKey TestSimpleWithList::class.simpleName!!
|
||||
}
|
||||
it("Can parse an enum list as a field") {
|
||||
// act
|
||||
val result = generateKontent<TestSimpleWithEnumList>()
|
||||
|
||||
// assert
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 4
|
||||
result shouldHaveKey "List-SimpleEnum"
|
||||
}
|
||||
it("Can support UUIDs") {
|
||||
// act
|
||||
val result = generateKontent<TestWithUUID>()
|
||||
|
||||
// assert
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 2
|
||||
result shouldContainKey UUID::class.simpleName!!
|
||||
result shouldContainKey TestWithUUID::class.simpleName!!
|
||||
result[UUID::class.simpleName] as FormattedSchema shouldBe FormattedSchema("uuid", "string")
|
||||
}
|
||||
it("Can generate a top level list response") {
|
||||
// act
|
||||
val result = generateKontent<List<TestSimpleModel>>()
|
||||
|
||||
// assert
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 4
|
||||
result shouldContainKey "List-TestSimpleModel"
|
||||
}
|
||||
it("Can handle a complex type") {
|
||||
// act
|
||||
val result = generateKontent<ComplexRequest>()
|
||||
|
||||
// assert
|
||||
result shouldNotBe null
|
||||
result shouldHaveSize 7
|
||||
result shouldContainKey "Map-String-CrazyItem"
|
||||
result["Map-String-CrazyItem"] as DictionarySchema shouldNotBe null
|
||||
}
|
||||
}
|
||||
})
|
@ -1,73 +1,39 @@
|
||||
package io.bkbn.kompendium.util
|
||||
package io.bkbn.kompendium.core.util
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import com.fasterxml.jackson.databind.SerializationFeature
|
||||
import io.bkbn.kompendium.Notarized.notarizedDelete
|
||||
import io.bkbn.kompendium.Notarized.notarizedException
|
||||
import io.bkbn.kompendium.Notarized.notarizedGet
|
||||
import io.bkbn.kompendium.Notarized.notarizedPost
|
||||
import io.bkbn.kompendium.Notarized.notarizedPut
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo
|
||||
import io.bkbn.kompendium.models.meta.RequestInfo
|
||||
import io.bkbn.kompendium.models.meta.ResponseInfo
|
||||
import io.bkbn.kompendium.core.Notarized.notarizedDelete
|
||||
import io.bkbn.kompendium.core.Notarized.notarizedGet
|
||||
import io.bkbn.kompendium.core.Notarized.notarizedPost
|
||||
import io.bkbn.kompendium.core.Notarized.notarizedPut
|
||||
import io.bkbn.kompendium.core.fixtures.Bibbity
|
||||
import io.bkbn.kompendium.core.fixtures.ComplexGibbit
|
||||
import io.bkbn.kompendium.core.fixtures.DefaultParameter
|
||||
import io.bkbn.kompendium.core.fixtures.Gibbity
|
||||
import io.bkbn.kompendium.core.fixtures.Mysterious
|
||||
import io.bkbn.kompendium.core.fixtures.SimpleGibbit
|
||||
import io.bkbn.kompendium.core.fixtures.TestFieldOverride
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.DEFAULT_TEST_ENDPOINT
|
||||
import io.bkbn.kompendium.core.fixtures.TestNested
|
||||
import io.bkbn.kompendium.core.fixtures.TestRequest
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponse
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.defaultField
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.defaultParam
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.formattedParam
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.minMaxString
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.nullableField
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.regexString
|
||||
import io.bkbn.kompendium.core.fixtures.TestResponseInfo.requiredParam
|
||||
import io.bkbn.kompendium.core.metadata.RequestInfo
|
||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.GetInfo
|
||||
import io.bkbn.kompendium.core.metadata.method.PostInfo
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.call
|
||||
import io.ktor.application.install
|
||||
import io.ktor.features.ContentNegotiation
|
||||
import io.ktor.features.StatusPages
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.jackson.jackson
|
||||
import io.ktor.response.respond
|
||||
import io.ktor.response.respondText
|
||||
import io.ktor.routing.route
|
||||
import io.ktor.routing.routing
|
||||
import io.ktor.serialization.json
|
||||
|
||||
fun Application.jacksonConfigModule() {
|
||||
install(ContentNegotiation) {
|
||||
jackson {
|
||||
enable(SerializationFeature.INDENT_OUTPUT)
|
||||
setSerializationInclusion(JsonInclude.Include.NON_NULL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.kotlinxConfigModule() {
|
||||
install(ContentNegotiation) {
|
||||
json()
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.statusPageModule() {
|
||||
install(StatusPages) {
|
||||
notarizedException<Exception, ExceptionResponse>(
|
||||
info = ResponseInfo(
|
||||
HttpStatusCode.BadRequest,
|
||||
"Bad Things Happened"
|
||||
)
|
||||
) {
|
||||
call.respond(HttpStatusCode.BadRequest, ExceptionResponse("Why you do dis?"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.statusPageMultiExceptions() {
|
||||
install(StatusPages) {
|
||||
notarizedException<AccessDeniedException, Unit>(
|
||||
info = ResponseInfo(HttpStatusCode.Forbidden, "New API who dis?")
|
||||
) {
|
||||
call.respond(HttpStatusCode.Forbidden)
|
||||
}
|
||||
notarizedException<Exception, ExceptionResponse>(
|
||||
info = ResponseInfo(
|
||||
HttpStatusCode.BadRequest,
|
||||
"Bad Things Happened"
|
||||
)
|
||||
) {
|
||||
call.respond(HttpStatusCode.BadRequest, ExceptionResponse("Why you do dis?"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.notarizedGetWithNotarizedException() {
|
||||
routing {
|
||||
@ -89,6 +55,26 @@ fun Application.notarizedGetWithMultipleThrowables() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.notarizedGetWithPolymorphicErrorResponse() {
|
||||
routing {
|
||||
route(DEFAULT_TEST_ENDPOINT) {
|
||||
notarizedGet(TestResponseInfo.testGetWithPolymorphicException) {
|
||||
error("something terrible has happened!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.notarizedGetWithGenericErrorResponse() {
|
||||
routing {
|
||||
route(DEFAULT_TEST_ENDPOINT) {
|
||||
notarizedGet(TestResponseInfo.testGetWithGenericException) {
|
||||
error("something terrible has happened!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.notarizedGetModule() {
|
||||
routing {
|
||||
route("/test") {
|
||||
@ -213,21 +199,11 @@ fun Application.primitives() {
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.emptyGet() {
|
||||
routing {
|
||||
route("/test/empty") {
|
||||
notarizedGet(TestResponseInfo.trulyEmptyTestGetInfo) {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.withExamples() {
|
||||
routing {
|
||||
route("/test/examples") {
|
||||
notarizedPost(
|
||||
info = MethodInfo.PostInfo<Unit, TestRequest, TestResponse>(
|
||||
info = PostInfo<Unit, TestRequest, TestResponse>(
|
||||
summary = "Example Parameters",
|
||||
description = "A test for setting parameter examples",
|
||||
requestInfo = RequestInfo(
|
||||
@ -254,9 +230,13 @@ fun Application.withDefaultParameter() {
|
||||
routing {
|
||||
route("/test") {
|
||||
notarizedGet(
|
||||
info = MethodInfo.GetInfo<DefaultParameter, TestResponse>(
|
||||
info = GetInfo<DefaultParameter, TestResponse>(
|
||||
summary = "Testing Default Params",
|
||||
description = "Should have a default parameter value"
|
||||
description = "Should have a default parameter value",
|
||||
responseInfo = ResponseInfo(
|
||||
HttpStatusCode.OK,
|
||||
"A good response"
|
||||
)
|
||||
)
|
||||
) {
|
||||
call.respond(TestResponse("hey"))
|
||||
@ -280,7 +260,7 @@ fun Application.withOperationId(){
|
||||
fun Application.nonRequiredParamsGet() {
|
||||
routing {
|
||||
route("/test/optional") {
|
||||
notarizedGet(TestResponseInfo.emptyTestGetInfo) {
|
||||
notarizedGet(TestResponseInfo.testOptionalParams) {
|
||||
call.respond(HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
@ -381,3 +361,173 @@ fun Application.simpleGenericResponse() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.overrideFieldInfo() {
|
||||
routing {
|
||||
route("/test/field_override") {
|
||||
notarizedGet(TestResponseInfo.fieldOverride) {
|
||||
call.respond(HttpStatusCode.OK, TestFieldOverride(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.constrainedIntInfo() {
|
||||
routing {
|
||||
route("/test/constrained_int") {
|
||||
notarizedGet(TestResponseInfo.minMaxInt) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.constrainedDoubleInfo() {
|
||||
routing {
|
||||
route("/test/constrained_int") {
|
||||
notarizedGet(TestResponseInfo.minMaxDouble) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.exclusiveMinMax() {
|
||||
routing {
|
||||
route("/test/constrained_int") {
|
||||
notarizedGet(TestResponseInfo.exclusiveMinMax) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.requiredParameter() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(requiredParam) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.defaultParameter() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(defaultParam) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.defaultField() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedPost(defaultField) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.nullableField() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedPost(nullableField) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.formattedParam() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(formattedParam) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.minMaxString() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(minMaxString) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.regexString() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(regexString) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.minMaxArray() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(TestResponseInfo.minMaxArray) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.uniqueArray() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(TestResponseInfo.uniqueArray) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.multipleOfInt() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(TestResponseInfo.multipleOfInt) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.multipleOfDouble() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(TestResponseInfo.multipleOfDouble) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.freeFormObject() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(TestResponseInfo.freeFormObject) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Application.minMaxFreeForm() {
|
||||
routing {
|
||||
route("/test/required_param") {
|
||||
notarizedGet(TestResponseInfo.minMaxFreeForm) {
|
||||
call.respond(HttpStatusCode.OK, TestResponse("hi"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package io.bkbn.kompendium.util
|
||||
|
||||
import java.io.File
|
||||
|
||||
object TestHelpers {
|
||||
fun getFileSnapshot(fileName: String): String {
|
||||
val snapshotPath = "src/test/resources"
|
||||
val file = File("$snapshotPath/$fileName")
|
||||
return file.readText()
|
||||
}
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
package io.bkbn.kompendium.util
|
||||
|
||||
import java.util.UUID
|
||||
import io.bkbn.kompendium.annotations.KompendiumField
|
||||
import io.bkbn.kompendium.annotations.KompendiumParam
|
||||
import io.bkbn.kompendium.annotations.ParamType
|
||||
import io.bkbn.kompendium.annotations.UndeclaredField
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
|
||||
data class TestSimpleModel(val a: String, val b: Int)
|
||||
|
||||
data class TestBigNumberModel(val a: BigDecimal, val b: BigInteger)
|
||||
|
||||
data class TestByteArrayModel(val a: ByteArray)
|
||||
|
||||
data class TestNestedModel(val inner: TestSimpleModel)
|
||||
|
||||
data class TestSimpleWithEnums(val a: String, val b: SimpleEnum)
|
||||
|
||||
data class TestSimpleWithMap(val a: String, val b: Map<String, TestSimpleModel>)
|
||||
|
||||
data class TestSimpleWithList(val a: Boolean, val b: List<TestSimpleModel>)
|
||||
|
||||
data class TestSimpleWithEnumList(val a: Double, val b: List<SimpleEnum>)
|
||||
|
||||
data class TestInvalidMap(val a: Map<Int, TestSimpleModel>)
|
||||
|
||||
data class TestParams(
|
||||
@KompendiumParam(ParamType.PATH) val a: String,
|
||||
@KompendiumParam(ParamType.QUERY) val aa: Int
|
||||
)
|
||||
|
||||
data class TestNested(val nesty: String)
|
||||
|
||||
data class TestWithUUID(val id: UUID)
|
||||
|
||||
data class TestRequest(
|
||||
@KompendiumField(name = "field_name")
|
||||
val fieldName: TestNested,
|
||||
val b: Double,
|
||||
val aaa: List<Long>
|
||||
)
|
||||
|
||||
data class TestResponse(val c: String)
|
||||
|
||||
data class TestGeneric<T>(val messy: String, val potato: T)
|
||||
|
||||
data class TestCreatedResponse(val id: Int, val c: String)
|
||||
|
||||
data class ComplexRequest(
|
||||
val org: String,
|
||||
@KompendiumField("amazing_field")
|
||||
val amazingField: String,
|
||||
val tables: List<NestedComplexItem>
|
||||
)
|
||||
|
||||
data class NestedComplexItem(
|
||||
val name: String,
|
||||
val alias: CustomAlias
|
||||
)
|
||||
|
||||
typealias CustomAlias = Map<String, CrazyItem>
|
||||
|
||||
data class CrazyItem(val enumeration: SimpleEnum)
|
||||
|
||||
enum class SimpleEnum {
|
||||
ONE,
|
||||
TWO
|
||||
}
|
||||
|
||||
data class DefaultParameter(
|
||||
@KompendiumParam(ParamType.QUERY) val a: Int = 100,
|
||||
@KompendiumParam(ParamType.PATH) val b: String?,
|
||||
@KompendiumParam(ParamType.PATH) val c: Boolean
|
||||
)
|
||||
|
||||
data class ExceptionResponse(val message: String)
|
||||
|
||||
data class OptionalParams(
|
||||
@KompendiumParam(ParamType.QUERY) val required: String,
|
||||
@KompendiumParam(ParamType.QUERY) val notRequired: String?
|
||||
)
|
||||
|
||||
sealed class FlibbityGibbit
|
||||
|
||||
data class SimpleGibbit(val a: String) : FlibbityGibbit()
|
||||
data class ComplexGibbit(val b: String, val c: Int) : FlibbityGibbit()
|
||||
|
||||
sealed interface SlammaJamma
|
||||
|
||||
data class OneJamma(val a: Int) : SlammaJamma
|
||||
data class AnothaJamma(val b: Float) : SlammaJamma
|
||||
//data class InsaneJamma(val c: SlammaJamma) : SlammaJamma // 👀
|
||||
|
||||
sealed interface Flibbity<T>
|
||||
|
||||
data class Gibbity<T>(val a: T) : Flibbity<T>
|
||||
data class Bibbity<T>(val b: String, val f: T) : Flibbity<T>
|
||||
|
||||
enum class Hehe {
|
||||
HAHA,
|
||||
HOHO
|
||||
}
|
||||
|
||||
@UndeclaredField("nowYouDont", Hehe::class)
|
||||
data class Mysterious(val nowYouSeeMe: String)
|
||||
|
||||
data class HeaderNameTest(
|
||||
@KompendiumParam(type = ParamType.HEADER) val `X-UserEmail`: String
|
||||
)
|
@ -1,123 +0,0 @@
|
||||
package io.bkbn.kompendium.util
|
||||
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo.DeleteInfo
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo.GetInfo
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo.PostInfo
|
||||
import io.bkbn.kompendium.models.meta.MethodInfo.PutInfo
|
||||
import io.bkbn.kompendium.models.meta.RequestInfo
|
||||
import io.bkbn.kompendium.models.meta.ResponseInfo
|
||||
import io.ktor.http.HttpStatusCode
|
||||
|
||||
object TestResponseInfo {
|
||||
private val testGetResponse = ResponseInfo<TestResponse>(HttpStatusCode.OK, "A Successful Endeavor")
|
||||
private val testGetListResponse =
|
||||
ResponseInfo<List<TestResponse>>(HttpStatusCode.OK, "A Successful List-y Endeavor")
|
||||
private val testPostResponse = ResponseInfo<TestCreatedResponse>(HttpStatusCode.Created, "A Successful Endeavor")
|
||||
private val testPostResponseAgain = ResponseInfo<Boolean>(HttpStatusCode.Created, "A Successful Endeavor")
|
||||
private val testDeleteResponse =
|
||||
ResponseInfo<Unit>(HttpStatusCode.NoContent, "A Successful Endeavor", mediaTypes = emptyList())
|
||||
private val testRequest = RequestInfo<TestRequest>("A Test request")
|
||||
private val testRequestAgain = RequestInfo<Int>("A Test request")
|
||||
private val complexRequest = RequestInfo<ComplexRequest>("A Complex request")
|
||||
val testGetInfo = GetInfo<TestParams, TestResponse>(
|
||||
summary = "Another get test",
|
||||
description = "testing more",
|
||||
responseInfo = testGetResponse
|
||||
)
|
||||
val testGetInfoAgain = GetInfo<TestParams, List<TestResponse>>(
|
||||
summary = "Another get test",
|
||||
description = "testing more",
|
||||
responseInfo = testGetListResponse
|
||||
)
|
||||
val testGetWithException = testGetInfo.copy(
|
||||
canThrow = setOf(Exception::class)
|
||||
)
|
||||
val testGetWithMultipleExceptions = testGetInfo.copy(
|
||||
canThrow = setOf(AccessDeniedException::class, Exception::class)
|
||||
)
|
||||
val testPostInfo = PostInfo<TestParams, TestRequest, TestCreatedResponse>(
|
||||
summary = "Test post endpoint",
|
||||
description = "Post your tests here!",
|
||||
responseInfo = testPostResponse,
|
||||
requestInfo = testRequest
|
||||
)
|
||||
val testPutInfo = PutInfo<Unit, ComplexRequest, TestCreatedResponse>(
|
||||
summary = "Test put endpoint",
|
||||
description = "Put your tests here!",
|
||||
responseInfo = testPostResponse,
|
||||
requestInfo = complexRequest
|
||||
)
|
||||
val testPutInfoAlso = PutInfo<TestParams, TestRequest, TestCreatedResponse>(
|
||||
summary = "Test put endpoint",
|
||||
description = "Put your tests here!",
|
||||
responseInfo = testPostResponse,
|
||||
requestInfo = testRequest
|
||||
)
|
||||
val testPutInfoAgain = PutInfo<Unit, Int, Boolean>(
|
||||
summary = "Test put endpoint",
|
||||
description = "Put your tests here!",
|
||||
responseInfo = testPostResponseAgain,
|
||||
requestInfo = testRequestAgain
|
||||
)
|
||||
val testDeleteInfo = DeleteInfo<TestParams, Unit>(
|
||||
summary = "Test delete endpoint",
|
||||
description = "testing my deletes",
|
||||
responseInfo = testDeleteResponse
|
||||
)
|
||||
val emptyTestGetInfo =
|
||||
GetInfo<OptionalParams, Unit>(
|
||||
summary = "No request params and response body",
|
||||
description = "testing more"
|
||||
)
|
||||
val trulyEmptyTestGetInfo = GetInfo<Unit, Unit>(
|
||||
summary = "No request params and response body",
|
||||
description = "testing more"
|
||||
)
|
||||
val polymorphicResponse = GetInfo<Unit, FlibbityGibbit>(
|
||||
summary = "All the gibbits",
|
||||
description = "Polymorphic response",
|
||||
responseInfo = simpleOkResponse()
|
||||
)
|
||||
val polymorphicListResponse = GetInfo<Unit, List<FlibbityGibbit>>(
|
||||
summary = "Oh so many gibbits",
|
||||
description = "Polymorphic list response",
|
||||
responseInfo = simpleOkResponse()
|
||||
)
|
||||
val polymorphicMapResponse = GetInfo<Unit, Map<String, FlibbityGibbit>>(
|
||||
summary = "By gawd that's a lot of gibbits",
|
||||
description = "Polymorphic list response",
|
||||
responseInfo = simpleOkResponse()
|
||||
)
|
||||
val polymorphicInterfaceResponse = GetInfo<Unit, SlammaJamma>(
|
||||
summary = "Come on and slam",
|
||||
description = "and welcome to the jam",
|
||||
responseInfo = simpleOkResponse()
|
||||
)
|
||||
val genericPolymorphicResponse = GetInfo<Unit, Flibbity<TestNested>>(
|
||||
summary = "More flibbity",
|
||||
description = "Polymorphic with generics",
|
||||
responseInfo = simpleOkResponse()
|
||||
)
|
||||
val anotherGenericPolymorphicResponse = GetInfo<Unit, Flibbity<FlibbityGibbit>>(
|
||||
summary = "The Most Flibbity",
|
||||
description = "Polymorphic with generics but like... crazier",
|
||||
responseInfo = simpleOkResponse()
|
||||
)
|
||||
val undeclaredResponseType = GetInfo<Unit, Mysterious>(
|
||||
summary = "spooky class",
|
||||
description = "break this glass in scenario of emergency",
|
||||
responseInfo = simpleOkResponse()
|
||||
)
|
||||
val headerParam = GetInfo<HeaderNameTest, TestResponse>(
|
||||
summary = "testing header stuffs",
|
||||
description = "Good for many things",
|
||||
responseInfo = simpleOkResponse()
|
||||
)
|
||||
val genericResponse = GetInfo<Unit, TestGeneric<Int>>(
|
||||
summary = "Single Generic",
|
||||
description = "Simple generic data class",
|
||||
responseInfo = simpleOkResponse()
|
||||
)
|
||||
|
||||
private fun <T> simpleOkResponse() = ResponseInfo<T>(HttpStatusCode.OK, "A successful endeavor")
|
||||
}
|
@ -1,133 +1,126 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"put" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Test put endpoint",
|
||||
"description" : "Put your tests here!",
|
||||
"parameters" : [ ],
|
||||
"requestBody" : {
|
||||
"description" : "A Complex request",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/ComplexRequest"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"put": {
|
||||
"tags": [],
|
||||
"summary": "Test put endpoint",
|
||||
"description": "Put your tests here!",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A Complex request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"amazing_field": {
|
||||
"type": "string"
|
||||
},
|
||||
"org": {
|
||||
"type": "string"
|
||||
},
|
||||
"tables": {
|
||||
"items": {
|
||||
"properties": {
|
||||
"alias": {
|
||||
"additionalProperties": {
|
||||
"properties": {
|
||||
"enumeration": {
|
||||
"enum": [
|
||||
"ONE",
|
||||
"TWO"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enumeration"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"alias"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"org",
|
||||
"amazingField",
|
||||
"tables"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required" : false
|
||||
"required": true
|
||||
},
|
||||
"responses" : {
|
||||
"201" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestCreatedResponse"
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"SimpleEnum" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "ONE", "TWO" ]
|
||||
},
|
||||
"CrazyItem" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"enumeration" : {
|
||||
"$ref" : "#/components/schemas/SimpleEnum"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Map-String-CrazyItem" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : {
|
||||
"$ref" : "#/components/schemas/CrazyItem"
|
||||
}
|
||||
},
|
||||
"NestedComplexItem" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"alias" : {
|
||||
"$ref" : "#/components/schemas/Map-String-CrazyItem"
|
||||
},
|
||||
"name" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"List-NestedComplexItem" : {
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"$ref" : "#/components/schemas/NestedComplexItem"
|
||||
}
|
||||
},
|
||||
"ComplexRequest" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"amazingField" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"org" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"tables" : {
|
||||
"$ref" : "#/components/schemas/List-NestedComplexItem"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"TestCreatedResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"id" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,164 +1,203 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/polymorphic" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "More flibbity",
|
||||
"description" : "Polymorphic with generics",
|
||||
"parameters" : [ ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A successful endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"anyOf" : [ {
|
||||
"$ref" : "#/components/schemas/Gibbity-TestNested"
|
||||
}, {
|
||||
"$ref" : "#/components/schemas/Bibbity-TestNested"
|
||||
} ]
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphic": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "More flibbity",
|
||||
"description": "Polymorphic with generics",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"f": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"/test/also/poly" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "The Most Flibbity",
|
||||
"description" : "Polymorphic with generics but like... crazier",
|
||||
"parameters" : [ ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A successful endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"anyOf" : [ {
|
||||
"$ref" : "#/components/schemas/Gibbity-FlibbityGibbit"
|
||||
}, {
|
||||
"$ref" : "#/components/schemas/Bibbity-FlibbityGibbit"
|
||||
} ]
|
||||
"/test/also/poly": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "The Most Flibbity",
|
||||
"description": "Polymorphic with generics but like... crazier",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"f": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestNested" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"nesty" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Gibbity-TestNested" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"a" : {
|
||||
"$ref" : "#/components/schemas/TestNested"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Bibbity-TestNested" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"f" : {
|
||||
"$ref" : "#/components/schemas/TestNested"
|
||||
}
|
||||
}
|
||||
},
|
||||
"SimpleGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"a" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"ComplexGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Gibbity-FlibbityGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"a" : {
|
||||
"anyOf" : [ {
|
||||
"$ref" : "#/components/schemas/SimpleGibbit"
|
||||
}, {
|
||||
"$ref" : "#/components/schemas/ComplexGibbit"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Bibbity-FlibbityGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"f" : {
|
||||
"anyOf" : [ {
|
||||
"$ref" : "#/components/schemas/SimpleGibbit"
|
||||
}, {
|
||||
"$ref" : "#/components/schemas/ComplexGibbit"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
87
kompendium-core/src/test/resources/default_field.json
Normal file
87
kompendium-core/src/test/resources/default_field.json
Normal file
@ -0,0 +1,87 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "default param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "cool",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
},
|
||||
"b": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
75
kompendium-core/src/test/resources/default_param.json
Normal file
75
kompendium-core/src/test/resources/default_param.json
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "default param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "b",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "heyo"
|
||||
},
|
||||
"required": false,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,77 +1,119 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/examples" : {
|
||||
"post" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Example Parameters",
|
||||
"description" : "A test for setting parameter examples",
|
||||
"parameters" : [ ],
|
||||
"requestBody" : {
|
||||
"description" : "Test",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestRequest"
|
||||
},
|
||||
"examples" : {
|
||||
"one" : {
|
||||
"value" : {
|
||||
"fieldName" : {
|
||||
"nesty" : "hey"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/examples": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "Example Parameters",
|
||||
"description": "A test for setting parameter examples",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "Test",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"aaa": {
|
||||
"items": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"b" : 4.0,
|
||||
"aaa" : [ ]
|
||||
"type": "array"
|
||||
},
|
||||
"b": {
|
||||
"format": "double",
|
||||
"type": "number"
|
||||
},
|
||||
"field_name": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"two" : {
|
||||
"value" : {
|
||||
"fieldName" : {
|
||||
"nesty" : "hello"
|
||||
"required": [
|
||||
"fieldName",
|
||||
"b",
|
||||
"aaa"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"examples": {
|
||||
"one": {
|
||||
"value": {
|
||||
"fieldName": {
|
||||
"nesty": "hey"
|
||||
},
|
||||
"b" : 3.8,
|
||||
"aaa" : [ 31324234 ]
|
||||
"b": 4.0,
|
||||
"aaa": []
|
||||
}
|
||||
},
|
||||
"two": {
|
||||
"value": {
|
||||
"fieldName": {
|
||||
"nesty": "hello"
|
||||
},
|
||||
"b": 3.8,
|
||||
"aaa": [
|
||||
31324234
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required" : false
|
||||
"required": true
|
||||
},
|
||||
"responses" : {
|
||||
"201" : {
|
||||
"description" : "nice",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "nice",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"examples" : {
|
||||
"test" : {
|
||||
"value" : {
|
||||
"c" : "spud"
|
||||
"examples": {
|
||||
"test": {
|
||||
"value": {
|
||||
"c": "spud"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,62 +121,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"Long" : {
|
||||
"type" : "integer",
|
||||
"format" : "int64"
|
||||
},
|
||||
"List-Long" : {
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"$ref" : "#/components/schemas/Long"
|
||||
}
|
||||
},
|
||||
"Double" : {
|
||||
"type" : "number",
|
||||
"format" : "double"
|
||||
},
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestNested" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"nesty" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TestRequest" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"aaa" : {
|
||||
"$ref" : "#/components/schemas/List-Long"
|
||||
},
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/Double"
|
||||
},
|
||||
"fieldName" : {
|
||||
"$ref" : "#/components/schemas/TestNested"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
69
kompendium-core/src/test/resources/exclusive_min_max.json
Normal file
69
kompendium-core/src/test/resources/exclusive_min_max.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/constrained_int": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Constrained int field",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"format": "int32",
|
||||
"type": "integer",
|
||||
"minimum": 5,
|
||||
"maximum": 100,
|
||||
"exclusiveMinimum": true,
|
||||
"exclusiveMaximum": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
65
kompendium-core/src/test/resources/field_override.json
Normal file
65
kompendium-core/src/test/resources/field_override.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/field_override": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "A Response with a spicy field",
|
||||
"description": "Important info within!",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"real_name": {
|
||||
"type": "boolean",
|
||||
"description": "A Field that is super important!"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
75
kompendium-core/src/test/resources/formatted_param_type.json
Normal file
75
kompendium-core/src/test/resources/formatted_param_type.json
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "password"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
65
kompendium-core/src/test/resources/free_form_object.json
Normal file
65
kompendium-core/src/test/resources/free_form_object.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"data": {
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
121
kompendium-core/src/test/resources/generic_exception.json
Normal file
121
kompendium-core/src/test/resources/generic_exception.json
Normal file
@ -0,0 +1,121 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Wow serious things went wrong",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"f": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,73 +1,69 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/polymorphic" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Single Generic",
|
||||
"description" : "Simple generic data class",
|
||||
"parameters" : [ ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A successful endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestGeneric-Int"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphic": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Single Generic",
|
||||
"description": "Simple generic data class",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"messy": {
|
||||
"type": "string"
|
||||
},
|
||||
"potato": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"messy",
|
||||
"potato"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"TestGeneric-Int" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"messy" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"potato" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
69
kompendium-core/src/test/resources/min_max_array.json
Normal file
69
kompendium-core/src/test/resources/min_max_array.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"maxItems": 10,
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
69
kompendium-core/src/test/resources/min_max_double_field.json
Normal file
69
kompendium-core/src/test/resources/min_max_double_field.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/constrained_int": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Constrained int field",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"format": "double",
|
||||
"type": "number",
|
||||
"minimum": 5.5,
|
||||
"maximum": 13.37,
|
||||
"exclusiveMinimum": false,
|
||||
"exclusiveMaximum": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
67
kompendium-core/src/test/resources/min_max_free_form.json
Normal file
67
kompendium-core/src/test/resources/min_max_free_form.json
Normal file
@ -0,0 +1,67 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"data": {
|
||||
"minProperties": 5,
|
||||
"maxProperties": 10,
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
69
kompendium-core/src/test/resources/min_max_int_field.json
Normal file
69
kompendium-core/src/test/resources/min_max_int_field.json
Normal file
@ -0,0 +1,69 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/constrained_int": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Constrained int field",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"format": "int32",
|
||||
"type": "integer",
|
||||
"minimum": 5,
|
||||
"maximum": 100,
|
||||
"exclusiveMinimum": false,
|
||||
"exclusiveMaximum": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
66
kompendium-core/src/test/resources/min_max_string.json
Normal file
66
kompendium-core/src/test/resources/min_max_string.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string",
|
||||
"minLength": 42,
|
||||
"maxLength": 1337
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
66
kompendium-core/src/test/resources/multiple_of_double.json
Normal file
66
kompendium-core/src/test/resources/multiple_of_double.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"format": "double",
|
||||
"type": "number",
|
||||
"multipleOf": 2.5
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
66
kompendium-core/src/test/resources/multiple_of_int.json
Normal file
66
kompendium-core/src/test/resources/multiple_of_int.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"format": "int32",
|
||||
"type": "integer",
|
||||
"multipleOf": 5
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,87 +1,84 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/testerino" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/testerino": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/empty" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "No request params and response body",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ ],
|
||||
"deprecated" : false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : { },
|
||||
"securitySchemes" : { }
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
}
|
@ -1,62 +1,69 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/optional" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "No request params and response body",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "notRequired",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/optional": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "No request params and response body",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "notRequired",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"required": false,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : false,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "required",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"deprecated" : false
|
||||
{
|
||||
"name": "required",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "Empty"
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -40,8 +40,8 @@
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
"format" : "int32",
|
||||
"type" : "integer"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
@ -56,15 +56,6 @@
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
},
|
||||
"security" : [ ],
|
||||
|
@ -1,87 +1,84 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,105 +1,102 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400" : {
|
||||
"description" : "Bad Things Happened",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/ExceptionResponse"
|
||||
"400": {
|
||||
"description": "Bad Things Happened",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"ExceptionResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"message" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,108 +1,120 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403" : {
|
||||
"description" : "New API who dis?"
|
||||
"403": {
|
||||
"description": "Access Denied",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400" : {
|
||||
"description" : "Bad Things Happened",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/ExceptionResponse"
|
||||
"400": {
|
||||
"description": "Bad Things Happened",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"message"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"ExceptionResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"message" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,88 +1,85 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"operationId" : "getTest",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"operationId": "getTest",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,137 +1,129 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"post" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Test post endpoint",
|
||||
"description" : "Post your tests here!",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "Test post endpoint",
|
||||
"description": "Post your tests here!",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"requestBody" : {
|
||||
"description" : "A Test request",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestRequest"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"description": "A Test request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"aaa": {
|
||||
"items": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"b": {
|
||||
"format": "double",
|
||||
"type": "number"
|
||||
},
|
||||
"field_name": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fieldName",
|
||||
"b",
|
||||
"aaa"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required" : false
|
||||
"required": true
|
||||
},
|
||||
"responses" : {
|
||||
"201" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestCreatedResponse"
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"Long" : {
|
||||
"type" : "integer",
|
||||
"format" : "int64"
|
||||
},
|
||||
"List-Long" : {
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"$ref" : "#/components/schemas/Long"
|
||||
}
|
||||
},
|
||||
"Double" : {
|
||||
"type" : "number",
|
||||
"format" : "double"
|
||||
},
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestNested" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"nesty" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TestRequest" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"aaa" : {
|
||||
"$ref" : "#/components/schemas/List-Long"
|
||||
},
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/Double"
|
||||
},
|
||||
"fieldName" : {
|
||||
"$ref" : "#/components/schemas/TestNested"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"TestCreatedResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"id" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,73 +1,68 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"put" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Test put endpoint",
|
||||
"description" : "Put your tests here!",
|
||||
"parameters" : [ ],
|
||||
"requestBody" : {
|
||||
"description" : "A Test request",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"put": {
|
||||
"tags": [],
|
||||
"summary": "Test put endpoint",
|
||||
"description": "Put your tests here!",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "A Test request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required" : false
|
||||
"required": true
|
||||
},
|
||||
"responses" : {
|
||||
"201" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/Boolean"
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"Boolean" : {
|
||||
"type" : "boolean"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,137 +1,129 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"put" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Test put endpoint",
|
||||
"description" : "Put your tests here!",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"put": {
|
||||
"tags": [],
|
||||
"summary": "Test put endpoint",
|
||||
"description": "Put your tests here!",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"requestBody" : {
|
||||
"description" : "A Test request",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestRequest"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"description": "A Test request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"aaa": {
|
||||
"items": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"b": {
|
||||
"format": "double",
|
||||
"type": "number"
|
||||
},
|
||||
"field_name": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fieldName",
|
||||
"b",
|
||||
"aaa"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required" : false
|
||||
"required": true
|
||||
},
|
||||
"responses" : {
|
||||
"201" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestCreatedResponse"
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"Long" : {
|
||||
"type" : "integer",
|
||||
"format" : "int64"
|
||||
},
|
||||
"List-Long" : {
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"$ref" : "#/components/schemas/Long"
|
||||
}
|
||||
},
|
||||
"Double" : {
|
||||
"type" : "number",
|
||||
"format" : "double"
|
||||
},
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestNested" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"nesty" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"TestRequest" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"aaa" : {
|
||||
"$ref" : "#/components/schemas/List-Long"
|
||||
},
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/Double"
|
||||
},
|
||||
"fieldName" : {
|
||||
"$ref" : "#/components/schemas/TestNested"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"TestCreatedResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"id" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
84
kompendium-core/src/test/resources/nullable_field.json
Normal file
84
kompendium-core/src/test/resources/nullable_field.json
Normal file
@ -0,0 +1,84 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"post": {
|
||||
"tags": [],
|
||||
"summary": "default param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"description": "cool",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,74 +1,74 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/with_header" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "testing header stuffs",
|
||||
"description" : "Good for many things",
|
||||
"parameters" : [ {
|
||||
"name" : "X-UserEmail",
|
||||
"in" : "header",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A successful endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/with_header": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "testing header stuffs",
|
||||
"description": "Good for many things",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-UserEmail",
|
||||
"in": "header",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,87 +1,84 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/this/is/a/complex/path/with/an/{id}" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Another get test",
|
||||
"description" : "testing more",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/this/is/a/complex/path/with/an/{id}": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "aa",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A Successful Endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/TestResponse"
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -0,0 +1,122 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Another get test",
|
||||
"description": "testing more",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "aa",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A Successful Endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"501": {
|
||||
"description": "The Gibbits are ANGRY",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
@ -1,91 +1,87 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/polymorphiclist" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Oh so many gibbits",
|
||||
"description" : "Polymorphic list response",
|
||||
"parameters" : [ ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A successful endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/List-FlibbityGibbit"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphiclist": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Oh so many gibbits",
|
||||
"description": "Polymorphic list response",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"SimpleGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"a" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"ComplexGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"List-FlibbityGibbit" : {
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"anyOf" : [ {
|
||||
"$ref" : "#/components/schemas/SimpleGibbit"
|
||||
}, {
|
||||
"$ref" : "#/components/schemas/ComplexGibbit"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,91 +1,87 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/polymorphicmap" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "By gawd that's a lot of gibbits",
|
||||
"description" : "Polymorphic list response",
|
||||
"parameters" : [ ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A successful endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"$ref" : "#/components/schemas/Map-String-FlibbityGibbit"
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphicmap": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "By gawd that's a lot of gibbits",
|
||||
"description": "Polymorphic list response",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"additionalProperties": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"SimpleGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"a" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"ComplexGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Map-String-FlibbityGibbit" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : {
|
||||
"anyOf" : [ {
|
||||
"$ref" : "#/components/schemas/SimpleGibbit"
|
||||
}, {
|
||||
"$ref" : "#/components/schemas/ComplexGibbit"
|
||||
} ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,85 +1,84 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/polymorphic" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "All the gibbits",
|
||||
"description" : "Polymorphic response",
|
||||
"parameters" : [ ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A successful endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"anyOf" : [ {
|
||||
"$ref" : "#/components/schemas/SimpleGibbit"
|
||||
}, {
|
||||
"$ref" : "#/components/schemas/ComplexGibbit"
|
||||
} ]
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphic": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "All the gibbits",
|
||||
"description": "Polymorphic response",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"c": {
|
||||
"format": "int32",
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"SimpleGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"a" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"ComplexGibbit" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/Int"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,89 +1,99 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test/polymorphic" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "More flibbity",
|
||||
"description" : "Polymorphic with generics",
|
||||
"parameters" : [ ],
|
||||
"responses" : {
|
||||
"200" : {
|
||||
"description" : "A successful endeavor",
|
||||
"content" : {
|
||||
"application/json" : {
|
||||
"schema" : {
|
||||
"anyOf" : [ {
|
||||
"$ref" : "#/components/schemas/Gibbity-TestNested"
|
||||
}, {
|
||||
"$ref" : "#/components/schemas/Bibbity-TestNested"
|
||||
} ]
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/polymorphic": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "More flibbity",
|
||||
"description": "Polymorphic with generics",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"properties": {
|
||||
"a": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"b": {
|
||||
"type": "string"
|
||||
},
|
||||
"f": {
|
||||
"properties": {
|
||||
"nesty": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"nesty"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"b",
|
||||
"f"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated" : false
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestNested" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"nesty" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Gibbity-TestNested" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"a" : {
|
||||
"$ref" : "#/components/schemas/TestNested"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Bibbity-TestNested" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"b" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
},
|
||||
"f" : {
|
||||
"$ref" : "#/components/schemas/TestNested"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,87 +1,95 @@
|
||||
{
|
||||
"openapi" : "3.0.3",
|
||||
"info" : {
|
||||
"title" : "Test API",
|
||||
"version" : "1.33.7",
|
||||
"description" : "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService" : "https://example.com",
|
||||
"contact" : {
|
||||
"name" : "Homer Simpson",
|
||||
"url" : "https://gph.is/1NPUDiM",
|
||||
"email" : "chunkylover53@aol.com"
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license" : {
|
||||
"name" : "MIT",
|
||||
"url" : "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers" : [ {
|
||||
"url" : "https://myawesomeapi.com",
|
||||
"description" : "Production instance of my API"
|
||||
}, {
|
||||
"url" : "https://staging.myawesomeapi.com",
|
||||
"description" : "Where the fun stuff happens"
|
||||
} ],
|
||||
"paths" : {
|
||||
"/test" : {
|
||||
"get" : {
|
||||
"tags" : [ ],
|
||||
"summary" : "Testing Default Params",
|
||||
"description" : "Should have a default parameter value",
|
||||
"parameters" : [ {
|
||||
"name" : "a",
|
||||
"in" : "query",
|
||||
"schema" : {
|
||||
"default" : 100,
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "b",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"required" : false,
|
||||
"deprecated" : false
|
||||
}, {
|
||||
"name" : "c",
|
||||
"in" : "path",
|
||||
"schema" : {
|
||||
"type" : "boolean"
|
||||
},
|
||||
"required" : true,
|
||||
"deprecated" : false
|
||||
} ],
|
||||
"deprecated" : false
|
||||
}
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
},
|
||||
"components" : {
|
||||
"schemas" : {
|
||||
"String" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"TestResponse" : {
|
||||
"type" : "object",
|
||||
"properties" : {
|
||||
"c" : {
|
||||
"$ref" : "#/components/schemas/String"
|
||||
],
|
||||
"paths": {
|
||||
"/test": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "Testing Default Params",
|
||||
"description": "Should have a default parameter value",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"format": "int32",
|
||||
"type": "integer",
|
||||
"default": 100
|
||||
},
|
||||
"required": false,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"required": false,
|
||||
"deprecated": false
|
||||
},
|
||||
{
|
||||
"name": "c",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"Int" : {
|
||||
"type" : "integer",
|
||||
"format" : "int32"
|
||||
},
|
||||
"Boolean" : {
|
||||
"type" : "boolean"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A good response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"securitySchemes" : { }
|
||||
}
|
||||
},
|
||||
"security" : [ ],
|
||||
"tags" : [ ]
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test API</title>
|
||||
<title>Docs</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||
|
65
kompendium-core/src/test/resources/regex_string.json
Normal file
65
kompendium-core/src/test/resources/regex_string.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"a": {
|
||||
"type": "string",
|
||||
"pattern": "^\\d{3}-\\d{2}-\\d{4}$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"a"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
74
kompendium-core/src/test/resources/required_param.json
Normal file
74
kompendium-core/src/test/resources/required_param.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "Test API",
|
||||
"version": "1.33.7",
|
||||
"description": "An amazing, fully-ish 😉 generated API spec",
|
||||
"termsOfService": "https://example.com",
|
||||
"contact": {
|
||||
"name": "Homer Simpson",
|
||||
"url": "https://gph.is/1NPUDiM",
|
||||
"email": "chunkylover53@aol.com"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/bkbnio/kompendium/blob/main/LICENSE"
|
||||
}
|
||||
},
|
||||
"servers": [
|
||||
{
|
||||
"url": "https://myawesomeapi.com",
|
||||
"description": "Production instance of my API"
|
||||
},
|
||||
{
|
||||
"url": "https://staging.myawesomeapi.com",
|
||||
"description": "Where the fun stuff happens"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/test/required_param": {
|
||||
"get": {
|
||||
"tags": [],
|
||||
"summary": "required param",
|
||||
"description": "Cool stuff",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "a",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true,
|
||||
"deprecated": false
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful endeavor",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"c": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"c"
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"securitySchemes": {}
|
||||
},
|
||||
"security": [],
|
||||
"tags": []
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user