Skip to content

Commit 63488e8

Browse files
authored
Merge pull request #3 from papsign/transformers-validators
Created Transformers and validators for easy constraint resolution.
2 parents b6a1c36 + db21d04 commit 63488e8

File tree

15 files changed

+196
-34
lines changed

15 files changed

+196
-34
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
kotlin.code.style=official
22
ktor_version=1.2.3
33
logback_version=1.2.1
4-
kotlin_version=1.3.30
4+
kotlin_version=1.3.50

src/main/kotlin/com/papsign/ktor/openapigen/OpenAPIGen.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.papsign.ktor.openapigen
22

3-
import com.papsign.ktor.openapigen.annotations.encodings.APIFormatter
4-
import com.papsign.ktor.openapigen.content.type.ContentTypeProvider
53
import com.papsign.ktor.openapigen.modules.CachingModuleProvider
64
import com.papsign.ktor.openapigen.modules.schema.*
75
import com.papsign.ktor.openapigen.openapi.ExternalDocumentation
@@ -20,6 +18,7 @@ class OpenAPIGen(
2018
private val config: Configuration,
2119
@Deprecated("Will be replaced with less dangerous alternative when the use case has been fleshed out.") val pipeline: ApplicationCallPipeline
2220
) {
21+
private val log = classLogger()
2322

2423
val api = config.api
2524

@@ -37,13 +36,13 @@ class OpenAPIGen(
3736
val globalModuleProvider = CachingModuleProvider()
3837

3938
init {
40-
val reflections = Reflections(javaClass.`package`.name)
41-
val classes = reflections.getTypesAnnotatedWith(APIFormatter::class.java).mapNotNull { it.kotlin.objectInstance }
42-
classes.forEach {
43-
when (it) {
44-
is ContentTypeProvider -> {
45-
globalModuleProvider.registerModule(it)
46-
}
39+
(config.scanPackagesForModules + javaClass.`package`.name).forEach {
40+
val reflections = Reflections(it)
41+
log.debug("Registering modules in package $it")
42+
val objects = reflections.getSubTypesOf(OpenAPIGenExtension::class.java).mapNotNull { it.kotlin.objectInstance }
43+
objects.forEach {
44+
log.trace("Registering global module: ${it::class.simpleName}")
45+
it.onInit(this)
4746
}
4847
}
4948
}
@@ -71,6 +70,7 @@ class OpenAPIGen(
7170
var schemaNamer: (KType) -> String = KType::toString
7271

7372
var registrars: Array<PartialSchemaRegistrar> = arrayOf()
73+
var scanPackagesForModules: Array<String> = arrayOf()
7474
}
7575

7676

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.papsign.ktor.openapigen
2+
3+
interface OpenAPIGenExtension {
4+
fun onInit(gen: OpenAPIGen)
5+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.papsign.ktor.openapigen
2+
3+
import com.papsign.ktor.openapigen.modules.OpenAPIModule
4+
5+
/**
6+
* implement this to automatically register an object as [OpenAPIModule] in the global context
7+
* only works if the object is in a package declared in [OpenAPIGen.Configuration.scanPackagesForModules]
8+
*/
9+
interface OpenAPIGenModuleExtension: OpenAPIModule, OpenAPIGenExtension {
10+
override fun onInit(gen: OpenAPIGen) {
11+
gen.globalModuleProvider.registerModule(this)
12+
}
13+
}

src/main/kotlin/com/papsign/ktor/openapigen/annotations/encodings/APIFormatter.kt

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/main/kotlin/com/papsign/ktor/openapigen/content/type/binary/BinaryContentTypeParser.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import com.papsign.kotlin.reflection.getKType
44
import com.papsign.kotlin.reflection.getObjectSubtypes
55
import com.papsign.kotlin.reflection.unitKType
66
import com.papsign.ktor.openapigen.OpenAPIGen
7+
import com.papsign.ktor.openapigen.OpenAPIGenModuleExtension
78
import com.papsign.ktor.openapigen.annotations.Response
8-
import com.papsign.ktor.openapigen.annotations.encodings.APIFormatter
99
import com.papsign.ktor.openapigen.content.type.BodyParser
1010
import com.papsign.ktor.openapigen.content.type.ContentTypeProvider
1111
import com.papsign.ktor.openapigen.content.type.ResponseSerializer
@@ -30,8 +30,7 @@ import kotlin.reflect.full.declaredMemberProperties
3030
import kotlin.reflect.full.findAnnotation
3131
import kotlin.reflect.jvm.jvmErasure
3232

33-
@APIFormatter
34-
object BinaryContentTypeParser: BodyParser, ResponseSerializer {
33+
object BinaryContentTypeParser: BodyParser, ResponseSerializer, OpenAPIGenModuleExtension {
3534

3635
override fun <T : Any> getParseableContentTypes(clazz: KClass<T>): List<ContentType> {
3736
return clazz.findAnnotation<BinaryRequest>()?.contentTypes?.map(ContentType.Companion::parse) ?: listOf()
@@ -94,4 +93,4 @@ object BinaryContentTypeParser: BodyParser, ResponseSerializer {
9493
}
9594

9695
private val acceptedTypes = setOf(getKType<InputStream>())
97-
}
96+
}

src/main/kotlin/com/papsign/ktor/openapigen/content/type/ktor/KtorContentProvider.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.papsign.kotlin.reflection.allTypes
44
import com.papsign.kotlin.reflection.getKType
55
import com.papsign.kotlin.reflection.unitKType
66
import com.papsign.ktor.openapigen.OpenAPIGen
7-
import com.papsign.ktor.openapigen.annotations.encodings.APIFormatter
7+
import com.papsign.ktor.openapigen.OpenAPIGenModuleExtension
88
import com.papsign.ktor.openapigen.annotations.encodings.APIRequestFormat
99
import com.papsign.ktor.openapigen.annotations.encodings.APIResponseFormat
1010
import com.papsign.ktor.openapigen.content.type.BodyParser
@@ -36,8 +36,7 @@ import kotlin.reflect.jvm.jvmErasure
3636
/**
3737
* default content provider using the ktor pipeline to handle the serialization and deserialization
3838
*/
39-
@APIFormatter
40-
object KtorContentProvider : ContentTypeProvider, BodyParser, ResponseSerializer {
39+
object KtorContentProvider : ContentTypeProvider, BodyParser, ResponseSerializer, OpenAPIGenModuleExtension {
4140

4241
private val arrayType = getKType<ByteArray>()
4342
private var contentNegotiation: ContentNegotiation? = null

src/main/kotlin/com/papsign/ktor/openapigen/content/type/multipart/MultipartFormDataContentProvider.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.papsign.kotlin.reflection.getKType
44
import com.papsign.kotlin.reflection.getObjectSubtypes
55
import com.papsign.kotlin.reflection.unitKType
66
import com.papsign.ktor.openapigen.OpenAPIGen
7-
import com.papsign.ktor.openapigen.annotations.encodings.APIFormatter
7+
import com.papsign.ktor.openapigen.OpenAPIGenModuleExtension
88
import com.papsign.ktor.openapigen.content.type.BodyParser
99
import com.papsign.ktor.openapigen.content.type.ContentTypeProvider
1010
import com.papsign.ktor.openapigen.exceptions.assertContent
@@ -30,8 +30,7 @@ import kotlin.reflect.full.primaryConstructor
3030
import kotlin.reflect.full.withNullability
3131
import kotlin.reflect.jvm.jvmErasure
3232

33-
@APIFormatter
34-
object MultipartFormDataContentProvider : BodyParser {
33+
object MultipartFormDataContentProvider : BodyParser, OpenAPIGenModuleExtension {
3534

3635
override fun <T : Any> getParseableContentTypes(clazz: KClass<T>): List<ContentType> {
3736
return listOf(ContentType.MultiPart.FormData)
@@ -162,4 +161,4 @@ object MultipartFormDataContentProvider : BodyParser {
162161
@Suppress("UNCHECKED_CAST")
163162
return mapOf(ContentType.MultiPart.FormData to MediaType(schema.schema as Schema<T>, example, null, contentTypes))
164163
}
165-
}
164+
}

src/main/kotlin/com/papsign/ktor/openapigen/route/OpenAPIRoute.kt

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.papsign.ktor.openapigen.modules.ofClass
1313
import com.papsign.ktor.openapigen.modules.openapi.HandlerModule
1414
import com.papsign.ktor.openapigen.openAPIGen
1515
import com.papsign.ktor.openapigen.route.response.Responder
16+
import com.papsign.ktor.openapigen.validators.ValidationHandler
1617
import io.ktor.application.ApplicationCall
1718
import io.ktor.application.call
1819
import io.ktor.http.ContentType
@@ -22,6 +23,8 @@ import io.ktor.routing.accept
2223
import io.ktor.routing.application
2324
import io.ktor.routing.contentType
2425
import io.ktor.util.pipeline.PipelineContext
26+
import org.omg.PortableServer.POAHelper
27+
import java.security.Provider
2528
import kotlin.reflect.KClass
2629

2730
abstract class OpenAPIRoute<T : OpenAPIRoute<T>>(val ktorRoute: Route, val provider: CachingModuleProvider) {
@@ -37,6 +40,9 @@ abstract class OpenAPIRoute<T : OpenAPIRoute<T>>(val ktorRoute: Route, val provi
3740
it.configure(apiGen, provider)
3841
}
3942

43+
val BHandler = ValidationHandler<B>(provider)
44+
val PHandler = ValidationHandler<P>(provider)
45+
4046
ktorRoute.apply {
4147
getAcceptMap(R::class).let {
4248
if (it.isNotEmpty()) it else listOf(ContentType.Any to listOf(SelectedSerializer(KtorContentProvider)))
@@ -46,15 +52,15 @@ abstract class OpenAPIRoute<T : OpenAPIRoute<T>>(val ktorRoute: Route, val provi
4652
if (Unit is B) {
4753
handle {
4854
val params: P = if (Unit is P) Unit else buildParameterObject(call, P::class.java)
49-
pass(this, responder, params, Unit)
55+
pass(this, responder, PHandler.handle(params), Unit)
5056
}
5157
} else {
5258
getContentTypesMap(B::class).forEach { (contentType, parsers) ->
5359
contentType(contentType) {
5460
handle {
55-
val receive: B = if (Unit is B) Unit else parsers.getBodyParser(call.request.contentType()).parseBody(B::class, this)
61+
val receive: B = parsers.getBodyParser(call.request.contentType()).parseBody(B::class, this)
5662
val params: P = if (Unit is P) Unit else buildParameterObject(call, P::class.java)
57-
pass(this, responder, params, receive)
63+
pass(this, responder, PHandler.handle(params), BHandler.handle(receive))
5864
}
5965
}
6066
}
@@ -98,4 +104,4 @@ abstract class OpenAPIRoute<T : OpenAPIRoute<T>>(val ktorRoute: Route, val provi
98104
} - ct.parameters.size // edge case already, no need to sort by potential wildcards too, if you do this you are already looking for problems
99105
}
100106
}
101-
}
107+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.papsign.ktor.openapigen.validators
2+
3+
import com.papsign.ktor.openapigen.validators.util.AValidator
4+
5+
@Retention(AnnotationRetention.RUNTIME)
6+
@Target(AnnotationTarget.PROPERTY)
7+
@ValidationAnnotation
8+
annotation class LowerCase
9+
10+
object LowerCaseValidator : AValidator<String, LowerCase>(String::class, LowerCase::class) {
11+
override fun validate(subject: String?, annotation: LowerCase): String? {
12+
return subject?.toLowerCase()
13+
}
14+
}

0 commit comments

Comments
 (0)