From f919a6a4b1711c29fcefebcdf89a7f5eae2a7a41 Mon Sep 17 00:00:00 2001 From: Gennadi Kudrjavtsev Date: Wed, 23 Feb 2022 17:24:57 +0200 Subject: [PATCH] fix: (#194) to support Location classes located in other non-location classes (e.g. inside Object) (#207) * Safely stop Location path calculation on first non-location parent * added a test * changelog * added result file and renamed test models Co-authored-by: Gennadi Kudrjavtsev --- CHANGELOG.md | 1 + .../locations/LocationMethodParser.kt | 2 +- .../locations/KompendiumLocationsTest.kt | 8 ++ .../kompendium/locations/util/TestModels.kt | 8 ++ .../kompendium/locations/util/TestModules.kt | 10 +++ .../locations/util/TestResponseInfo.kt | 10 ++- ...sted_location_from_non_location_class.json | 88 +++++++++++++++++++ .../playground/LocationPlayground.kt | 43 +++++---- 8 files changed, 149 insertions(+), 21 deletions(-) create mode 100644 kompendium-locations/src/test/resources/notarized_get_nested_location_from_non_location_class.json diff --git a/CHANGELOG.md b/CHANGELOG.md index bfabc395a..738922301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## Unreleased +- Fixed support Location classes located in other non-location classes ### Added diff --git a/kompendium-locations/src/main/kotlin/io/bkbn/kompendium/locations/LocationMethodParser.kt b/kompendium-locations/src/main/kotlin/io/bkbn/kompendium/locations/LocationMethodParser.kt index 7b9e9cb96..d23f206ec 100644 --- a/kompendium-locations/src/main/kotlin/io/bkbn/kompendium/locations/LocationMethodParser.kt +++ b/kompendium-locations/src/main/kotlin/io/bkbn/kompendium/locations/LocationMethodParser.kt @@ -50,7 +50,7 @@ object LocationMethodParser : IMethodParser { fun KClass<*>.calculateLocationPath(suffix: String = ""): String { val locationAnnotation = this.findAnnotation() require(locationAnnotation != null) { "Location annotation must be present to leverage notarized location api" } - val parent = this.java.declaringClass?.kotlin + val parent = this.java.declaringClass?.kotlin?.takeIf { it.hasAnnotation() } val newSuffix = locationAnnotation.path.plus(suffix) return when (parent) { null -> newSuffix diff --git a/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/KompendiumLocationsTest.kt b/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/KompendiumLocationsTest.kt index cd415c420..c45ad217f 100644 --- a/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/KompendiumLocationsTest.kt +++ b/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/KompendiumLocationsTest.kt @@ -5,6 +5,7 @@ import io.bkbn.kompendium.locations.util.locationsConfig import io.bkbn.kompendium.locations.util.notarizedDeleteNestedLocation import io.bkbn.kompendium.locations.util.notarizedDeleteSimpleLocation import io.bkbn.kompendium.locations.util.notarizedGetNestedLocation +import io.bkbn.kompendium.locations.util.notarizedGetNestedLocationFromNonLocationClass import io.bkbn.kompendium.locations.util.notarizedGetSimpleLocation import io.bkbn.kompendium.locations.util.notarizedPostNestedLocation import io.bkbn.kompendium.locations.util.notarizedPostSimpleLocation @@ -70,5 +71,12 @@ class KompendiumLocationsTest : DescribeSpec({ notarizedDeleteNestedLocation() } } + it("Can notarize a get with a nested location nested in a non-location class") { + // act + openApiTestAllSerializers("notarized_get_nested_location_from_non_location_class.json") { + locationsConfig() + notarizedGetNestedLocationFromNonLocationClass() + } + } } }) diff --git a/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModels.kt b/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModels.kt index e07412131..a0aee790d 100644 --- a/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModels.kt +++ b/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModels.kt @@ -10,5 +10,13 @@ data class SimpleLoc(@Param(ParamType.PATH) val name: String) { data class NestedLoc(@Param(ParamType.QUERY) val isCool: Boolean, val parent: SimpleLoc) } +object NonLocationObject { + @Location("/test/{name}") + data class SimpleLoc(@Param(ParamType.PATH) val name: String) { + @Location("/nesty") + data class NestedLoc(@Param(ParamType.QUERY) val isCool: Boolean, val parent: SimpleLoc) + } +} + data class SimpleResponse(val result: Boolean) data class SimpleRequest(val input: String) diff --git a/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModules.kt b/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModules.kt index b7382f3b7..3345dc126 100644 --- a/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModules.kt +++ b/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestModules.kt @@ -95,3 +95,13 @@ fun Application.notarizedDeleteNestedLocation() { } } } + +fun Application.notarizedGetNestedLocationFromNonLocationClass() { + routing { + route("/test") { + notarizedGet(TestResponseInfo.testGetNestedLocationFromNonLocationClass) { + call.respondText { "hey dude ‼️ congratz on the get request" } + } + } + } +} diff --git a/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestResponseInfo.kt b/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestResponseInfo.kt index a01513fed..652583f2c 100644 --- a/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestResponseInfo.kt +++ b/kompendium-locations/src/test/kotlin/io/bkbn/kompendium/locations/util/TestResponseInfo.kt @@ -1,6 +1,5 @@ package io.bkbn.kompendium.locations.util -import io.bkbn.kompendium.core.metadata.method.MethodInfo import io.bkbn.kompendium.core.metadata.RequestInfo import io.bkbn.kompendium.core.metadata.ResponseInfo import io.bkbn.kompendium.core.metadata.method.DeleteInfo @@ -86,4 +85,13 @@ object TestResponseInfo { description = "A successful endeavor" ) ) + + val testGetNestedLocationFromNonLocationClass = GetInfo( + summary = "Location Test", + description = "A cool test", + responseInfo = ResponseInfo( + status = HttpStatusCode.OK, + description = "A successful endeavor" + ) + ) } diff --git a/kompendium-locations/src/test/resources/notarized_get_nested_location_from_non_location_class.json b/kompendium-locations/src/test/resources/notarized_get_nested_location_from_non_location_class.json new file mode 100644 index 000000000..4a9adabcc --- /dev/null +++ b/kompendium-locations/src/test/resources/notarized_get_nested_location_from_non_location_class.json @@ -0,0 +1,88 @@ +{ + "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/test/{name}/nesty": { + "get": { + "tags": [], + "summary": "Location Test", + "description": "A cool test", + "parameters": [ + { + "name": "isCool", + "in": "query", + "schema": { + "type": "boolean" + }, + "required": true, + "deprecated": false + }, + { + "name": "name", + "in": "path", + "schema": { + "type": "string" + }, + "required": true, + "deprecated": false + } + ], + "responses": { + "200": { + "description": "A successful endeavor", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SimpleResponse" + } + } + } + } + }, + "deprecated": false + } + } + }, + "components": { + "schemas": { + "SimpleResponse": { + "properties": { + "result": { + "type": "boolean" + } + }, + "required": [ + "result" + ], + "type": "object" + } + }, + "securitySchemes": {} + }, + "security": [], + "tags": [] +} diff --git a/kompendium-playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt b/kompendium-playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt index e69337b5c..a2727f9ab 100644 --- a/kompendium-playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt +++ b/kompendium-playground/src/main/kotlin/io/bkbn/kompendium/playground/LocationPlayground.kt @@ -69,7 +69,7 @@ private fun Application.mainModule() { } private object LocationsToC { - val testLocation = GetInfo( + val testLocation = GetInfo( summary = "Shallow", description = "Ez Pz Lemon Squeezy", responseInfo = ResponseInfo( @@ -77,7 +77,7 @@ private object LocationsToC { description = "Great!" ) ) - val testNestLocation = GetInfo( + val testNestLocation = GetInfo( summary = "Nested", description = "Gettin' scary", responseInfo = ResponseInfo( @@ -85,7 +85,7 @@ private object LocationsToC { description = "Hmmm" ) ) - val ohBoiUCrazy = GetInfo( + val ohBoiUCrazy = GetInfo( summary = "Example Deeply Nested", description = "We deep now", responseInfo = ResponseInfo( @@ -99,23 +99,28 @@ private object LocationsToC { // For more info make sure to read through the Ktor location docs // Additionally, make sure to note that even though we define the locations here, we still must annotate fields // with KompendiumParam!!! -@Location("test/{name}") -data class TestLocations( - @Param(ParamType.PATH) - val name: String, -) { - @Location("/spaghetti") - data class NestedTestLocations( - @Param(ParamType.QUERY) - val idk: Int, - val parent: TestLocations +object TestLocationsParent { + + @Location("test/{name}") + data class TestLocations( + @Param(ParamType.PATH) + val name: String, ) { - @Location("/hehe/{madness}") - data class OhBoiUCrazy( - @Param(ParamType.PATH) - val madness: Boolean, - val parent: NestedTestLocations - ) + + @Location("/spaghetti") + data class NestedTestLocations( + @Param(ParamType.QUERY) + val idk: Int, + val parent: TestLocations + ) { + + @Location("/hehe/{madness}") + data class OhBoiUCrazy( + @Param(ParamType.PATH) + val madness: Boolean, + val parent: NestedTestLocations + ) + } } }