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 <gennadi@agrello.org>
This commit is contained in:

committed by
GitHub

parent
019b2cf4db
commit
f919a6a4b1
@ -1,6 +1,7 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
- Fixed support Location classes located in other non-location classes
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ object LocationMethodParser : IMethodParser {
|
|||||||
fun KClass<*>.calculateLocationPath(suffix: String = ""): String {
|
fun KClass<*>.calculateLocationPath(suffix: String = ""): String {
|
||||||
val locationAnnotation = this.findAnnotation<Location>()
|
val locationAnnotation = this.findAnnotation<Location>()
|
||||||
require(locationAnnotation != null) { "Location annotation must be present to leverage notarized location api" }
|
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<Location>() }
|
||||||
val newSuffix = locationAnnotation.path.plus(suffix)
|
val newSuffix = locationAnnotation.path.plus(suffix)
|
||||||
return when (parent) {
|
return when (parent) {
|
||||||
null -> newSuffix
|
null -> newSuffix
|
||||||
|
@ -5,6 +5,7 @@ import io.bkbn.kompendium.locations.util.locationsConfig
|
|||||||
import io.bkbn.kompendium.locations.util.notarizedDeleteNestedLocation
|
import io.bkbn.kompendium.locations.util.notarizedDeleteNestedLocation
|
||||||
import io.bkbn.kompendium.locations.util.notarizedDeleteSimpleLocation
|
import io.bkbn.kompendium.locations.util.notarizedDeleteSimpleLocation
|
||||||
import io.bkbn.kompendium.locations.util.notarizedGetNestedLocation
|
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.notarizedGetSimpleLocation
|
||||||
import io.bkbn.kompendium.locations.util.notarizedPostNestedLocation
|
import io.bkbn.kompendium.locations.util.notarizedPostNestedLocation
|
||||||
import io.bkbn.kompendium.locations.util.notarizedPostSimpleLocation
|
import io.bkbn.kompendium.locations.util.notarizedPostSimpleLocation
|
||||||
@ -70,5 +71,12 @@ class KompendiumLocationsTest : DescribeSpec({
|
|||||||
notarizedDeleteNestedLocation()
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -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)
|
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 SimpleResponse(val result: Boolean)
|
||||||
data class SimpleRequest(val input: String)
|
data class SimpleRequest(val input: String)
|
||||||
|
@ -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" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package io.bkbn.kompendium.locations.util
|
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.RequestInfo
|
||||||
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
import io.bkbn.kompendium.core.metadata.ResponseInfo
|
||||||
import io.bkbn.kompendium.core.metadata.method.DeleteInfo
|
import io.bkbn.kompendium.core.metadata.method.DeleteInfo
|
||||||
@ -86,4 +85,13 @@ object TestResponseInfo {
|
|||||||
description = "A successful endeavor"
|
description = "A successful endeavor"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val testGetNestedLocationFromNonLocationClass = GetInfo<NonLocationObject.SimpleLoc.NestedLoc, SimpleResponse>(
|
||||||
|
summary = "Location Test",
|
||||||
|
description = "A cool test",
|
||||||
|
responseInfo = ResponseInfo(
|
||||||
|
status = HttpStatusCode.OK,
|
||||||
|
description = "A successful endeavor"
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -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": []
|
||||||
|
}
|
@ -69,7 +69,7 @@ private fun Application.mainModule() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private object LocationsToC {
|
private object LocationsToC {
|
||||||
val testLocation = GetInfo<TestLocations, LocationModels.ExampleResponse>(
|
val testLocation = GetInfo<TestLocationsParent.TestLocations, LocationModels.ExampleResponse>(
|
||||||
summary = "Shallow",
|
summary = "Shallow",
|
||||||
description = "Ez Pz Lemon Squeezy",
|
description = "Ez Pz Lemon Squeezy",
|
||||||
responseInfo = ResponseInfo(
|
responseInfo = ResponseInfo(
|
||||||
@ -77,7 +77,7 @@ private object LocationsToC {
|
|||||||
description = "Great!"
|
description = "Great!"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val testNestLocation = GetInfo<TestLocations.NestedTestLocations, LocationModels.ExampleResponse>(
|
val testNestLocation = GetInfo<TestLocationsParent.TestLocations.NestedTestLocations, LocationModels.ExampleResponse>(
|
||||||
summary = "Nested",
|
summary = "Nested",
|
||||||
description = "Gettin' scary",
|
description = "Gettin' scary",
|
||||||
responseInfo = ResponseInfo(
|
responseInfo = ResponseInfo(
|
||||||
@ -85,7 +85,7 @@ private object LocationsToC {
|
|||||||
description = "Hmmm"
|
description = "Hmmm"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val ohBoiUCrazy = GetInfo<TestLocations.NestedTestLocations.OhBoiUCrazy, LocationModels.ExampleResponse>(
|
val ohBoiUCrazy = GetInfo<TestLocationsParent.TestLocations.NestedTestLocations.OhBoiUCrazy, LocationModels.ExampleResponse>(
|
||||||
summary = "Example Deeply Nested",
|
summary = "Example Deeply Nested",
|
||||||
description = "We deep now",
|
description = "We deep now",
|
||||||
responseInfo = ResponseInfo(
|
responseInfo = ResponseInfo(
|
||||||
@ -99,23 +99,28 @@ private object LocationsToC {
|
|||||||
// For more info make sure to read through the Ktor location docs
|
// 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
|
// Additionally, make sure to note that even though we define the locations here, we still must annotate fields
|
||||||
// with KompendiumParam!!!
|
// with KompendiumParam!!!
|
||||||
@Location("test/{name}")
|
object TestLocationsParent {
|
||||||
data class TestLocations(
|
|
||||||
@Param(ParamType.PATH)
|
@Location("test/{name}")
|
||||||
val name: String,
|
data class TestLocations(
|
||||||
) {
|
@Param(ParamType.PATH)
|
||||||
@Location("/spaghetti")
|
val name: String,
|
||||||
data class NestedTestLocations(
|
|
||||||
@Param(ParamType.QUERY)
|
|
||||||
val idk: Int,
|
|
||||||
val parent: TestLocations
|
|
||||||
) {
|
) {
|
||||||
@Location("/hehe/{madness}")
|
|
||||||
data class OhBoiUCrazy(
|
@Location("/spaghetti")
|
||||||
@Param(ParamType.PATH)
|
data class NestedTestLocations(
|
||||||
val madness: Boolean,
|
@Param(ParamType.QUERY)
|
||||||
val parent: NestedTestLocations
|
val idk: Int,
|
||||||
)
|
val parent: TestLocations
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Location("/hehe/{madness}")
|
||||||
|
data class OhBoiUCrazy(
|
||||||
|
@Param(ParamType.PATH)
|
||||||
|
val madness: Boolean,
|
||||||
|
val parent: NestedTestLocations
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user