diff --git a/examples/spring-boot-openapi-maven-plugin/pom.xml b/examples/spring-boot-openapi-maven-plugin/pom.xml
index f1bcda33..cee7a247 100644
--- a/examples/spring-boot-openapi-maven-plugin/pom.xml
+++ b/examples/spring-boot-openapi-maven-plugin/pom.xml
@@ -89,6 +89,7 @@
Kotlin
+ true
@@ -104,6 +105,7 @@
Kotlin
+ false
@@ -119,6 +121,7 @@
Java
+ false
@@ -134,6 +137,7 @@
Java
+ false
@@ -157,6 +161,32 @@
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+
+ default-compile
+ none
+
+
+ default-testCompile
+ none
+
+
+ java-compile
+ compile
+ compile
+
+
+ java-test-compile
+ test-compile
+ testCompile
+
+
+
diff --git a/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/java/JavaPetstoreClient.kt b/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/java/JavaPetstoreClient.kt
index ab30abfc..58e94ed0 100644
--- a/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/java/JavaPetstoreClient.kt
+++ b/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/java/JavaPetstoreClient.kt
@@ -1,10 +1,7 @@
package community.flock.wirespec.examples.open_api_app.java
import com.fasterxml.jackson.databind.ObjectMapper
-import community.flock.wirespec.java.Wirespec.Request
-import community.flock.wirespec.java.Wirespec.Response
-import community.flock.wirespec.java.Wirespec.Content
-import community.flock.wirespec.java.Wirespec.ContentMapper
+import community.flock.wirespec.Wirespec
import community.flock.wirespec.generated.java.v3.AddPet
import community.flock.wirespec.generated.java.v3.FindPetsByStatus
import org.springframework.context.annotation.Bean
@@ -21,27 +18,27 @@ class JavaPetClientConfiguration {
@Bean
fun javaContentMapper(objectMapper: ObjectMapper) =
- object : ContentMapper {
- override fun read(content: Content, valueType: Type): Content = content.let {
+ object : Wirespec.ContentMapper {
+ override fun read(content: Wirespec.Content, valueType: Type): Wirespec.Content = content.let {
val type = objectMapper.constructType(valueType)
val obj: T = objectMapper.readValue(content.body, type)
- Content(it.type, obj)
+ Wirespec.Content(it.type, obj)
}
- override fun write(content: Content): Content = content.let {
+ override fun write(content: Wirespec.Content): Wirespec.Content = content.let {
val bytes = objectMapper.writeValueAsBytes(content.body)
- Content(it.type, bytes)
+ Wirespec.Content(it.type, bytes)
}
}
@Bean
- fun javaPetstoreClient(restTemplate: RestTemplate, javaContentMapper: ContentMapper): JavaPetstoreClient =
+ fun javaPetstoreClient(restTemplate: RestTemplate, javaContentMapper: Wirespec.ContentMapper): JavaPetstoreClient =
object : JavaPetstoreClient {
- fun , Res : Response<*>> handle(
+ fun , Res : Wirespec.Response<*>> handle(
request: Req,
- responseMapper: (ContentMapper, Int, Map>, Content) -> Res
+ responseMapper: (Wirespec.ContentMapper, Int, Map>, Wirespec.Content) -> Res
):Res = restTemplate.execute(
URI("https://6467e16be99f0ba0a819fd68.mockapi.io${request.path}"),
HttpMethod.valueOf(request.method.name),
@@ -52,7 +49,7 @@ class JavaPetClientConfiguration {
},
{ res ->
val contentType = res.headers.contentType?.toString() ?: error("No content type")
- val content = Content(contentType, res.body.readBytes())
+ val content = Wirespec.Content(contentType, res.body.readBytes())
responseMapper(javaContentMapper, res.statusCode.value(), res.headers, content)
}
) ?: error("No response")
diff --git a/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/java/JavaPetstoreController.kt b/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/java/JavaPetstoreController.kt
index 468c539a..ca7ecc32 100644
--- a/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/java/JavaPetstoreController.kt
+++ b/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/java/JavaPetstoreController.kt
@@ -19,7 +19,7 @@ class JavaPetstoreController(
val pet = Pet(Optional.empty(), "Petje", Optional.empty(), emptyList(), Optional.empty(), Optional.empty())
val req = AddPet.RequestApplicationJson(pet)
return when (val res = javaPetstoreClient.addPet(req)) {
- is AddPet.Response200ApplicationJson -> res.content.body.id
+ is AddPet.Response200ApplicationJson -> res.content?.body?.id
else -> error("No response")
}
}
@@ -28,7 +28,7 @@ class JavaPetstoreController(
suspend fun create(@RequestBody pet: Pet): List {
val req = FindPetsByStatus.RequestVoid(Optional.of(FindPetsByStatusParameterStatus.available))
return when (val res = javaPetstoreClient.findPetsByStatus(req)) {
- is FindPetsByStatus.Response200ApplicationJson -> res.content.body.mapNotNull { it.id.getOrNull() }
+ is FindPetsByStatus.Response200ApplicationJson -> res.content?.body?.mapNotNull { it.id.getOrNull() } ?: emptyList()
else -> error("No response")
}
}
diff --git a/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/kotlin/KotlinPetstoreClient.kt b/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/kotlin/KotlinPetstoreClient.kt
index 97d5b5d7..edd947ea 100644
--- a/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/kotlin/KotlinPetstoreClient.kt
+++ b/examples/spring-boot-openapi-maven-plugin/src/main/kotlin/community/flock/wirespec/examples/open_api_app/kotlin/KotlinPetstoreClient.kt
@@ -3,7 +3,7 @@ package community.flock.wirespec.examples.open_api_app.kotlin
import com.fasterxml.jackson.databind.ObjectMapper
import community.flock.wirespec.generated.kotlin.v3.AddPet
import community.flock.wirespec.generated.kotlin.v3.FindPetsByStatus
-import community.flock.wirespec.kotlin.Wirespec
+import community.flock.wirespec.Wirespec
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
@@ -42,7 +42,7 @@ class KotlinPetClientConfiguration {
object : KotlinPetstoreClient {
fun , Res : Wirespec.Response<*>> handle(
request: Req,
- responseMapper: (Wirespec.ContentMapper) -> (Int, Map>, Wirespec.Content) -> Res
+ responseMapper: (Wirespec.ContentMapper, Int, Map>, Wirespec.Content) -> Res
) = restTemplate.execute(
URI("https://6467e16be99f0ba0a819fd68.mockapi.io${request.path}"),
HttpMethod.valueOf(request.method.name),
@@ -54,7 +54,8 @@ class KotlinPetClientConfiguration {
{ res ->
val contentType = res.headers.contentType?.toString() ?: error("No content type")
val content = Wirespec.Content(contentType, res.body.readBytes())
- responseMapper(kotlinContentMapper)(
+ responseMapper(
+ kotlinContentMapper,
res.statusCode.value(),
res.headers,
content
diff --git a/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/JavaEmitter.kt b/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/JavaEmitter.kt
index a311853f..f13f3346 100644
--- a/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/JavaEmitter.kt
+++ b/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/JavaEmitter.kt
@@ -17,7 +17,7 @@ class JavaEmitter(
) : Emitter(logger, true) {
override val shared = """
- |package community.flock.wirespec.java;
+ |package community.flock.wirespec;
|
|import java.lang.reflect.Type;
|import java.lang.reflect.ParameterizedType;
@@ -44,7 +44,7 @@ class JavaEmitter(
""".trimMargin()
private val pkg = if (packageName.isBlank()) "" else "package $packageName;"
- private fun import(ast:AST) = if (!ast.hasEndpoints()) "" else "import community.flock.wirespec.java.Wirespec;\n\n"
+ private fun import(ast:AST) = if (!ast.hasEndpoints()) "" else "import community.flock.wirespec.Wirespec;\n\n"
override fun emit(ast: AST): List> = super.emit(ast)
.map { (name, result) -> name to "$pkg\n\n${import(ast)}$result" }
diff --git a/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/KotlinEmitter.kt b/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/KotlinEmitter.kt
index 07bbeaaa..a206cdf0 100644
--- a/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/KotlinEmitter.kt
+++ b/src/compiler/core/src/commonMain/kotlin/community/flock/wirespec/compiler/core/emit/KotlinEmitter.kt
@@ -19,23 +19,36 @@ class KotlinEmitter(
) : Emitter(logger) {
override val shared = """
- |package community.flock.wirespec.kotlin
+ |package community.flock.wirespec
|
|import java.lang.reflect.Type
+ |import java.lang.reflect.ParameterizedType
|
|interface Wirespec {
|${SPACER}enum class Method { GET, PUT, POST, DELETE, OPTIONS, HEAD, PATCH, TRACE }
- |${SPACER}data class Content (val type:String, val body:T )
+ |${SPACER}@JvmRecord data class Content (val type:String, val body:T )
|${SPACER}interface Request { val path:String; val method: Method; val query: Map>; val headers: Map>; val content:Content? }
|${SPACER}interface Response { val status:Int; val headers: Map>; val content:Content? }
|${SPACER}interface ContentMapper { fun read(content: Content, valueType: Type): Content fun write(content: Content): Content }
+ |${SPACER}companion object {
+ |${SPACER}${SPACER}@JvmStatic fun getType(type: Class<*>, isIterable: Boolean): Type {
+ |${SPACER}${SPACER}${SPACER}return if (isIterable) {
+ |${SPACER}${SPACER}${SPACER}${SPACER}object : ParameterizedType {
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}override fun getRawType() = MutableList::class.java
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}override fun getActualTypeArguments() = arrayOf(type)
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}override fun getOwnerType() = null
+ |${SPACER}${SPACER}${SPACER}${SPACER}}
+ |${SPACER}${SPACER}${SPACER}} else {
+ |${SPACER}${SPACER}${SPACER}${SPACER}type
+ |${SPACER}${SPACER}${SPACER}}
+ |${SPACER}${SPACER}}
+ |${SPACER}}
|}
""".trimMargin()
val import = """
- |import kotlin.reflect.typeOf
- |import kotlin.reflect.jvm.javaType
- |import community.flock.wirespec.kotlin.Wirespec
+ |
+ |import community.flock.wirespec.Wirespec
|
""".trimMargin()
@@ -72,7 +85,7 @@ class KotlinEmitter(
.sanitizeSymbols()
}
- override fun Reference.emit() = withLogging(logger) {
+ private fun Reference.emitPrimaryType() = withLogging(logger) {
when (this) {
is Reference.Any -> "Any"
is Reference.Custom -> value
@@ -82,6 +95,10 @@ class KotlinEmitter(
Reference.Primitive.Type.Boolean -> "Boolean"
}
}
+ }
+
+ override fun Reference.emit() = withLogging(logger) {
+ emitPrimaryType()
.let { if (isIterable) "List<$it>" else it }
.let { if (isMap) "Map" else it }
}
@@ -108,11 +125,11 @@ class KotlinEmitter(
|${responses.filter { it.status.isInt() }.map { it.status }.toSet().joinToString("\n") { "${SPACER}sealed interface Response${it}: Response${it.groupStatus()}" }}
|${responses.filter { it.status.isInt() }.distinctBy { it.status to it.content?.type }.joinToString("\n") { "${SPACER}class Response${it.status}${it.content?.emitContentType() ?: "Unit"} (override val headers: Map>${it.content?.let { ", body: ${it.reference.emit()}" } ?: ""} ): Response${it.status}<${it.content?.reference?.emit() ?: "Unit"}> { override val status = ${it.status}; override val content = ${it.content?.let { "Wirespec.Content(\"${it.type}\", body)" } ?: "null"}}" }}
|${responses.filter { !it.status.isInt() }.distinctBy { it.status to it.content?.type }.joinToString("\n") { "${SPACER}class Response${it.status.firstToUpper()}${it.content?.emitContentType() ?: "Unit"} (override val status: Int, override val headers: Map>${it.content?.let { ", body: ${it.reference.emit()}" } ?: ""} ): Response${it.status.firstToUpper()}<${it.content?.reference?.emit() ?: "Unit"}> { override val content = ${it.content?.let { "Wirespec.Content(\"${it.type}\", body)" } ?: "null"}}" }}
- |suspend fun ${name.firstToLower()}(request: Request<*>): Response<*>
+ |${SPACER}suspend fun ${name.firstToLower()}(request: Request<*>): Response<*>
|${SPACER}companion object{
|${SPACER}${SPACER}const val PATH = "${path.emitSegment()}"
- |${SPACER}${SPACER}${requests.emitRequestMapper()}
- |${SPACER}${SPACER}${responses.emitResponseMapper()}
+ |${requests.emitRequestMapper()}
+ |${responses.emitResponseMapper()}
|${SPACER}}
|}
|""".trimMargin()
@@ -157,47 +174,45 @@ class KotlinEmitter(
}
private fun List.emitRequestMapper() = """
- |fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper) =
- |${SPACER}fun(path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) =
- |${SPACER}${SPACER}when {
+ |${SPACER}${SPACER}fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) =
+ |${SPACER}${SPACER}${SPACER}when {
|${joinToString("\n") { it.emitRequestMapperCondition() }}
- |${SPACER}${SPACER}${SPACER}else -> error("Cannot map request")
- |${SPACER}${SPACER}}
+ |${SPACER}${SPACER}${SPACER}${SPACER}else -> error("Cannot map request")
+ |${SPACER}${SPACER}${SPACER}}
""".trimMargin()
private fun Endpoint.Request.emitRequestMapperCondition() =
when (content) {
null -> """
- |${SPACER}${SPACER}${SPACER}content == null -> RequestUnit(path, method, query, headers, null)
+ |${SPACER}${SPACER}${SPACER}${SPACER}content == null -> RequestUnit(path, method, query, headers, null)
""".trimMargin()
else -> """
- |${SPACER}${SPACER}${SPACER}content?.type == "${content.type}" -> contentMapper
- |${SPACER}${SPACER}${SPACER}${SPACER}.read<${content.reference.emit()}>(content, typeOf<${content.reference.emit()}>().javaType)
- |${SPACER}${SPACER}${SPACER}${SPACER}.let{ Request${content.emitContentType()}(path, method, query, headers, it) }
+ |${SPACER}${SPACER}${SPACER}${SPACER}content?.type == "${content.type}" -> contentMapper
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}.read<${content.reference.emit()}>(content, Wirespec.getType(${content.reference.emitPrimaryType()}::class.java, ${content.reference.isIterable}))
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}.let{ Request${content.emitContentType()}(path, method, query, headers, it) }
""".trimMargin()
}
private fun List.emitResponseMapper() = """
- |fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper) =
- |${SPACER}fun(status: Int, headers:Map>, content: Wirespec.Content?) =
- |${SPACER}${SPACER}when {
+ |${SPACER}${SPACER}fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) =
+ |${SPACER}${SPACER}${SPACER}when {
|${filter { it.status.isInt() }.distinctBy { it.status to it.content?.type }.joinToString("\n") { it.emitResponseMapperCondition() }}
|${filter { !it.status.isInt() }.distinctBy { it.status to it.content?.type }.joinToString("\n") { it.emitResponseMapperCondition() }}
- |${SPACER}${SPACER}${SPACER}else -> error("Cannot map response with status ${"$"}status")
- |${SPACER}${SPACER}}
+ |${SPACER}${SPACER}${SPACER}${SPACER}else -> error("Cannot map response with status ${"$"}status")
+ |${SPACER}${SPACER}${SPACER}}
""".trimMargin()
private fun Endpoint.Response.emitResponseMapperCondition() =
when (content) {
null -> """
- |${SPACER}${SPACER}${SPACER}${status.takeIf { it.isInt() }?.let { "status == $status && " }.orEmptyString()}content == null -> Response${status.firstToUpper()}Unit(${status.takeIf { !it.isInt() }?.let { "status, " }.orEmptyString()}headers)
+ |${SPACER}${SPACER}${SPACER}${SPACER}${status.takeIf { it.isInt() }?.let { "status == $status && " }.orEmptyString()}content == null -> Response${status.firstToUpper()}Unit(${status.takeIf { !it.isInt() }?.let { "status, " }.orEmptyString()}headers)
""".trimMargin()
else -> """
- |${SPACER}${SPACER}${SPACER}${status.takeIf { it.isInt() }?.let { "status == $status && " }.orEmptyString()}content?.type == "${content.type}" -> contentMapper
- |${SPACER}${SPACER}${SPACER}${SPACER}.read<${content.reference.emit()}>(content, typeOf<${content.reference.emit()}>().javaType)
- |${SPACER}${SPACER}${SPACER}${SPACER}.let{ Response${status.firstToUpper()}${content.emitContentType()}(${status.takeIf { !it.isInt() }?.let { "status, " }.orEmptyString()}headers, it.body) }
+ |${SPACER}${SPACER}${SPACER}${SPACER}${status.takeIf { it.isInt() }?.let { "status == $status && " }.orEmptyString()}content?.type == "${content.type}" -> contentMapper
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}.read<${content.reference.emit()}>(content, Wirespec.getType(${content.reference.emitPrimaryType()}::class.java, ${content.reference.isIterable}))
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}.let{ Response${status.firstToUpper()}${content.emitContentType()}(${status.takeIf { !it.isInt() }?.let { "status, " }.orEmptyString()}headers, it.body) }
""".trimMargin()
}
diff --git a/src/plugin/maven/src/main/kotlin/GenerateMojo.kt b/src/plugin/maven/src/main/kotlin/GenerateMojo.kt
index e17d94bb..fec82148 100644
--- a/src/plugin/maven/src/main/kotlin/GenerateMojo.kt
+++ b/src/plugin/maven/src/main/kotlin/GenerateMojo.kt
@@ -34,6 +34,9 @@ class GenerateMojo : BaseMojo() {
@Parameter
private var languages: List? = null
+ @Parameter
+ private var shared: Boolean? = null
+
@Parameter(defaultValue = "\${project}", readonly = true, required = true)
private lateinit var project: MavenProject
@@ -51,7 +54,9 @@ class GenerateMojo : BaseMojo() {
fun executeKotlin() {
val emitter = KotlinEmitter(packageName, logger)
- JvmUtil.emitJvm("community.flock.wirespec.kotlin", output, "Wirespec", "kt").writeText(emitter.shared)
+ if(shared == true) {
+ JvmUtil.emitJvm("community.flock.wirespec", output, "Wirespec", "kt").writeText(emitter.shared)
+ }
if (openapi != null) {
val fileName = input.split("/")
.last()
@@ -75,7 +80,9 @@ class GenerateMojo : BaseMojo() {
fun executeJava() {
val emitter = JavaEmitter(packageName, logger)
- JvmUtil.emitJvm("community.flock.wirespec.java", output, "Wirespec", "java").writeText(emitter.shared)
+ if(shared == true) {
+ JvmUtil.emitJvm("community.flock.wirespec", output, "Wirespec", "java").writeText(emitter.shared)
+ }
if (openapi != null) {
val json = File(input).readText()
val ast = when (openapi) {
@@ -96,6 +103,9 @@ class GenerateMojo : BaseMojo() {
fun executeScala() {
val emitter = ScalaEmitter(packageName, logger)
+ if(shared == true) {
+ JvmUtil.emitJvm("community.flock.wirespec", output, "Wirespec", "scala").writeText(emitter.shared)
+ }
compile(input, logger, emitter)
.forEach { (name, result) -> JvmUtil.emitJvm(packageName, output, name, "scala").writeText(result) }
}