chore: test all serializers (#180)
This commit is contained in:
@ -6,6 +6,8 @@
|
||||
|
||||
### Changed
|
||||
- Cleaned up and broke out handlers into separate classes
|
||||
- Serializer cleanup
|
||||
- Tests now run against Jackson, Gson and kotlinx on every run
|
||||
|
||||
### Remove
|
||||
|
||||
|
@ -8,7 +8,7 @@ import io.bkbn.kompendium.auth.util.configBasicAuth
|
||||
import io.bkbn.kompendium.auth.util.configJwtAuth
|
||||
import io.bkbn.kompendium.auth.util.notarizedAuthRoute
|
||||
import io.bkbn.kompendium.auth.util.setupOauth
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTest
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||
import io.bkbn.kompendium.oas.security.OAuth
|
||||
import io.kotest.core.spec.style.DescribeSpec
|
||||
|
||||
@ -21,7 +21,7 @@ class KompendiumAuthTest : DescribeSpec({
|
||||
}
|
||||
|
||||
// act
|
||||
openApiTest("notarized_basic_authenticated_get.json") {
|
||||
openApiTestAllSerializers("notarized_basic_authenticated_get.json") {
|
||||
configBasicAuth()
|
||||
notarizedAuthRoute(authConfig)
|
||||
}
|
||||
@ -35,7 +35,7 @@ class KompendiumAuthTest : DescribeSpec({
|
||||
}
|
||||
|
||||
// act
|
||||
openApiTest("notarized_jwt_authenticated_get.json") {
|
||||
openApiTestAllSerializers("notarized_jwt_authenticated_get.json") {
|
||||
configJwtAuth()
|
||||
notarizedAuthRoute(authConfig)
|
||||
}
|
||||
@ -60,7 +60,7 @@ class KompendiumAuthTest : DescribeSpec({
|
||||
}
|
||||
|
||||
// act
|
||||
openApiTest("notarized_oauth_all_flows.json") {
|
||||
openApiTestAllSerializers("notarized_oauth_all_flows.json") {
|
||||
setupOauth()
|
||||
notarizedAuthRoute(authConfig)
|
||||
}
|
||||
|
@ -21,6 +21,14 @@ The downside is that issues could exist in serialization frameworks that have no
|
||||
Gson and KotlinX serialization have all been tested. If you run into any serialization issues, particularly with a
|
||||
serializer not listed above, please open an issue on GitHub 🙏
|
||||
|
||||
Note for Kotlinx ⚠️
|
||||
|
||||
You will need to include the `SerializersModule` provided in `KompendiumSerializersModule` in order to serialize
|
||||
any provided defaults. This comes down to how Kotlinx expects users to handle serializing `Any`. Essentially, this
|
||||
serializer module will convert any `Any` serialization to be `Contextual`. This is pretty hacky, but seemed to be the
|
||||
only way to get Kotlinx to play nice with serializing `Any`. If you come up with a better solution, definitely go ahead
|
||||
and open up a PR!
|
||||
|
||||
## Notarization
|
||||
|
||||
Central to Kompendium is the concept of notarization.
|
||||
|
@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization")
|
||||
id("io.bkbn.sourdough.library.jvm")
|
||||
id("io.gitlab.arturbosch.detekt")
|
||||
id("com.adarshr.test-logger")
|
||||
@ -40,6 +41,7 @@ dependencies {
|
||||
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-gson", version = ktorVersion)
|
||||
testFixturesApi(group = "io.ktor", name = "ktor-serialization", version = ktorVersion)
|
||||
|
||||
testFixturesApi(group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.3.2")
|
||||
|
@ -2,7 +2,7 @@ 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.fixtures.TestHelpers.openApiTestAllSerializers
|
||||
import io.bkbn.kompendium.core.util.complexType
|
||||
import io.bkbn.kompendium.core.util.constrainedDoubleInfo
|
||||
import io.bkbn.kompendium.core.util.constrainedIntInfo
|
||||
@ -60,37 +60,37 @@ import io.ktor.http.HttpStatusCode
|
||||
class KompendiumTest : DescribeSpec({
|
||||
describe("Notarized Open API Metadata Tests") {
|
||||
it("Can notarize a get request") {
|
||||
openApiTest("notarized_get.json") { notarizedGetModule() }
|
||||
openApiTestAllSerializers("notarized_get.json") { notarizedGetModule() }
|
||||
}
|
||||
it("Can notarize a post request") {
|
||||
openApiTest("notarized_post.json") { notarizedPostModule() }
|
||||
openApiTestAllSerializers("notarized_post.json") { notarizedPostModule() }
|
||||
}
|
||||
it("Can notarize a put request") {
|
||||
openApiTest("notarized_put.json") { notarizedPutModule() }
|
||||
openApiTestAllSerializers("notarized_put.json") { notarizedPutModule() }
|
||||
}
|
||||
it("Can notarize a delete request") {
|
||||
openApiTest("notarized_delete.json") { notarizedDeleteModule() }
|
||||
openApiTestAllSerializers("notarized_delete.json") { notarizedDeleteModule() }
|
||||
}
|
||||
it("Can notarize a patch request") {
|
||||
openApiTest("notarized_patch.json") { notarizedPatchModule() }
|
||||
openApiTestAllSerializers("notarized_patch.json") { notarizedPatchModule() }
|
||||
}
|
||||
it("Can notarize a head request") {
|
||||
openApiTest("notarized_head.json") { notarizedHeadModule() }
|
||||
openApiTestAllSerializers("notarized_head.json") { notarizedHeadModule() }
|
||||
}
|
||||
it("Can notarize an options request") {
|
||||
openApiTest("notarized_options.json") { notarizedOptionsModule() }
|
||||
openApiTestAllSerializers("notarized_options.json") { notarizedOptionsModule() }
|
||||
}
|
||||
it("Can notarize a complex type") {
|
||||
openApiTest("complex_type.json") { complexType() }
|
||||
openApiTestAllSerializers("complex_type.json") { complexType() }
|
||||
}
|
||||
it("Can notarize primitives") {
|
||||
openApiTest("notarized_primitives.json") { primitives() }
|
||||
openApiTestAllSerializers("notarized_primitives.json") { primitives() }
|
||||
}
|
||||
it("Can notarize a top level list response") {
|
||||
openApiTest("response_list.json") { returnsList() }
|
||||
openApiTestAllSerializers("response_list.json") { returnsList() }
|
||||
}
|
||||
it("Can notarize a route with non-required params") {
|
||||
openApiTest("non_required_params.json") { nonRequiredParamsGet() }
|
||||
openApiTestAllSerializers("non_required_params.json") { nonRequiredParamsGet() }
|
||||
}
|
||||
}
|
||||
describe("Notarized Ktor Functionality Tests") {
|
||||
@ -122,80 +122,80 @@ class KompendiumTest : DescribeSpec({
|
||||
}
|
||||
describe("Route Parsing") {
|
||||
it("Can parse a simple path and store it under the expected route") {
|
||||
openApiTest("path_parser.json") { pathParsingTestModule() }
|
||||
openApiTestAllSerializers("path_parser.json") { pathParsingTestModule() }
|
||||
}
|
||||
it("Can notarize the root route") {
|
||||
openApiTest("root_route.json") { rootModule() }
|
||||
openApiTestAllSerializers("root_route.json") { rootModule() }
|
||||
}
|
||||
it("Can notarize a route under the root module without appending trailing slash") {
|
||||
openApiTest("nested_under_root.json") { nestedUnderRootModule() }
|
||||
openApiTestAllSerializers("nested_under_root.json") { nestedUnderRootModule() }
|
||||
}
|
||||
it("Can notarize a route with a trailing slash") {
|
||||
openApiTest("trailing_slash.json") { trailingSlash() }
|
||||
openApiTestAllSerializers("trailing_slash.json") { trailingSlash() }
|
||||
}
|
||||
}
|
||||
describe("Exceptions") {
|
||||
it("Can add an exception status code to a response") {
|
||||
openApiTest("notarized_get_with_exception_response.json") { notarizedGetWithNotarizedException() }
|
||||
openApiTestAllSerializers("notarized_get_with_exception_response.json") { notarizedGetWithNotarizedException() }
|
||||
}
|
||||
it("Can support multiple response codes") {
|
||||
openApiTest("notarized_get_with_multiple_exception_responses.json") { notarizedGetWithMultipleThrowables() }
|
||||
openApiTestAllSerializers("notarized_get_with_multiple_exception_responses.json") { notarizedGetWithMultipleThrowables() }
|
||||
}
|
||||
it("Can add a polymorphic exception response") {
|
||||
openApiTest("polymorphic_error_status_codes.json") { notarizedGetWithPolymorphicErrorResponse() }
|
||||
openApiTestAllSerializers("polymorphic_error_status_codes.json") { notarizedGetWithPolymorphicErrorResponse() }
|
||||
}
|
||||
it("Can add a generic exception response") {
|
||||
openApiTest("generic_exception.json") { notarizedGetWithGenericErrorResponse() }
|
||||
openApiTestAllSerializers("generic_exception.json") { notarizedGetWithGenericErrorResponse() }
|
||||
}
|
||||
}
|
||||
describe("Examples") {
|
||||
it("Can generate example response and request bodies") {
|
||||
openApiTest("example_req_and_resp.json") { withExamples() }
|
||||
openApiTestAllSerializers("example_req_and_resp.json") { withExamples() }
|
||||
}
|
||||
it("Can describe example parameters") {
|
||||
openApiTest("example_parameters.json") { exampleParams() }
|
||||
openApiTestAllSerializers("example_parameters.json") { exampleParams() }
|
||||
}
|
||||
}
|
||||
describe("Defaults") {
|
||||
it("Can generate a default parameter values") {
|
||||
openApiTest("query_with_default_parameter.json") { withDefaultParameter() }
|
||||
openApiTestAllSerializers("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() }
|
||||
openApiTestAllSerializers("required_param.json") { requiredParameter() }
|
||||
}
|
||||
it("Does not mark a parameter as required if a default value is provided") {
|
||||
openApiTest("default_param.json") { defaultParameter() }
|
||||
openApiTestAllSerializers("default_param.json") { defaultParameter() }
|
||||
}
|
||||
it("Does not mark a field as required if a default value is provided") {
|
||||
openApiTest("default_field.json") { defaultField() }
|
||||
openApiTestAllSerializers("default_field.json") { defaultField() }
|
||||
}
|
||||
it("Marks a field as nullable when expected") {
|
||||
openApiTest("nullable_field.json") { nullableField() }
|
||||
openApiTestAllSerializers("nullable_field.json") { nullableField() }
|
||||
}
|
||||
}
|
||||
describe("Polymorphism and Generics") {
|
||||
it("can generate a polymorphic response type") {
|
||||
openApiTest("polymorphic_response.json") { polymorphicResponse() }
|
||||
openApiTestAllSerializers("polymorphic_response.json") { polymorphicResponse() }
|
||||
}
|
||||
it("Can generate a collection with polymorphic response type") {
|
||||
openApiTest("polymorphic_list_response.json") { polymorphicCollectionResponse() }
|
||||
openApiTestAllSerializers("polymorphic_list_response.json") { polymorphicCollectionResponse() }
|
||||
}
|
||||
it("Can generate a map with a polymorphic response type") {
|
||||
openApiTest("polymorphic_map_response.json") { polymorphicMapResponse() }
|
||||
openApiTestAllSerializers("polymorphic_map_response.json") { polymorphicMapResponse() }
|
||||
}
|
||||
it("Can generate a polymorphic response from a sealed interface") {
|
||||
openApiTest("sealed_interface_response.json") { polymorphicInterfaceResponse() }
|
||||
openApiTestAllSerializers("sealed_interface_response.json") { polymorphicInterfaceResponse() }
|
||||
}
|
||||
it("Can generate a response type with a generic type") {
|
||||
openApiTest("generic_response.json") { simpleGenericResponse() }
|
||||
openApiTestAllSerializers("generic_response.json") { simpleGenericResponse() }
|
||||
}
|
||||
it("Can generate a polymorphic response type with generics") {
|
||||
openApiTest("polymorphic_response_with_generics.json") { genericPolymorphicResponse() }
|
||||
openApiTestAllSerializers("polymorphic_response_with_generics.json") { genericPolymorphicResponse() }
|
||||
}
|
||||
it("Can handle an absolutely psycho inheritance test") {
|
||||
openApiTest("crazy_polymorphic_example.json") { genericPolymorphicResponseMultipleImpls() }
|
||||
openApiTestAllSerializers("crazy_polymorphic_example.json") { genericPolymorphicResponseMultipleImpls() }
|
||||
}
|
||||
}
|
||||
describe("Miscellaneous") {
|
||||
@ -203,59 +203,59 @@ class KompendiumTest : DescribeSpec({
|
||||
apiFunctionalityTest(getFileSnapshot("redoc.html"), "/docs") { returnsList() }
|
||||
}
|
||||
it("Can add an operation id to a notarized route") {
|
||||
openApiTest("notarized_get_with_operation_id.json") { withOperationId() }
|
||||
openApiTestAllSerializers("notarized_get_with_operation_id.json") { withOperationId() }
|
||||
}
|
||||
it("Can add an undeclared field") {
|
||||
openApiTest("undeclared_field.json") { undeclaredType() }
|
||||
openApiTestAllSerializers("undeclared_field.json") { undeclaredType() }
|
||||
}
|
||||
it("Can add a custom header parameter with a name override") {
|
||||
openApiTest("override_parameter_name.json") { headerParameter() }
|
||||
openApiTestAllSerializers("override_parameter_name.json") { headerParameter() }
|
||||
}
|
||||
it("Can override field values via annotation") {
|
||||
openApiTest("field_override.json") { overrideFieldInfo() }
|
||||
openApiTestAllSerializers("field_override.json") { overrideFieldInfo() }
|
||||
}
|
||||
it("Can serialize a recursive type using references") {
|
||||
openApiTest("simple_recursive.json") { simpleRecursive() }
|
||||
openApiTestAllSerializers("simple_recursive.json") { simpleRecursive() }
|
||||
}
|
||||
}
|
||||
describe("Constraints") {
|
||||
it("Can set a minimum and maximum integer value") {
|
||||
openApiTest("min_max_int_field.json") { constrainedIntInfo() }
|
||||
openApiTestAllSerializers("min_max_int_field.json") { constrainedIntInfo() }
|
||||
}
|
||||
it("Can set a minimum and maximum double value") {
|
||||
openApiTest("min_max_double_field.json") { constrainedDoubleInfo() }
|
||||
openApiTestAllSerializers("min_max_double_field.json") { constrainedDoubleInfo() }
|
||||
}
|
||||
it("Can set an exclusive min and exclusive max integer value") {
|
||||
openApiTest("exclusive_min_max.json") { exclusiveMinMax() }
|
||||
openApiTestAllSerializers("exclusive_min_max.json") { exclusiveMinMax() }
|
||||
}
|
||||
it("Can add a custom format to a string field") {
|
||||
openApiTest("formatted_param_type.json") { formattedParam() }
|
||||
openApiTestAllSerializers("formatted_param_type.json") { formattedParam() }
|
||||
}
|
||||
it("Can set a minimum and maximum length on a string field") {
|
||||
openApiTest("min_max_string.json") { minMaxString() }
|
||||
openApiTestAllSerializers("min_max_string.json") { minMaxString() }
|
||||
}
|
||||
it("Can set a custom regex pattern on a string field") {
|
||||
openApiTest("regex_string.json") { regexString() }
|
||||
openApiTestAllSerializers("regex_string.json") { regexString() }
|
||||
}
|
||||
it("Can set a minimum and maximum item count on an array field") {
|
||||
openApiTest("min_max_array.json") { minMaxArray() }
|
||||
openApiTestAllSerializers("min_max_array.json") { minMaxArray() }
|
||||
}
|
||||
it("Can set a unique items constraint on an array field") {
|
||||
openApiTest("unique_array.json") { uniqueArray() }
|
||||
openApiTestAllSerializers("unique_array.json") { uniqueArray() }
|
||||
}
|
||||
it("Can set a multiple-of constraint on an int field") {
|
||||
openApiTest("multiple_of_int.json") { multipleOfInt() }
|
||||
openApiTestAllSerializers("multiple_of_int.json") { multipleOfInt() }
|
||||
}
|
||||
it("Can set a multiple of constraint on an double field") {
|
||||
openApiTest("multiple_of_double.json") { multipleOfDouble() }
|
||||
openApiTestAllSerializers("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() }
|
||||
openApiTestAllSerializers("min_max_free_form.json") { minMaxFreeForm() }
|
||||
}
|
||||
}
|
||||
describe("Free Form") {
|
||||
it("Can create a free-form field") {
|
||||
openApiTest("free_form_object.json") { freeFormObject() }
|
||||
openApiTestAllSerializers("free_form_object.json") { freeFormObject() }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -0,0 +1,7 @@
|
||||
package io.bkbn.kompendium.core.fixtures
|
||||
|
||||
enum class SupportedSerializer {
|
||||
KOTLINX,
|
||||
GSON,
|
||||
JACKSON
|
||||
}
|
@ -1,16 +1,22 @@
|
||||
package io.bkbn.kompendium.core.fixtures
|
||||
|
||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.kotest.assertions.ktor.shouldHaveStatus
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.ktor.application.Application
|
||||
import io.ktor.application.install
|
||||
import io.ktor.features.ContentNegotiation
|
||||
import io.ktor.gson.gson
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.serialization.json
|
||||
import io.ktor.server.testing.TestApplicationEngine
|
||||
import io.ktor.server.testing.createTestEnvironment
|
||||
import io.ktor.server.testing.handleRequest
|
||||
import io.ktor.server.testing.withApplication
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
|
||||
object TestHelpers {
|
||||
@ -42,16 +48,44 @@ object TestHelpers {
|
||||
/**
|
||||
* This will take a provided JSON snapshot file, retrieve it from the resource folder,
|
||||
* and build a test ktor server to compare the expected output with the output found in the default
|
||||
* OpenAPI json endpoint
|
||||
* OpenAPI json endpoint. By default, this will run the same test with Gson, Kotlinx, and Jackson serializers
|
||||
* @param snapshotName The snapshot file to retrieve from the resources folder
|
||||
* @param moduleFunction Initializer for the application to allow tests to pass the required Ktor modules
|
||||
*/
|
||||
fun openApiTest(snapshotName: String, moduleFunction: Application.() -> Unit) {
|
||||
fun openApiTestAllSerializers(snapshotName: String, moduleFunction: Application.() -> Unit) {
|
||||
openApiTest(snapshotName, SupportedSerializer.KOTLINX, moduleFunction)
|
||||
openApiTest(snapshotName, SupportedSerializer.JACKSON, moduleFunction)
|
||||
openApiTest(snapshotName, SupportedSerializer.GSON, moduleFunction)
|
||||
}
|
||||
|
||||
private fun openApiTest(
|
||||
snapshotName: String,
|
||||
serializer: SupportedSerializer,
|
||||
moduleFunction: Application.() -> Unit
|
||||
) {
|
||||
withApplication(createTestEnvironment()) {
|
||||
moduleFunction(application.apply {
|
||||
kompendium()
|
||||
docs()
|
||||
when (serializer) {
|
||||
SupportedSerializer.KOTLINX -> {
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
encodeDefaults = true
|
||||
explicitNulls = false
|
||||
serializersModule = KompendiumSerializersModule.module
|
||||
})
|
||||
}
|
||||
}
|
||||
SupportedSerializer.GSON -> {
|
||||
install(ContentNegotiation) {
|
||||
gson()
|
||||
}
|
||||
}
|
||||
SupportedSerializer.JACKSON -> {
|
||||
jacksonConfigModule()
|
||||
}
|
||||
}
|
||||
})
|
||||
compareOpenAPISpec(snapshotName)
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ 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 kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
@ -46,10 +47,12 @@ data class TestParams(
|
||||
@Param(ParamType.QUERY) val aa: Int
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TestNested(val nesty: String)
|
||||
|
||||
data class TestWithUUID(val id: UUID)
|
||||
|
||||
@Serializable
|
||||
data class TestRequest(
|
||||
@Field(name = "field_name")
|
||||
val fieldName: TestNested,
|
||||
@ -57,6 +60,7 @@ data class TestRequest(
|
||||
val aaa: List<Long>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class TestResponse(val c: String)
|
||||
|
||||
data class TestGeneric<T>(val messy: String, val potato: T)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.bkbn.kompendium.locations
|
||||
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTest
|
||||
import io.bkbn.kompendium.core.fixtures.TestHelpers.openApiTestAllSerializers
|
||||
import io.bkbn.kompendium.locations.util.locationsConfig
|
||||
import io.bkbn.kompendium.locations.util.notarizedDeleteNestedLocation
|
||||
import io.bkbn.kompendium.locations.util.notarizedDeleteSimpleLocation
|
||||
@ -16,56 +16,56 @@ class KompendiumLocationsTest : DescribeSpec({
|
||||
describe("Locations") {
|
||||
it("Can notarize a get request with a simple location") {
|
||||
// act
|
||||
openApiTest("notarized_get_simple_location.json") {
|
||||
openApiTestAllSerializers("notarized_get_simple_location.json") {
|
||||
locationsConfig()
|
||||
notarizedGetSimpleLocation()
|
||||
}
|
||||
}
|
||||
it("Can notarize a get request with a nested location") {
|
||||
// act
|
||||
openApiTest("notarized_get_nested_location.json") {
|
||||
openApiTestAllSerializers("notarized_get_nested_location.json") {
|
||||
locationsConfig()
|
||||
notarizedGetNestedLocation()
|
||||
}
|
||||
}
|
||||
it("Can notarize a post with a simple location") {
|
||||
// act
|
||||
openApiTest("notarized_post_simple_location.json") {
|
||||
openApiTestAllSerializers("notarized_post_simple_location.json") {
|
||||
locationsConfig()
|
||||
notarizedPostSimpleLocation()
|
||||
}
|
||||
}
|
||||
it("Can notarize a post with a nested location") {
|
||||
// act
|
||||
openApiTest("notarized_post_nested_location.json") {
|
||||
openApiTestAllSerializers("notarized_post_nested_location.json") {
|
||||
locationsConfig()
|
||||
notarizedPostNestedLocation()
|
||||
}
|
||||
}
|
||||
it("Can notarize a put with a simple location") {
|
||||
// act
|
||||
openApiTest("notarized_put_simple_location.json") {
|
||||
openApiTestAllSerializers("notarized_put_simple_location.json") {
|
||||
locationsConfig()
|
||||
notarizedPutSimpleLocation()
|
||||
}
|
||||
}
|
||||
it("Can notarize a put with a nested location") {
|
||||
// act
|
||||
openApiTest("notarized_put_nested_location.json") {
|
||||
openApiTestAllSerializers("notarized_put_nested_location.json") {
|
||||
locationsConfig()
|
||||
notarizedPutNestedLocation()
|
||||
}
|
||||
}
|
||||
it("Can notarize a delete with a simple location") {
|
||||
// act
|
||||
openApiTest("notarized_delete_simple_location.json") {
|
||||
openApiTestAllSerializers("notarized_delete_simple_location.json") {
|
||||
locationsConfig()
|
||||
notarizedDeleteSimpleLocation()
|
||||
}
|
||||
}
|
||||
it("Can notarize a delete with a nested location") {
|
||||
// act
|
||||
openApiTest("notarized_delete_nested_location.json") {
|
||||
openApiTestAllSerializers("notarized_delete_nested_location.json") {
|
||||
locationsConfig()
|
||||
notarizedDeleteNestedLocation()
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ sourdough {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(group = "org.jetbrains.kotlinx", "kotlinx-serialization-json", version = "1.3.1")
|
||||
implementation(group = "org.jetbrains.kotlinx", "kotlinx-serialization-json", version = "1.3.2")
|
||||
}
|
||||
|
||||
testing {
|
||||
|
@ -1,10 +1,9 @@
|
||||
package io.bkbn.kompendium.oas.schema
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.JsonClassDiscriminator
|
||||
import io.bkbn.kompendium.oas.serialization.ComponentSchemaSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonClassDiscriminator("component_type") // todo figure out a way to filter this
|
||||
@Serializable(with = ComponentSchemaSerializer::class)
|
||||
sealed interface ComponentSchema {
|
||||
val description: String?
|
||||
get() = null
|
||||
|
@ -1,8 +1,7 @@
|
||||
package io.bkbn.kompendium.oas.security
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.JsonClassDiscriminator
|
||||
import io.bkbn.kompendium.oas.serialization.SecuritySchemaSerializer
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@JsonClassDiscriminator("schema_type") // todo figure out a way to filter this
|
||||
@Serializable(with = SecuritySchemaSerializer::class)
|
||||
sealed interface SecuritySchema
|
||||
|
@ -3,6 +3,8 @@ package io.bkbn.kompendium.oas.serialization
|
||||
import kotlin.reflect.KClass
|
||||
import kotlinx.serialization.InternalSerializationApi
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
@ -17,8 +19,7 @@ class AnySerializer<T : Any> : KSerializer<T> {
|
||||
error("Abandon all hope ye who enter 💀")
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor
|
||||
get() = TODO("Not yet implemented")
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("KompendiumAny", PrimitiveKind.STRING)
|
||||
|
||||
@OptIn(InternalSerializationApi::class)
|
||||
fun serialize(encoder: Encoder, obj: T, clazz: KClass<T>) {
|
||||
|
@ -0,0 +1,45 @@
|
||||
package io.bkbn.kompendium.oas.serialization
|
||||
|
||||
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.ReferencedSchema
|
||||
import io.bkbn.kompendium.oas.schema.SimpleSchema
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Serializer(forClass = ComponentSchema::class)
|
||||
object ComponentSchemaSerializer : KSerializer<ComponentSchema> {
|
||||
override fun deserialize(decoder: Decoder): ComponentSchema {
|
||||
error("Abandon all hope ye who enter 💀")
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ComponentSchema", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: ComponentSchema) {
|
||||
when (value) {
|
||||
is AnyOfSchema -> AnyOfSchema.serializer().serialize(encoder, value)
|
||||
is ReferencedSchema -> ReferencedSchema.serializer().serialize(encoder, value)
|
||||
is ArraySchema -> ArraySchema.serializer().serialize(encoder, value)
|
||||
is DictionarySchema -> DictionarySchema.serializer().serialize(encoder, value)
|
||||
is EnumSchema -> EnumSchema.serializer().serialize(encoder, value)
|
||||
is FormattedSchema -> FormattedSchema.serializer().serialize(encoder, value)
|
||||
is FreeFormSchema -> FreeFormSchema.serializer().serialize(encoder, value)
|
||||
is ObjectSchema -> ObjectSchema.serializer().serialize(encoder, value)
|
||||
is SimpleSchema -> SimpleSchema.serializer().serialize(encoder, value)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +1,9 @@
|
||||
package io.bkbn.kompendium.oas.serialization
|
||||
|
||||
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.ReferencedSchema
|
||||
import io.bkbn.kompendium.oas.schema.SimpleSchema
|
||||
import io.bkbn.kompendium.oas.security.ApiKeyAuth
|
||||
import io.bkbn.kompendium.oas.security.BasicAuth
|
||||
import io.bkbn.kompendium.oas.security.BearerAuth
|
||||
import io.bkbn.kompendium.oas.security.OAuth
|
||||
import io.bkbn.kompendium.oas.security.SecuritySchema
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
import kotlinx.serialization.modules.polymorphic
|
||||
|
||||
object KompendiumSerializersModule {
|
||||
|
||||
val module = SerializersModule {
|
||||
polymorphic(ComponentSchema::class) {
|
||||
subclass(SimpleSchema::class, SimpleSchema.serializer())
|
||||
subclass(FormattedSchema::class, FormattedSchema.serializer())
|
||||
subclass(ObjectSchema::class, ObjectSchema.serializer())
|
||||
subclass(AnyOfSchema::class, AnyOfSchema.serializer())
|
||||
subclass(ArraySchema::class, ArraySchema.serializer())
|
||||
subclass(DictionarySchema::class, DictionarySchema.serializer())
|
||||
subclass(EnumSchema::class, EnumSchema.serializer())
|
||||
subclass(FreeFormSchema::class, FreeFormSchema.serializer())
|
||||
subclass(ReferencedSchema::class, ReferencedSchema.serializer())
|
||||
}
|
||||
polymorphic(SecuritySchema::class) {
|
||||
subclass(ApiKeyAuth::class, ApiKeyAuth.serializer())
|
||||
subclass(BasicAuth::class, BasicAuth.serializer())
|
||||
subclass(BearerAuth::class, BearerAuth.serializer())
|
||||
subclass(OAuth::class, OAuth.serializer())
|
||||
}
|
||||
contextual(Any::class, AnySerializer())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,10 @@ object NumberSerializer : KSerializer<Number> {
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Number", PrimitiveKind.DOUBLE)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: Number) {
|
||||
encoder.encodeString(value.toString())
|
||||
when (value) {
|
||||
is Int -> encoder.encodeInt(value)
|
||||
is Double -> encoder.encodeDouble(value)
|
||||
else -> error("Invalid OpenApi Number $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package io.bkbn.kompendium.oas.serialization
|
||||
|
||||
import io.bkbn.kompendium.oas.security.ApiKeyAuth
|
||||
import io.bkbn.kompendium.oas.security.BasicAuth
|
||||
import io.bkbn.kompendium.oas.security.BearerAuth
|
||||
import io.bkbn.kompendium.oas.security.OAuth
|
||||
import io.bkbn.kompendium.oas.security.SecuritySchema
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.Serializer
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Serializer(forClass = SecuritySchema::class)
|
||||
object SecuritySchemaSerializer : KSerializer<SecuritySchema> {
|
||||
override fun deserialize(decoder: Decoder): SecuritySchema {
|
||||
error("Abandon all hope ye who enter 💀")
|
||||
}
|
||||
|
||||
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("SecuritySchema", PrimitiveKind.STRING)
|
||||
|
||||
override fun serialize(encoder: Encoder, value: SecuritySchema) {
|
||||
when (value) {
|
||||
is ApiKeyAuth -> ApiKeyAuth.serializer().serialize(encoder, value)
|
||||
is BasicAuth -> BasicAuth.serializer().serialize(encoder, value)
|
||||
is BearerAuth -> BearerAuth.serializer().serialize(encoder, value)
|
||||
is OAuth -> OAuth.serializer().serialize(encoder, value)
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ dependencies {
|
||||
implementation("org.slf4j:slf4j-simple:1.7.35")
|
||||
|
||||
|
||||
implementation(group = "org.jetbrains.kotlinx", "kotlinx-serialization-json", version = "1.3.1")
|
||||
implementation(group = "org.jetbrains.kotlinx", "kotlinx-serialization-json", version = "1.3.2")
|
||||
|
||||
implementation(group = "joda-time", name = "joda-time", version = "2.10.13")
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ fun main() {
|
||||
// Application Module
|
||||
private fun Application.mainModule() {
|
||||
install(ContentNegotiation) {
|
||||
json(json = Util.kotlinxConfig)
|
||||
json()
|
||||
}
|
||||
install(Kompendium) {
|
||||
spec = Util.baseSpec
|
||||
|
@ -15,7 +15,6 @@ 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.routes.redoc
|
||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||
import io.bkbn.kompendium.playground.BasicModels.BasicParameters
|
||||
import io.bkbn.kompendium.playground.BasicModels.BasicRequest
|
||||
import io.bkbn.kompendium.playground.BasicModels.BasicResponse
|
||||
@ -38,7 +37,6 @@ import io.ktor.server.engine.embeddedServer
|
||||
import io.ktor.server.netty.Netty
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
@ -57,7 +55,7 @@ fun main() {
|
||||
private fun Application.mainModule() {
|
||||
// Installs Simple JSON Content Negotiation
|
||||
install(ContentNegotiation) {
|
||||
json(json = Util.kotlinxConfig)
|
||||
json()
|
||||
}
|
||||
// Installs the Kompendium Plugin and sets up baseline server metadata
|
||||
install(Kompendium) {
|
||||
@ -76,7 +74,7 @@ private fun Application.mainModule() {
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
}
|
||||
// It can also infer path parameters
|
||||
route("/{a}") {
|
||||
route("/testerino/{a}") {
|
||||
notarizedGet(simpleGetExampleWithParameters) {
|
||||
val a = call.parameters["a"] ?: error("Unable to read expected path parameter")
|
||||
val b = call.request.queryParameters["b"]?.toInt() ?: error("Unable to read expected query parameter")
|
||||
|
@ -54,7 +54,7 @@ fun main() {
|
||||
private fun Application.mainModule() {
|
||||
// Installs Simple JSON Content Negotiation
|
||||
install(ContentNegotiation) {
|
||||
json(json = Util.kotlinxConfig)
|
||||
json()
|
||||
}
|
||||
// Installs the Kompendium Plugin and sets up baseline server metadata
|
||||
install(Kompendium) {
|
||||
|
@ -36,7 +36,7 @@ fun main() {
|
||||
private fun Application.mainModule() {
|
||||
// Installs Simple JSON Content Negotiation
|
||||
install(ContentNegotiation) {
|
||||
json(json = Util.kotlinxConfig)
|
||||
json()
|
||||
}
|
||||
// Installs the Kompendium Plugin and sets up baseline server metadata
|
||||
install(Kompendium) {
|
||||
|
@ -35,7 +35,7 @@ fun main() {
|
||||
private fun Application.mainModule() {
|
||||
// Installs Simple JSON Content Negotiation
|
||||
install(ContentNegotiation) {
|
||||
json(json = Util.kotlinxConfig)
|
||||
json()
|
||||
}
|
||||
// Installs the Kompendium Plugin and sets up baseline server metadata
|
||||
install(Kompendium) {
|
||||
|
@ -39,7 +39,7 @@ fun main() {
|
||||
|
||||
private fun Application.mainModule() {
|
||||
install(ContentNegotiation) {
|
||||
json(json = Util.kotlinxConfig)
|
||||
json()
|
||||
}
|
||||
install(Kompendium) {
|
||||
spec = Util.baseSpec
|
||||
|
@ -34,7 +34,7 @@ fun main() {
|
||||
private fun Application.mainModule() {
|
||||
// Installs Simple JSON Content Negotiation
|
||||
install(ContentNegotiation) {
|
||||
json(json = Util.kotlinxConfig)
|
||||
json()
|
||||
}
|
||||
// Installs the Kompendium Plugin and sets up baseline server metadata
|
||||
install(Kompendium) {
|
||||
|
@ -58,7 +58,7 @@ fun main() {
|
||||
|
||||
private fun Application.mainModule() {
|
||||
install(ContentNegotiation) {
|
||||
json(json = Util.kotlinxConfig)
|
||||
json()
|
||||
}
|
||||
install(Kompendium) {
|
||||
spec = Util.baseSpec
|
||||
|
@ -33,7 +33,7 @@ fun main() {
|
||||
private fun Application.mainModule() {
|
||||
// Installs Simple JSON Content Negotiation
|
||||
install(ContentNegotiation) {
|
||||
json(json = Util.kotlinxConfig)
|
||||
json()
|
||||
}
|
||||
install(Webjars)
|
||||
// Installs the Kompendium Plugin and sets up baseline server metadata
|
||||
|
@ -4,22 +4,12 @@ import io.bkbn.kompendium.oas.OpenApiSpec
|
||||
import io.bkbn.kompendium.oas.info.Contact
|
||||
import io.bkbn.kompendium.oas.info.Info
|
||||
import io.bkbn.kompendium.oas.info.License
|
||||
import io.bkbn.kompendium.oas.serialization.KompendiumSerializersModule
|
||||
import io.bkbn.kompendium.oas.server.Server
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.net.URI
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
object Util {
|
||||
val kotlinxConfig = Json {
|
||||
classDiscriminator = "class"
|
||||
serializersModule = KompendiumSerializersModule.module
|
||||
prettyPrint = true
|
||||
explicitNulls = false
|
||||
encodeDefaults = true
|
||||
}
|
||||
|
||||
val baseSpec = OpenApiSpec(
|
||||
info = Info(
|
||||
title = "Simple Demo API",
|
||||
|
Reference in New Issue
Block a user