From c019f92cc0236dc807d6626edf5237872da01229 Mon Sep 17 00:00:00 2001 From: Ryan Brink <5607577+rgbrizzlehizzle@users.noreply.github.com> Date: Thu, 6 May 2021 09:36:35 -0400 Subject: [PATCH] =?UTF-8?q?V1=20beta=20release=20time=20=F0=9F=A5=B3=20(#4?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 25 ++ CHANGELOG.md | 12 + README.md | 24 +- gradle.properties | 2 +- .../kompendium/auth/KompendiumAuthTest.kt | 4 +- .../org/leafygreens/kompendium/Kompendium.kt | 3 + .../kompendium/KompendiumPreFlight.kt | 17 + .../org/leafygreens/kompendium/Kontent.kt | 42 +++ .../leafygreens/kompendium/MethodParser.kt | 80 +++- .../org/leafygreens/kompendium/Notarized.kt | 42 ++- .../kompendium/models/meta/ResponseInfo.kt | 4 +- .../kompendium/path/CorePathCalculator.kt | 3 + .../kompendium/path/PathCalculator.kt | 9 + .../kompendium/routes/OpenApiRoute.kt | 4 + .../leafygreens/kompendium/routes/Redoc.kt | 10 +- .../kompendium/util/KompendiumHttpCodes.kt | 81 ---- .../leafygreens/kompendium/KompendiumTest.kt | 351 +----------------- .../leafygreens/kompendium/util/TestModels.kt | 19 +- .../kompendium/util/TestModules.kt | 257 +++++++++++++ .../kompendium/util/TestResponseInfo.kt | 71 ++++ .../leafygreens/kompendium/playground/Main.kt | 3 +- .../kompendium/playground/PlaygroundToC.kt | 18 +- 22 files changed, 616 insertions(+), 465 deletions(-) create mode 100644 .github/workflows/release.yml delete mode 100644 kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/KompendiumHttpCodes.kt create mode 100644 kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModules.kt create mode 100644 kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestResponseInfo.kt diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..88fcdd425 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +name: Publish to GitHub Packages +on: + release: + types: + - prereleased + - released +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle.kts') }} + restore-keys: ${{ runner.os }}-gradle + - name: Publish package + run: ./gradlew publish -Prelease=true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 57c9cb2de..9e79f6be4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +### [1.0.0-beta] - May 6th, 2021 + +### Added + +- Release action to package a release JAR 🍻 +- EXTREME DOCUMENTATION 📜 + +### Changed + +- Cleanup to test files +- Removes KompendiumHttpCodes in favor of Ktor HttpStatusCode + ### [0.9.0] - May 5th, 2021 ### Added diff --git a/README.md b/README.md index 77334dfb6..2c4cee17f 100644 --- a/README.md +++ b/README.md @@ -37,16 +37,6 @@ dependencies { ## In depth -### Warning 🚨 -Kompendium is still under active development ⚠️ There are a number of yet-to-be-implemented features, including - -- Sealed Class / Polymorphic Support 😬 -- Validation / Enforcement (❓👀❓) - -If you have a feature that is not listed here, please open an issue! - -In addition, if you find any 🐞😱 please open an issue as well! - ### Notarized Routes Kompendium introduces the concept of `notarized` HTTP methods. That is, for all your `GET`, `POST`, `PUT`, and `DELETE` @@ -204,3 +194,17 @@ it offers a seriously clean UX where the implementer doesn't need to worry about drawback, however, is that you are limited to a single API per classpath. If this is a blocker, please open a GitHub issue, and we can start to think out solutions! + +## Future Work +Work on V1 of Kompendium has come to a close. This, however, does not mean it has achieved complete +parity with the OpenAPI feature spec, nor does it have all-of-the nice to have features that a truly next-gen API spec +should have. There are several outstanding features that have been added to the +[V2 Milestone](https://github.com/lg-backbone/kompendium/milestone/2). Among others, this includes + +- Polymorphic support +- AsyncAPI Integration +- Field Validation +- MavenCentral Release + +If you have a feature that you would like to see implemented that is not on this list, or discover a 🐞, please open +an issue [here](https://github.com/lg-backbone/kompendium/issues/new) diff --git a/gradle.properties b/gradle.properties index 20203f766..a2d9e155d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # Kompendium -project.version=0.9.0 +project.version=1.0.0-beta # Kotlin kotlin.code.style=official # Gradle diff --git a/kompendium-auth/src/test/kotlin/org/leafygreens/kompendium/auth/KompendiumAuthTest.kt b/kompendium-auth/src/test/kotlin/org/leafygreens/kompendium/auth/KompendiumAuthTest.kt index 7c6c4fdee..fc773b994 100644 --- a/kompendium-auth/src/test/kotlin/org/leafygreens/kompendium/auth/KompendiumAuthTest.kt +++ b/kompendium-auth/src/test/kotlin/org/leafygreens/kompendium/auth/KompendiumAuthTest.kt @@ -11,6 +11,7 @@ import io.ktor.auth.authenticate import io.ktor.features.ContentNegotiation import io.ktor.http.ContentType import io.ktor.http.HttpMethod +import io.ktor.http.HttpStatusCode import io.ktor.jackson.jackson import io.ktor.response.respondText import io.ktor.routing.route @@ -31,7 +32,6 @@ import org.leafygreens.kompendium.models.meta.MethodInfo import org.leafygreens.kompendium.models.meta.ResponseInfo import org.leafygreens.kompendium.routes.openApi import org.leafygreens.kompendium.routes.redoc -import org.leafygreens.kompendium.util.KompendiumHttpCodes internal class KompendiumAuthTest { @@ -189,7 +189,7 @@ internal class KompendiumAuthTest { } private companion object { - val testGetResponse = ResponseInfo(KompendiumHttpCodes.OK, "A Successful Endeavor") + val testGetResponse = ResponseInfo(HttpStatusCode.OK, "A Successful Endeavor") fun testGetInfo(vararg security: String) = MethodInfo.GetInfo( summary = "Another get test", diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt index 2ab746974..47c104ae5 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kompendium.kt @@ -7,6 +7,9 @@ import org.leafygreens.kompendium.models.oas.OpenApiSpecInfo import org.leafygreens.kompendium.path.CorePathCalculator import org.leafygreens.kompendium.path.PathCalculator +/** + * Maintains all state for the Kompendium library + */ object Kompendium { var errorMap: ErrorMap = emptyMap() diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/KompendiumPreFlight.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/KompendiumPreFlight.kt index 3ee7c71e5..97a23e5cc 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/KompendiumPreFlight.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/KompendiumPreFlight.kt @@ -4,8 +4,19 @@ 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 methodNotarizationPreFlight( block: (KType, KType, KType) -> Route @@ -20,6 +31,12 @@ object KompendiumPreFlight { 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 errorNotarizationPreFlight( block: (KType, KType) -> Unit diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kontent.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kontent.kt index 8014d6de4..489824fc9 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kontent.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Kontent.kt @@ -21,10 +21,19 @@ import org.leafygreens.kompendium.util.Helpers.getReferenceSlug import org.leafygreens.kompendium.util.Helpers.logged import org.slf4j.LoggerFactory +/** + * 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 generateKontent( cache: SchemaMap = emptyMap() @@ -33,6 +42,12 @@ object Kontent { return generateKTypeKontent(kontentType, cache) } + /** + * 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 generateParameterKontent( cache: SchemaMap = emptyMap() @@ -42,6 +57,11 @@ object Kontent { .filterNot { (slug, _) -> slug == (kontentType.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() @@ -65,6 +85,11 @@ object Kontent { } } + /** + * 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 + */ private fun handleComplexType(clazz: KClass<*>, cache: SchemaMap): SchemaMap = when (cache.containsKey(clazz.simpleName)) { true -> { @@ -92,11 +117,22 @@ object Kontent { } } + /** + * 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 } @@ -112,6 +148,12 @@ object Kontent { 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!! diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/MethodParser.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/MethodParser.kt index 9b2a26667..bc1bbd5e2 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/MethodParser.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/MethodParser.kt @@ -26,7 +26,19 @@ import org.leafygreens.kompendium.util.Helpers import org.leafygreens.kompendium.util.Helpers.getReferenceSlug import org.leafygreens.kompendium.util.Helpers.getSimpleSlug +/** + * The MethodParser is responsible for converting route metadata and types into an OpenAPI compatible data class. + */ object MethodParser { + + /** + * Generates the OpenAPI Path spec from provided metadata + * @param info implementation of the [MethodInfo] sealed class + * @param paramType Type of `TParam` + * @param requestType Type of `TReq` if required + * @param responseType Type of `TResp` + * @return object representing the OpenAPI Path spec. + */ fun parseMethodInfo( info: MethodInfo<*, *>, paramType: KType, @@ -61,19 +73,34 @@ object MethodParser { ) else null ) - private fun parseThrowables(throwables: Set>): Map = throwables.mapNotNull { - Kompendium.errorMap[it.createType()] - }.toMap() - - fun ResponseInfo.parseErrorInfo( + /** + * 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)) } - // TODO These two lookin' real similar 👀 Combine? - private fun KType.toRequestSpec(requestInfo: RequestInfo?): OpenApiSpecRequest? = + /** + * 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>): Map = throwables.mapNotNull { + Kompendium.errorMap[it.createType()] + }.toMap() + + /** + * Converts a [KType] to an [OpenApiSpecRequest] + * @receiver [KType] to convert + * @param requestInfo request metadata + * @return Will return a generated [OpenApiSpecRequest] if requestInfo is not null + */ + private fun KType.toRequestSpec(requestInfo: RequestInfo<*>?): OpenApiSpecRequest<*>? = when (requestInfo) { null -> null else -> { @@ -84,18 +111,31 @@ object MethodParser { } } - private fun KType.toResponseSpec(responseInfo: ResponseInfo?): Pair>? = + /** + * Converts a [KType] to a pairing of http status code to [OpenApiSpecRequest] + * @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>? = when (responseInfo) { - null -> null // TODO again probably revisit this + null -> null else -> { val specResponse = OpenApiSpecResponse( description = responseInfo.description, content = resolveContent(responseInfo.mediaTypes, responseInfo.examples) ) - Pair(responseInfo.status, specResponse) + Pair(responseInfo.status.value, specResponse) } } + /** + * Generates MediaTypes along with any examples provided + * @receiver [KType] Type of the object + * @param mediaTypes list of acceptable http media types + * @param examples Mapping of named examples of valid bodies. + * @return Named mapping of media types. + */ private fun KType.resolveContent( mediaTypes: List, examples: Map @@ -111,6 +151,13 @@ object MethodParser { } else null } + /** + * Parses a type for all parameter information. All fields in the receiver + * must be annotated with [org.leafygreens.kompendium.annotations.KompendiumParam]. + * @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 { val clazz = classifier as KClass<*> return clazz.memberProperties.map { prop -> @@ -131,6 +178,12 @@ object MethodParser { } } + /** + * Absolutely disgusting reflection to determine if a default value is available for a given property. + * @param clazz to which the property belongs + * @param prop the property in question + * @return The default value if found + */ private fun getDefaultParameterValue(clazz: KClass<*>, prop: KProperty<*>): Any? { val constructor = clazz.primaryConstructor val parameterInQuestion = constructor @@ -152,6 +205,12 @@ object MethodParser { return getterFunction.invoke(instance) } + /** + * Allows the reflection invoker to populate a parameter map with values in order to sus out any default parameters. + * @param param Parameter to provide value for + * @return value of the proper type to match param + * @throws [IllegalStateException] if parameter type is not one of the basic types supported below. + */ private fun defaultValueInjector(param: KParameter): Any = when (param.type.classifier) { String::class -> "test" Boolean::class -> false @@ -162,5 +221,4 @@ object MethodParser { UUID::class -> UUID.randomUUID() else -> error("Unsupported Type") } - } diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Notarized.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Notarized.kt index f6b583c1c..c00181327 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Notarized.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/Notarized.kt @@ -17,9 +17,20 @@ import org.leafygreens.kompendium.models.meta.MethodInfo.DeleteInfo import org.leafygreens.kompendium.models.meta.ResponseInfo import org.leafygreens.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 { - @OptIn(ExperimentalStdlibApi::class) + /** + * Notarization for an HTTP GET request + * @param TParam The class containing all parameter fields. + * Each field must be annotated with @[org.leafygreens.kompendium.annotations.KompendiumField] + * @param TResp Class detailing the expected API response + * @param info Route metadata + */ inline fun Route.notarizedGet( info: GetInfo, noinline body: PipelineInterceptor @@ -31,6 +42,14 @@ object Notarized { 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 @[org.leafygreens.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 Route.notarizedPost( info: PostInfo, noinline body: PipelineInterceptor @@ -42,6 +61,14 @@ object Notarized { 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 @[org.leafygreens.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 Route.notarizedPut( info: PutInfo, noinline body: PipelineInterceptor, @@ -54,6 +81,13 @@ object Notarized { 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 @[org.leafygreens.kompendium.annotations.KompendiumField] + * @param TResp Class detailing the expected API response + * @param info Route metadata + */ inline fun Route.notarizedDelete( info: DeleteInfo, noinline body: PipelineInterceptor @@ -65,6 +99,12 @@ object Notarized { 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 StatusPages.Configuration.notarizedException( info: ResponseInfo, noinline handler: suspend PipelineContext.(TErr) -> Unit diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/meta/ResponseInfo.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/meta/ResponseInfo.kt index 2273fef59..0c5c4628d 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/meta/ResponseInfo.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/models/meta/ResponseInfo.kt @@ -1,7 +1,9 @@ package org.leafygreens.kompendium.models.meta +import io.ktor.http.HttpStatusCode + data class ResponseInfo( - val status: Int, + val status: HttpStatusCode, val description: String, val mediaTypes: List = listOf("application/json"), val examples: Map = emptyMap() diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/path/CorePathCalculator.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/path/CorePathCalculator.kt index 6fb65a599..44f19250b 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/path/CorePathCalculator.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/path/CorePathCalculator.kt @@ -7,6 +7,9 @@ import io.ktor.routing.Route import io.ktor.util.InternalAPI import org.slf4j.LoggerFactory +/** + * Default [PathCalculator] meant to be overridden as necessary + */ open class CorePathCalculator : PathCalculator { private val logger = LoggerFactory.getLogger(javaClass) diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/path/PathCalculator.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/path/PathCalculator.kt index 89f6752a3..b3eedb6e8 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/path/PathCalculator.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/path/PathCalculator.kt @@ -2,10 +2,19 @@ package org.leafygreens.kompendium.path import io.ktor.routing.Route +/** + * Extensible interface for calculating Ktor paths + */ interface PathCalculator { + /** + * Core route calculation method + */ fun calculate(route: Route?, tail: String = ""): String + /** + * Used to handle any custom selectors that may be missed by the base route calculation + */ fun handleCustomSelectors(route: Route, tail: String): String } diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/routes/OpenApiRoute.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/routes/OpenApiRoute.kt index bb91eb674..c506569b3 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/routes/OpenApiRoute.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/routes/OpenApiRoute.kt @@ -7,6 +7,10 @@ import io.ktor.routing.get import io.ktor.routing.route import org.leafygreens.kompendium.models.oas.OpenApiSpec +/** + * Provides an out-of-the-box route to return the generated [OpenApiSpec] + * @param oas spec that is returned + */ fun Routing.openApi(oas: OpenApiSpec) { route("/openapi.json") { get { diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/routes/Redoc.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/routes/Redoc.kt index c7bd09fbf..f99eec5c9 100644 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/routes/Redoc.kt +++ b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/routes/Redoc.kt @@ -15,7 +15,12 @@ import kotlinx.html.title import kotlinx.html.unsafe import org.leafygreens.kompendium.models.oas.OpenApiSpec -fun Routing.redoc(oas: OpenApiSpec) { +/** + * Provides an out-of-the-box route to view docs using ReDoc + * @param oas spec to reference + * @param specUrl url to point ReDoc to the OpenAPI json document + */ +fun Routing.redoc(oas: OpenApiSpec, specUrl: String = "/openapi.json") { route("/docs") { get { call.respondHtml { @@ -41,8 +46,7 @@ fun Routing.redoc(oas: OpenApiSpec) { } } body { - // TODO needs to mirror openApi route - unsafe { +"" } + unsafe { +"" } script { src = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js" } diff --git a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/KompendiumHttpCodes.kt b/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/KompendiumHttpCodes.kt deleted file mode 100644 index 640af740e..000000000 --- a/kompendium-core/src/main/kotlin/org/leafygreens/kompendium/util/KompendiumHttpCodes.kt +++ /dev/null @@ -1,81 +0,0 @@ -package org.leafygreens.kompendium.util - -// Take from https://developer.mozilla.org/en-US/docs/Web/HTTP/Status -object KompendiumHttpCodes { - - // Informational responses - const val CONTINUE = 100 - const val SWITCHING_PROTOCOL = 101 - const val PROCESSING = 102 - const val EARLY_HINTS = 103 - - // Successful responses - const val OK = 200 - const val CREATED = 201 - const val ACCEPTED = 202 - const val NON_AUTHORITATIVE_INFORMATION = 203 - const val NO_CONTENT = 204 - const val RESET_CONTENT = 205 - const val PARTIAL_CONTENT = 206 - const val MULTI_STATUS = 207 - const val ALREADY_REPORTED = 208 - const val IM_USED = 226 - - // Redirection messages - const val MULTIPLE_CHOICE = 300 - const val MOVED_PERMANENTLY = 301 - const val FOUND = 302 - const val SEE_OTHER = 303 - const val NOT_MODIFIED = 304 - @Deprecated("Deprecated due to security concerns regarding in-band configuration of a proxy") - const val USE_PROXY = 305 - @Deprecated("This response code is no longer used; it is just reserved.") - const val UNUSED = 306 - const val TEMPORARY_REDIRECT = 307 - const val PERMANENT_REDIRECT = 308 - - // Client Response Errors - const val BAD_REQUEST = 400 - const val UNAUTHORIZED = 401 - const val PAYMENT_REQUIRED = 402 - const val FORBIDDEN = 403 - const val NOT_FOUND = 404 - const val METHOD_NOT_ALLOWED = 405 - const val NOT_ACCEPTABLE = 406 - const val PROXY_AUTHENTICATION_REQUIRED = 407 - const val REQUEST_TIMEOUT = 408 - const val CONFLICT = 409 - const val GONE = 410 - const val LENGTH_REQUIRED = 411 - const val PRECONDITION_FAILED = 412 - const val PAYLOAD_TOO_LARGE = 413 - const val URI_TOO_LONG = 414 - const val UNSUPPORTED_MEDIA_TYPE = 415 - const val RANGE_NOT_SATISFIABLE = 416 - const val EXPECTATION_FAILED = 417 - const val IM_A_TEAPOT = 418 - const val MISDIRECTED_REQUEST = 421 - const val UNPROCESSABLE_ENTITY = 422 - const val LOCKED = 423 - const val FAILED_DEPENDENCY = 424 - const val TOO_EARLY = 425 - const val UPGRADE_REQUIRED = 426 - const val PRECONDITION_REQUIRED = 428 - const val TOO_MANY_REQUESTS = 429 - const val REQUEST_HEADER_FIELDS_TOO_LARGE = 431 - const val UNAVAILABLE_FOR_LEGAL_REASONS = 451 - - // Server Error Responses - const val INTERNAL_SERVER_ERROR = 500 - const val NOT_IMPLEMENTED = 501 - const val BAD_GATEWAY = 502 - const val SERVICE_UNAVAILABLE = 503 - const val GATEWAY_TIMEOUT = 504 - const val HTTP_VERSION_NOT_SUPPORTED = 505 - const val VARIANT_ALSO_NEGOTIATES = 506 - const val INSUFFICIENT_STORAGE = 507 - const val LOOP_DETECTED = 508 - const val NOT_EXTENDED = 510 - const val NETWORK_AUTHENTICATION_REQUIRED = 511 - -} diff --git a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt index aca8622bf..ce4aee157 100644 --- a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt @@ -1,18 +1,8 @@ package org.leafygreens.kompendium -import com.fasterxml.jackson.annotation.JsonInclude -import com.fasterxml.jackson.databind.SerializationFeature 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.HttpMethod 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.server.testing.handleRequest import io.ktor.server.testing.withTestApplication @@ -20,35 +10,33 @@ import java.net.URI import kotlin.test.AfterTest import kotlin.test.Test import kotlin.test.assertEquals -import org.leafygreens.kompendium.Notarized.notarizedDelete -import org.leafygreens.kompendium.Notarized.notarizedException -import org.leafygreens.kompendium.Notarized.notarizedGet -import org.leafygreens.kompendium.Notarized.notarizedPost -import org.leafygreens.kompendium.Notarized.notarizedPut -import org.leafygreens.kompendium.annotations.KompendiumParam -import org.leafygreens.kompendium.annotations.ParamType -import org.leafygreens.kompendium.models.meta.MethodInfo.DeleteInfo -import org.leafygreens.kompendium.models.meta.MethodInfo.GetInfo -import org.leafygreens.kompendium.models.meta.MethodInfo.PostInfo -import org.leafygreens.kompendium.models.meta.MethodInfo.PutInfo -import org.leafygreens.kompendium.models.meta.RequestInfo -import org.leafygreens.kompendium.models.meta.ResponseInfo import org.leafygreens.kompendium.models.oas.OpenApiSpecInfo import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoContact import org.leafygreens.kompendium.models.oas.OpenApiSpecInfoLicense import org.leafygreens.kompendium.models.oas.OpenApiSpecServer import org.leafygreens.kompendium.routes.openApi import org.leafygreens.kompendium.routes.redoc -import org.leafygreens.kompendium.util.ComplexRequest -import org.leafygreens.kompendium.util.DefaultParameter -import org.leafygreens.kompendium.util.ExceptionResponse -import org.leafygreens.kompendium.util.KompendiumHttpCodes -import org.leafygreens.kompendium.util.TestCreatedResponse import org.leafygreens.kompendium.util.TestHelpers.getFileSnapshot -import org.leafygreens.kompendium.util.TestNested -import org.leafygreens.kompendium.util.TestParams -import org.leafygreens.kompendium.util.TestRequest -import org.leafygreens.kompendium.util.TestResponse +import org.leafygreens.kompendium.util.complexType +import org.leafygreens.kompendium.util.configModule +import org.leafygreens.kompendium.util.emptyGet +import org.leafygreens.kompendium.util.nestedUnderRootModule +import org.leafygreens.kompendium.util.nonRequiredParamsGet +import org.leafygreens.kompendium.util.notarizedDeleteModule +import org.leafygreens.kompendium.util.notarizedGetModule +import org.leafygreens.kompendium.util.notarizedGetWithMultipleThrowables +import org.leafygreens.kompendium.util.notarizedGetWithNotarizedException +import org.leafygreens.kompendium.util.notarizedPostModule +import org.leafygreens.kompendium.util.notarizedPutModule +import org.leafygreens.kompendium.util.pathParsingTestModule +import org.leafygreens.kompendium.util.primitives +import org.leafygreens.kompendium.util.returnsList +import org.leafygreens.kompendium.util.rootModule +import org.leafygreens.kompendium.util.statusPageModule +import org.leafygreens.kompendium.util.statusPageMultiExceptions +import org.leafygreens.kompendium.util.trailingSlash +import org.leafygreens.kompendium.util.withDefaultParameter +import org.leafygreens.kompendium.util.withExamples internal class KompendiumTest { @@ -448,305 +436,6 @@ internal class KompendiumTest { } } - private companion object { - val testGetResponse = ResponseInfo(KompendiumHttpCodes.OK, "A Successful Endeavor") - val testGetListResponse = ResponseInfo>(KompendiumHttpCodes.OK, "A Successful List-y Endeavor") - val testPostResponse = ResponseInfo(KompendiumHttpCodes.CREATED, "A Successful Endeavor") - val testPostResponseAgain = ResponseInfo(KompendiumHttpCodes.CREATED, "A Successful Endeavor") - val testDeleteResponse = - ResponseInfo(KompendiumHttpCodes.NO_CONTENT, "A Successful Endeavor", mediaTypes = emptyList()) - val testRequest = RequestInfo("A Test request") - val testRequestAgain = RequestInfo("A Test request") - val complexRequest = RequestInfo("A Complex request") - val testGetInfo = GetInfo( - summary = "Another get test", - description = "testing more", - responseInfo = testGetResponse - ) - val testGetInfoAgain = GetInfo>( - 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( - summary = "Test post endpoint", - description = "Post your tests here!", - responseInfo = testPostResponse, - requestInfo = testRequest - ) - val testPutInfo = PutInfo( - summary = "Test put endpoint", - description = "Put your tests here!", - responseInfo = testPostResponse, - requestInfo = complexRequest - ) - val testPutInfoAlso = PutInfo( - summary = "Test put endpoint", - description = "Put your tests here!", - responseInfo = testPostResponse, - requestInfo = testRequest - ) - val testPutInfoAgain = PutInfo( - summary = "Test put endpoint", - description = "Put your tests here!", - responseInfo = testPostResponseAgain, - requestInfo = testRequestAgain - ) - val testDeleteInfo = DeleteInfo( - summary = "Test delete endpoint", - description = "testing my deletes", - responseInfo = testDeleteResponse - ) - val emptyTestGetInfo = - GetInfo(summary = "No request params and response body", description = "testing more") - val trulyEmptyTestGetInfo = - GetInfo(summary = "No request params and response body", description = "testing more") - } - - private fun Application.configModule() { - install(ContentNegotiation) { - jackson { - enable(SerializationFeature.INDENT_OUTPUT) - setSerializationInclusion(JsonInclude.Include.NON_NULL) - } - } - } - - private fun Application.statusPageModule() { - install(StatusPages) { - notarizedException(info = ResponseInfo(400, "Bad Things Happened")) { - call.respond(HttpStatusCode.BadRequest, ExceptionResponse("Why you do dis?")) - } - } - } - - private fun Application.statusPageMultiExceptions() { - install(StatusPages) { - notarizedException(info = ResponseInfo(403, "New API who dis?")) { - call.respond(HttpStatusCode.Forbidden) - } - notarizedException(info = ResponseInfo(400, "Bad Things Happened")) { - call.respond(HttpStatusCode.BadRequest, ExceptionResponse("Why you do dis?")) - } - } - } - - private fun Application.notarizedGetWithNotarizedException() { - routing { - route("/test") { - notarizedGet(testGetWithException) { - error("something terrible has happened!") - } - } - } - } - - private fun Application.notarizedGetWithMultipleThrowables() { - routing { - route("/test") { - notarizedGet(testGetWithMultipleExceptions) { - error("something terrible has happened!") - } - } - } - } - - private fun Application.notarizedGetModule() { - routing { - route("/test") { - notarizedGet(testGetInfo) { - call.respondText { "hey dude ‼️ congratz on the get request" } - } - } - } - } - - private fun Application.notarizedPostModule() { - routing { - route("/test") { - notarizedPost(testPostInfo) { - call.respondText { "hey dude ✌️ congratz on the post request" } - } - } - } - } - - private fun Application.notarizedDeleteModule() { - routing { - route("/test") { - notarizedDelete(testDeleteInfo) { - call.respond(HttpStatusCode.NoContent) - } - } - } - } - - private fun Application.notarizedPutModule() { - routing { - route("/test") { - notarizedPut(testPutInfoAlso) { - call.respondText { "hey pal 🌝 whatcha doin' here?" } - } - } - } - } - - private fun Application.pathParsingTestModule() { - routing { - route("/this") { - route("/is") { - route("/a") { - route("/complex") { - route("path") { - route("with/an/{id}") { - notarizedGet(testGetInfo) { - call.respondText { "Aww you followed this whole route 🥺" } - } - } - } - } - } - } - } - } - } - - private fun Application.rootModule() { - routing { - route("/") { - notarizedGet(testGetInfo) { - call.respondText { "☎️🏠🌲" } - } - } - } - } - - private fun Application.nestedUnderRootModule() { - routing { - route("/") { - route("/testerino") { - notarizedGet(testGetInfo) { - call.respondText { "🤔🔥" } - } - } - } - } - } - - private fun Application.trailingSlash() { - routing { - route("/test") { - route("/") { - notarizedGet(testGetInfo) { - call.respondText { "🙀👾" } - } - } - } - } - } - - private fun Application.returnsList() { - routing { - route("/test") { - notarizedGet(testGetInfoAgain) { - call.respondText { "hey dude ur doing amazing work!" } - } - } - } - } - - private fun Application.complexType() { - routing { - route("/test") { - notarizedPut(testPutInfo) { - call.respondText { "heya" } - } - } - } - } - - private fun Application.primitives() { - routing { - route("/test") { - notarizedPut(testPutInfoAgain) { - call.respondText { "heya" } - } - } - } - } - - private fun Application.emptyGet() { - routing { - route("/test/empty") { - notarizedGet(trulyEmptyTestGetInfo) { - call.respond(HttpStatusCode.OK) - } - } - } - } - - private fun Application.withExamples() { - routing { - route("/test/examples") { - notarizedPost( - info = PostInfo( - summary = "Example Parameters", - description = "A test for setting parameter examples", - requestInfo = RequestInfo( - description = "Test", - examples = mapOf( - "one" to TestRequest(fieldName = TestNested(nesty = "hey"), b = 4.0, aaa = emptyList()), - "two" to TestRequest(fieldName = TestNested(nesty = "hello"), b = 3.8, aaa = listOf(31324234)) - ) - ), - responseInfo = ResponseInfo( - status = 201, - description = "nice", - examples = mapOf("test" to TestResponse(c = "spud")) - ), - ) - ) { - call.respond(HttpStatusCode.OK) - } - } - } - } - - private fun Application.withDefaultParameter() { - routing { - route("/test") { - notarizedGet( - info = GetInfo( - summary = "Testing Default Params", - description = "Should have a default parameter value" - ) - ) { - call.respond(TestResponse("hey")) - } - } - } - } - - data class OptionalParams( - @KompendiumParam(ParamType.QUERY) val required: String, - @KompendiumParam(ParamType.QUERY) val notRequired: String? - ) - - private fun Application.nonRequiredParamsGet() { - routing { - route("/test/optional") { - notarizedGet(emptyTestGetInfo) { - call.respond(HttpStatusCode.OK) - } - } - } - } private val oas = Kompendium.openApiSpec.copy( info = OpenApiSpecInfo( diff --git a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModels.kt b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModels.kt index 397da0d0e..64340c4c7 100644 --- a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModels.kt +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModels.kt @@ -42,18 +42,12 @@ data class TestResponse(val c: String) data class TestCreatedResponse(val id: Int, val c: String) -object TestDeleteResponse - data class ComplexRequest( val org: String, @KompendiumField("amazing_field") val amazingField: String, val tables: List -) { - fun testThing() { - println("hey mom 👋") - } -} +) data class NestedComplexItem( val name: String, @@ -75,10 +69,9 @@ data class DefaultParameter( @KompendiumParam(ParamType.PATH) val c: Boolean ) -sealed class TestSealedClass(open val a: String) - -data class SimpleTSC(val b: Int) : TestSealedClass("hey") -open class MediumTSC(override val a: String, val b: Int) : TestSealedClass(a) -data class WildTSC(val c: Boolean, val d: String, val e: Int) : MediumTSC(d, e) - data class ExceptionResponse(val message: String) + +data class OptionalParams( + @KompendiumParam(ParamType.QUERY) val required: String, + @KompendiumParam(ParamType.QUERY) val notRequired: String? +) diff --git a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModules.kt b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModules.kt new file mode 100644 index 000000000..82a281ca1 --- /dev/null +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestModules.kt @@ -0,0 +1,257 @@ +package org.leafygreens.kompendium.util + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.databind.SerializationFeature +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 org.leafygreens.kompendium.Notarized.notarizedDelete +import org.leafygreens.kompendium.Notarized.notarizedException +import org.leafygreens.kompendium.Notarized.notarizedGet +import org.leafygreens.kompendium.Notarized.notarizedPost +import org.leafygreens.kompendium.Notarized.notarizedPut +import org.leafygreens.kompendium.models.meta.MethodInfo +import org.leafygreens.kompendium.models.meta.RequestInfo +import org.leafygreens.kompendium.models.meta.ResponseInfo + +fun Application.configModule() { + install(ContentNegotiation) { + jackson { + enable(SerializationFeature.INDENT_OUTPUT) + setSerializationInclusion(JsonInclude.Include.NON_NULL) + } + } +} + +fun Application.statusPageModule() { + install(StatusPages) { + notarizedException(info = ResponseInfo(HttpStatusCode.BadRequest, "Bad Things Happened")) { + call.respond(HttpStatusCode.BadRequest, ExceptionResponse("Why you do dis?")) + } + } +} + +fun Application.statusPageMultiExceptions() { + install(StatusPages) { + notarizedException(info = ResponseInfo(HttpStatusCode.Forbidden, "New API who dis?")) { + call.respond(HttpStatusCode.Forbidden) + } + notarizedException(info = ResponseInfo(HttpStatusCode.BadRequest, "Bad Things Happened")) { + call.respond(HttpStatusCode.BadRequest, ExceptionResponse("Why you do dis?")) + } + } +} + +fun Application.notarizedGetWithNotarizedException() { + routing { + route("/test") { + notarizedGet(TestResponseInfo.testGetWithException) { + error("something terrible has happened!") + } + } + } +} + +fun Application.notarizedGetWithMultipleThrowables() { + routing { + route("/test") { + notarizedGet(TestResponseInfo.testGetWithMultipleExceptions) { + error("something terrible has happened!") + } + } + } +} + +fun Application.notarizedGetModule() { + routing { + route("/test") { + notarizedGet(TestResponseInfo.testGetInfo) { + call.respondText { "hey dude ‼️ congratz on the get request" } + } + } + } +} + +fun Application.notarizedPostModule() { + routing { + route("/test") { + notarizedPost(TestResponseInfo.testPostInfo) { + call.respondText { "hey dude ✌️ congratz on the post request" } + } + } + } +} + +fun Application.notarizedDeleteModule() { + routing { + route("/test") { + notarizedDelete(TestResponseInfo.testDeleteInfo) { + call.respond(HttpStatusCode.NoContent) + } + } + } +} + +fun Application.notarizedPutModule() { + routing { + route("/test") { + notarizedPut(TestResponseInfo.testPutInfoAlso) { + call.respondText { "hey pal 🌝 whatcha doin' here?" } + } + } + } +} + +fun Application.pathParsingTestModule() { + routing { + route("/this") { + route("/is") { + route("/a") { + route("/complex") { + route("path") { + route("with/an/{id}") { + notarizedGet(TestResponseInfo.testGetInfo) { + call.respondText { "Aww you followed this whole route 🥺" } + } + } + } + } + } + } + } + } +} + +fun Application.rootModule() { + routing { + route("/") { + notarizedGet(TestResponseInfo.testGetInfo) { + call.respondText { "☎️🏠🌲" } + } + } + } +} + +fun Application.nestedUnderRootModule() { + routing { + route("/") { + route("/testerino") { + notarizedGet(TestResponseInfo.testGetInfo) { + call.respondText { "🤔🔥" } + } + } + } + } +} + +fun Application.trailingSlash() { + routing { + route("/test") { + route("/") { + notarizedGet(TestResponseInfo.testGetInfo) { + call.respondText { "🙀👾" } + } + } + } + } +} + +fun Application.returnsList() { + routing { + route("/test") { + notarizedGet(TestResponseInfo.testGetInfoAgain) { + call.respondText { "hey dude ur doing amazing work!" } + } + } + } +} + +fun Application.complexType() { + routing { + route("/test") { + notarizedPut(TestResponseInfo.testPutInfo) { + call.respondText { "heya" } + } + } + } +} + +fun Application.primitives() { + routing { + route("/test") { + notarizedPut(TestResponseInfo.testPutInfoAgain) { + call.respondText { "heya" } + } + } + } +} + +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( + summary = "Example Parameters", + description = "A test for setting parameter examples", + requestInfo = RequestInfo( + description = "Test", + examples = mapOf( + "one" to TestRequest(fieldName = TestNested(nesty = "hey"), b = 4.0, aaa = emptyList()), + "two" to TestRequest(fieldName = TestNested(nesty = "hello"), b = 3.8, aaa = listOf(31324234)) + ) + ), + responseInfo = ResponseInfo( + status = HttpStatusCode.Created, + description = "nice", + examples = mapOf("test" to TestResponse(c = "spud")) + ), + ) + ) { + call.respond(HttpStatusCode.OK) + } + } + } +} + +fun Application.withDefaultParameter() { + routing { + route("/test") { + notarizedGet( + info = MethodInfo.GetInfo( + summary = "Testing Default Params", + description = "Should have a default parameter value" + ) + ) { + call.respond(TestResponse("hey")) + } + } + } +} + +fun Application.nonRequiredParamsGet() { + routing { + route("/test/optional") { + notarizedGet(TestResponseInfo.emptyTestGetInfo) { + call.respond(HttpStatusCode.OK) + } + } + } +} diff --git a/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestResponseInfo.kt b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestResponseInfo.kt new file mode 100644 index 000000000..bda7fea8b --- /dev/null +++ b/kompendium-core/src/test/kotlin/org/leafygreens/kompendium/util/TestResponseInfo.kt @@ -0,0 +1,71 @@ +package org.leafygreens.kompendium.util + +import io.ktor.http.HttpStatusCode +import org.leafygreens.kompendium.models.meta.MethodInfo +import org.leafygreens.kompendium.models.meta.RequestInfo +import org.leafygreens.kompendium.models.meta.ResponseInfo + +object TestResponseInfo { + private val testGetResponse = ResponseInfo(HttpStatusCode.OK, "A Successful Endeavor") + private val testGetListResponse = + ResponseInfo>(HttpStatusCode.OK, "A Successful List-y Endeavor") + private val testPostResponse = ResponseInfo(HttpStatusCode.Created, "A Successful Endeavor") + private val testPostResponseAgain = ResponseInfo(HttpStatusCode.Created, "A Successful Endeavor") + private val testDeleteResponse = + ResponseInfo(HttpStatusCode.NoContent, "A Successful Endeavor", mediaTypes = emptyList()) + private val testRequest = RequestInfo("A Test request") + private val testRequestAgain = RequestInfo("A Test request") + private val complexRequest = RequestInfo("A Complex request") + val testGetInfo = MethodInfo.GetInfo( + summary = "Another get test", + description = "testing more", + responseInfo = testGetResponse + ) + val testGetInfoAgain = MethodInfo.GetInfo>( + 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 = MethodInfo.PostInfo( + summary = "Test post endpoint", + description = "Post your tests here!", + responseInfo = testPostResponse, + requestInfo = testRequest + ) + val testPutInfo = MethodInfo.PutInfo( + summary = "Test put endpoint", + description = "Put your tests here!", + responseInfo = testPostResponse, + requestInfo = complexRequest + ) + val testPutInfoAlso = MethodInfo.PutInfo( + summary = "Test put endpoint", + description = "Put your tests here!", + responseInfo = testPostResponse, + requestInfo = testRequest + ) + val testPutInfoAgain = MethodInfo.PutInfo( + summary = "Test put endpoint", + description = "Put your tests here!", + responseInfo = testPostResponseAgain, + requestInfo = testRequestAgain + ) + val testDeleteInfo = MethodInfo.DeleteInfo( + summary = "Test delete endpoint", + description = "testing my deletes", + responseInfo = testDeleteResponse + ) + val emptyTestGetInfo = + MethodInfo.GetInfo( + summary = "No request params and response body", + description = "testing more" + ) + val trulyEmptyTestGetInfo = + MethodInfo.GetInfo(summary = "No request params and response body", description = "testing more") +} diff --git a/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/Main.kt b/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/Main.kt index 2c520be11..6d09b450e 100644 --- a/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/Main.kt +++ b/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/Main.kt @@ -38,7 +38,6 @@ import org.leafygreens.kompendium.playground.PlaygroundToC.testSinglePutInfo import org.leafygreens.kompendium.routes.openApi import org.leafygreens.kompendium.routes.redoc import org.leafygreens.kompendium.swagger.swaggerUI -import org.leafygreens.kompendium.util.KompendiumHttpCodes fun main() { embeddedServer( @@ -74,7 +73,7 @@ fun Application.configModule() { install(StatusPages) { notarizedException( info = ResponseInfo( - KompendiumHttpCodes.BAD_REQUEST, + HttpStatusCode.BadRequest, "Bad Things Happened", examples = mapOf("example" to ExceptionResponse("hey bad things happened sorry")) ) diff --git a/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/PlaygroundToC.kt b/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/PlaygroundToC.kt index dc6fa21c6..b8e621c9e 100644 --- a/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/PlaygroundToC.kt +++ b/kompendium-playground/src/main/kotlin/org/leafygreens/kompendium/playground/PlaygroundToC.kt @@ -1,16 +1,16 @@ package org.leafygreens.kompendium.playground +import io.ktor.http.HttpStatusCode import org.leafygreens.kompendium.models.meta.MethodInfo import org.leafygreens.kompendium.models.meta.RequestInfo import org.leafygreens.kompendium.models.meta.ResponseInfo -import org.leafygreens.kompendium.util.KompendiumHttpCodes object PlaygroundToC { val testGetWithExamples = MethodInfo.GetInfo( summary = "Example Parameters", description = "A test for setting parameter examples", responseInfo = ResponseInfo( - status = 200, + status = HttpStatusCode.OK, description = "nice", examples = mapOf("test" to ExampleResponse(c = "spud")) ), @@ -27,7 +27,7 @@ object PlaygroundToC { ) ), responseInfo = ResponseInfo( - status = KompendiumHttpCodes.CREATED, + status = HttpStatusCode.Created, description = "Congratz you hit da endpoint", examples = mapOf( "Expect This" to ExampleResponse(c = "Hi"), @@ -42,7 +42,7 @@ object PlaygroundToC { description = "Test for the getting", tags = setOf("test", "sample", "get"), responseInfo = ResponseInfo( - status = KompendiumHttpCodes.OK, + status = HttpStatusCode.OK, description = "Returns sample info" ) ) @@ -51,7 +51,7 @@ object PlaygroundToC { description = "testing more", tags = setOf("anotherTest", "sample"), responseInfo = ResponseInfo( - status = KompendiumHttpCodes.OK, + status = HttpStatusCode.OK, description = "Returns a different sample" ) ) @@ -66,7 +66,7 @@ object PlaygroundToC { description = "Simple request body" ), responseInfo = ResponseInfo( - status = KompendiumHttpCodes.CREATED, + status = HttpStatusCode.Created, description = "Worlds most complex response" ) ) @@ -77,7 +77,7 @@ object PlaygroundToC { description = "Info needed to perform this put request" ), responseInfo = ResponseInfo( - status = KompendiumHttpCodes.CREATED, + status = HttpStatusCode.Created, description = "What we give you when u do the puts" ) ) @@ -85,7 +85,7 @@ object PlaygroundToC { summary = "Test delete endpoint", description = "testing my deletes", responseInfo = ResponseInfo( - status = KompendiumHttpCodes.NO_CONTENT, + status = HttpStatusCode.NoContent, description = "Signifies that your item was deleted successfully", mediaTypes = emptyList() ) @@ -95,7 +95,7 @@ object PlaygroundToC { description = "testing more", tags = setOf("anotherTest", "sample"), responseInfo = ResponseInfo( - status = KompendiumHttpCodes.OK, + status = HttpStatusCode.OK, description = "Returns a different sample" ), securitySchemes = setOf("basic")