diff --git a/examples/spring-boot-openapi-maven-plugin/pom.xml b/examples/spring-boot-openapi-maven-plugin/pom.xml
index cee7a247..30e48c06 100644
--- a/examples/spring-boot-openapi-maven-plugin/pom.xml
+++ b/examples/spring-boot-openapi-maven-plugin/pom.xml
@@ -142,51 +142,6 @@
-
- org.codehaus.mojo
- build-helper-maven-plugin
- 3.0.0
-
-
- add-source
- generate-sources
-
- add-source
-
-
-
-
-
-
-
-
-
-
-
- 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 58e94ed0..c9b12103 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
@@ -10,6 +10,7 @@ import org.springframework.http.HttpMethod
import org.springframework.web.client.RestTemplate
import java.lang.reflect.Type
import java.net.URI
+import java.util.concurrent.CompletableFuture
interface JavaPetstoreClient : AddPet, FindPetsByStatus
@@ -38,8 +39,8 @@ class JavaPetClientConfiguration {
object : JavaPetstoreClient {
fun , Res : Wirespec.Response<*>> handle(
request: Req,
- responseMapper: (Wirespec.ContentMapper, Int, Map>, Wirespec.Content) -> Res
- ):Res = restTemplate.execute(
+ responseMapper: (Wirespec.ContentMapper, Wirespec.Response) -> Res
+ ):CompletableFuture = restTemplate.execute(
URI("https://6467e16be99f0ba0a819fd68.mockapi.io${request.path}"),
HttpMethod.valueOf(request.method.name),
{ req ->
@@ -50,16 +51,21 @@ class JavaPetClientConfiguration {
{ res ->
val contentType = res.headers.contentType?.toString() ?: error("No content type")
val content = Wirespec.Content(contentType, res.body.readBytes())
- responseMapper(javaContentMapper, res.statusCode.value(), res.headers, content)
+ val response = object :Wirespec.Response{
+ override val status = res.statusCode.value()
+ override val headers = res.headers
+ override val content = content
+ }
+ CompletableFuture.completedFuture(responseMapper(javaContentMapper, response))
}
) ?: error("No response")
- override fun addPet(request: AddPet.Request<*>): AddPet.Response<*> {
+ override fun addPet(request: AddPet.Request<*>): CompletableFuture> {
return handle(request, AddPet::RESPONSE_MAPPER)
}
- override fun findPetsByStatus(request: FindPetsByStatus.Request<*>): FindPetsByStatus.Response<*> {
+ override fun findPetsByStatus(request: FindPetsByStatus.Request<*>): CompletableFuture> {
return handle(request, FindPetsByStatus::RESPONSE_MAPPER)
}
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 ca7ecc32..44a09685 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
@@ -18,7 +18,7 @@ class JavaPetstoreController(
suspend fun addPet(): Optional? {
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)) {
+ return when (val res = javaPetstoreClient.addPet(req).get()) {
is AddPet.Response200ApplicationJson -> res.content?.body?.id
else -> error("No response")
}
@@ -27,7 +27,7 @@ class JavaPetstoreController(
@PostMapping
suspend fun create(@RequestBody pet: Pet): List {
val req = FindPetsByStatus.RequestVoid(Optional.of(FindPetsByStatusParameterStatus.available))
- return when (val res = javaPetstoreClient.findPetsByStatus(req)) {
+ return when (val res = javaPetstoreClient.findPetsByStatus(req).get()) {
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 edd947ea..d2ceec8f 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
@@ -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, Wirespec.Response) -> Res
) = restTemplate.execute(
URI("https://6467e16be99f0ba0a819fd68.mockapi.io${request.path}"),
HttpMethod.valueOf(request.method.name),
@@ -54,12 +54,12 @@ class KotlinPetClientConfiguration {
{ res ->
val contentType = res.headers.contentType?.toString() ?: error("No content type")
val content = Wirespec.Content(contentType, res.body.readBytes())
- responseMapper(
- kotlinContentMapper,
- res.statusCode.value(),
- res.headers,
- content
- )
+ val response = object :Wirespec.Response{
+ override val status = res.statusCode.value()
+ override val headers = res.headers
+ override val content = content
+ }
+ responseMapper(kotlinContentMapper, response)
}
) ?: error("No response")
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 f13f3346..0c18f24e 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
@@ -44,7 +44,8 @@ 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.Wirespec;\n\n"
+ private fun import(ast: AST) =
+ if (!ast.hasEndpoints()) "" else "import community.flock.wirespec.Wirespec;\nimport java.util.concurrent.CompletableFuture;\n\n"
override fun emit(ast: AST): List> = super.emit(ast)
.map { (name, result) -> name to "$pkg\n\n${import(ast)}$result" }
@@ -64,7 +65,14 @@ class JavaEmitter(
"$SPACER${if (isNullable) "java.util.Optional<${reference.emit()}>" else reference.emit()} ${identifier.emit()}"
}
- override fun Type.Shape.Field.Identifier.emit() = withLogging(logger) { value }
+ override fun Type.Shape.Field.Identifier.emit() = withLogging(logger) {
+ value
+ .split("-")
+ .mapIndexed { index, s -> if (index > 0) s.firstToUpper() else s }
+ .joinToString("")
+ .sanitizeKeywords()
+ .sanitizeSymbols()
+ }
private fun Reference.emitPrimaryType() = withLogging(logger) {
when (this) {
@@ -84,7 +92,7 @@ class JavaEmitter(
}
override fun Enum.emit() = withLogging(logger) {
- fun String.sanitize() = replace("-", "_").let { if(it.first().isDigit()) "_$it" else it }
+ fun String.sanitize() = replace("-", "_").let { if (it.first().isDigit()) "_$it" else it }
val body = """
|${SPACER}public final String label;
|${SPACER}$name(String label) {
@@ -97,7 +105,7 @@ class JavaEmitter(
|${SPACER}${SPACER}return label;
|${SPACER}}
""".trimMargin()
- "public enum $name {\n${SPACER}${entries.joinToString(",\n${SPACER}"){ enum -> "${enum.sanitize()}(\"${enum}\")"}};\n${body}\n${toString}\n}\n"
+ "public enum $name {\n${SPACER}${entries.joinToString(",\n${SPACER}") { enum -> "${enum.sanitize()}(\"${enum}\")" }};\n${body}\n${toString}\n}\n"
}
override fun Refined.emit() = withLogging(logger) {
@@ -117,25 +125,25 @@ class JavaEmitter(
"""public interface $name {
|${SPACER}static String PATH = "${path.emitSegment()}";
|${responses.emitResponseMapper()}
- |${SPACER}interface Request extends Wirespec.Request {}
+ |${SPACER}sealed interface Request extends Wirespec.Request {}
|${requests.joinToString("\n") { it.emit(this) }}
- |${SPACER}interface Response extends Wirespec.Response {}
+ |${SPACER}sealed interface Response extends Wirespec.Response {}
|${
responses.map { it.status.groupStatus() }.toSet()
- .joinToString("\n") { "${SPACER}interface Response${it} extends Response{};" }
+ .joinToString("\n") { "${SPACER}sealed interface Response${it} extends Response{};" }
}
|${
responses.filter { it.status.isInt() }.map { it.status }.toSet()
- .joinToString("\n") { "${SPACER}interface Response${it} extends Response${it.groupStatus()}{};" }
+ .joinToString("\n") { "${SPACER}sealed interface Response${it} extends Response${it.groupStatus()}{};" }
}
|${responses.distinctBy { it.status to it.content?.type }.joinToString("\n") { it.emit() }}
- |${SPACER}public Response ${name.firstToLower()}(Request request);
+ |${SPACER}public CompletableFuture ${name.firstToLower()}(Request request);
|}
|""".trimMargin()
}
private fun Endpoint.Request.emit(endpoint: Endpoint) = """
- |${SPACER}class Request${content.emitContentType()} implements Request<${content?.reference?.emit() ?: "Void"}> {
+ |${SPACER}final class Request${content.emitContentType()} implements Request<${content?.reference?.emit() ?: "Void"}> {
|${SPACER}${SPACER}private final String path;
|${SPACER}${SPACER}private final Wirespec.Method method;
|${SPACER}${SPACER}private final java.util.Map> query;
@@ -157,8 +165,8 @@ class JavaEmitter(
""".trimMargin()
private fun Endpoint.Response.emit() = """
- |${SPACER}class Response${status.firstToUpper()}${content.emitContentType()} implements Response${
- status.takeIf { it.isInt() }?.groupStatus().orEmptyString()
+ |${SPACER}final class Response${status.firstToUpper()}${content.emitContentType()} implements Response${
+ status.firstToUpper().orEmptyString()
}<${content?.reference?.emit() ?: "Void"}> {
|${SPACER}${SPACER}private final int status;
|${SPACER}${SPACER}private final java.util.Map> headers;
@@ -177,7 +185,7 @@ class JavaEmitter(
""".trimMargin()
private fun List.emitResponseMapper() = """
- |${SPACER}static Response RESPONSE_MAPPER(Wirespec.ContentMapper contentMapper, int status, java.util.Map> headers, Wirespec.Content content) {
+ |${SPACER}static Response RESPONSE_MAPPER(Wirespec.ContentMapper contentMapper, Wirespec.Response response) {
|${distinctBy { it.status to it.content?.type }.joinToString("") { it.emitResponseMapperCondition() }}
|${SPACER}${SPACER}throw new IllegalStateException("Unknown response type");
|${SPACER}}
@@ -187,21 +195,21 @@ class JavaEmitter(
when (content) {
null -> """
|${SPACER}${SPACER}${SPACER}if(${
- status.takeIf { it.isInt() }?.let { "status == $status && " }.orEmptyString()
- }content == null) { return new Response${status.firstToUpper()}Void(${
- status.takeIf { !it.isInt() }?.let { "status, " }.orEmptyString()
- }headers); }
+ status.takeIf { it.isInt() }?.let { "response.getStatus() == $status && " }.orEmptyString()
+ }response.getContent() == null) { return new Response${status.firstToUpper()}Void(${
+ status.takeIf { !it.isInt() }?.let { "response.getStatus(), " }.orEmptyString()
+ }response.getHeaders()); }
|
""".trimMargin()
else -> """
|${SPACER}${SPACER}${SPACER}if(${
- status.takeIf { it.isInt() }?.let { "status == $status && " }.orEmptyString()
- }content.type().equals("${content.type}")) {
- |${SPACER}${SPACER}${SPACER}${SPACER}Wirespec.Content<${content.reference.emit()}> c = contentMapper.read(content, Wirespec.getType(${content.reference.emitPrimaryType()}.class, ${content.reference.isIterable}));
+ status.takeIf { it.isInt() }?.let { "response.getStatus() == $status && " }.orEmptyString()
+ }response.getContent().type().equals("${content.type}")) {
+ |${SPACER}${SPACER}${SPACER}${SPACER}Wirespec.Content<${content.reference.emit()}> content = contentMapper.read(response.getContent(), Wirespec.getType(${content.reference.emitPrimaryType()}.class, ${content.reference.isIterable}));
|${SPACER}${SPACER}${SPACER}${SPACER}return new Response${status.firstToUpper()}${content.emitContentType()}(${
- status.takeIf { !it.isInt() }?.let { "status, " }.orEmptyString()
- }headers, c.body());
+ status.takeIf { !it.isInt() }?.let { "response.getStatus(), " }.orEmptyString()
+ }response.getHeaders(), content.body());
|${SPACER}${SPACER}${SPACER}}
|
""".trimMargin()
@@ -231,11 +239,7 @@ class JavaEmitter(
.joinToString(", ") { it.emit() }
}
- private fun List.emitMap() = joinToString(
- ", ",
- "java.util.Map.of(",
- ")"
- ) { "\"${it.identifier.emit()}\", java.util.List.of(${it.identifier.emit()})" }
+ private fun List.emitMap() = joinToString(", ", "java.util.Map.of(", ")") { "\"${it.identifier.value}\", java.util.List.of(${it.identifier.emit()})" }
private fun List.emitSegment() = "/" + joinToString("/") {
when (it) {
@@ -254,4 +258,21 @@ class JavaEmitter(
if (isInt()) substring(0, 1) + "XX"
else firstToUpper()
+ fun String.sanitizeKeywords() = if (reservedKeywords.contains(this)) "_$this" else this
+
+ fun String.sanitizeSymbols() = replace(".", "")
+ companion object {
+ private val reservedKeywords = listOf(
+ "abstract", "continue", "for", "new", "switch",
+ "assert", "default", "goto", "package", "synchronized",
+ "boolean", "do", "if", "private", "this",
+ "break", "double", "implements", "protected", "throw",
+ "byte", "else", "import", "public", "throws",
+ "case", "enum", "instanceof", "return", "transient",
+ "catch", "extends", "int", "short", "try",
+ "char", "final", "interface", "static", "void",
+ "class", "finally", "long", "strictfp", "volatile",
+ "const", "float", "native", "super", "while"
+ )
+ }
}
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 a206cdf0..9d630c2c 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
@@ -174,7 +174,7 @@ class KotlinEmitter(
}
private fun List.emitRequestMapper() = """
- |${SPACER}${SPACER}fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, path:String, method: Wirespec.Method, query: Map>, headers:Map>, content: Wirespec.Content?) =
+ |${SPACER}${SPACER}fun REQUEST_MAPPER(contentMapper: Wirespec.ContentMapper, request: Wirespec.Request) =
|${SPACER}${SPACER}${SPACER}when {
|${joinToString("\n") { it.emitRequestMapperCondition() }}
|${SPACER}${SPACER}${SPACER}${SPACER}else -> error("Cannot map request")
@@ -184,35 +184,35 @@ class KotlinEmitter(
private fun Endpoint.Request.emitRequestMapperCondition() =
when (content) {
null -> """
- |${SPACER}${SPACER}${SPACER}${SPACER}content == null -> RequestUnit(path, method, query, headers, null)
+ |${SPACER}${SPACER}${SPACER}${SPACER}request.content == null -> RequestUnit(request.path, request.method, request.query, request.headers, null)
""".trimMargin()
else -> """
- |${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) }
+ |${SPACER}${SPACER}${SPACER}${SPACER}request.content?.type == "${content.type}" -> contentMapper
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}.read<${content.reference.emit()}>(request.content!!, Wirespec.getType(${content.reference.emitPrimaryType()}::class.java, ${content.reference.isIterable}))
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}.let{ Request${content.emitContentType()}(request.path, request.method, request.query, request.headers, it) }
""".trimMargin()
}
private fun List.emitResponseMapper() = """
- |${SPACER}${SPACER}fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, status: Int, headers:Map>, content: Wirespec.Content?) =
+ |${SPACER}${SPACER}fun RESPONSE_MAPPER(contentMapper: Wirespec.ContentMapper, response: Wirespec.Response) =
|${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}${SPACER}else -> error("Cannot map response with status ${"$"}status")
+ |${SPACER}${SPACER}${SPACER}${SPACER}else -> error("Cannot map response with status ${"$"}response.status")
|${SPACER}${SPACER}${SPACER}}
""".trimMargin()
private fun Endpoint.Response.emitResponseMapperCondition() =
when (content) {
null -> """
- |${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)
+ |${SPACER}${SPACER}${SPACER}${SPACER}${status.takeIf { it.isInt() }?.let { "response.status == $status && " }.orEmptyString()}response.content == null -> Response${status.firstToUpper()}Unit(${status.takeIf { !it.isInt() }?.let { "response.status, " }.orEmptyString()}response.headers)
""".trimMargin()
else -> """
- |${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) }
+ |${SPACER}${SPACER}${SPACER}${SPACER}${status.takeIf { it.isInt() }?.let { "response.status == $status && " }.orEmptyString()}response.content?.type == "${content.type}" -> contentMapper
+ |${SPACER}${SPACER}${SPACER}${SPACER}${SPACER}.read<${content.reference.emit()}>(response.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 { "response.status, " }.orEmptyString()}response.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 fec82148..d55d7b8b 100644
--- a/src/plugin/maven/src/main/kotlin/GenerateMojo.kt
+++ b/src/plugin/maven/src/main/kotlin/GenerateMojo.kt
@@ -35,7 +35,7 @@ class GenerateMojo : BaseMojo() {
private var languages: List? = null
@Parameter
- private var shared: Boolean? = null
+ private var shared: Boolean = true
@Parameter(defaultValue = "\${project}", readonly = true, required = true)
private lateinit var project: MavenProject
@@ -50,11 +50,12 @@ class GenerateMojo : BaseMojo() {
Language.Wirespec -> TODO()
}
}
+ project.addCompileSourceRoot(output);
}
fun executeKotlin() {
val emitter = KotlinEmitter(packageName, logger)
- if(shared == true) {
+ if(shared) {
JvmUtil.emitJvm("community.flock.wirespec", output, "Wirespec", "kt").writeText(emitter.shared)
}
if (openapi != null) {
@@ -80,7 +81,7 @@ class GenerateMojo : BaseMojo() {
fun executeJava() {
val emitter = JavaEmitter(packageName, logger)
- if(shared == true) {
+ if(shared) {
JvmUtil.emitJvm("community.flock.wirespec", output, "Wirespec", "java").writeText(emitter.shared)
}
if (openapi != null) {
@@ -103,7 +104,7 @@ class GenerateMojo : BaseMojo() {
fun executeScala() {
val emitter = ScalaEmitter(packageName, logger)
- if(shared == true) {
+ if(shared) {
JvmUtil.emitJvm("community.flock.wirespec", output, "Wirespec", "scala").writeText(emitter.shared)
}
compile(input, logger, emitter)