Skip to content

Commit

Permalink
Support for OpenAPIV2 and OpenAPIV3 language in plugins (#304)
Browse files Browse the repository at this point in the history
Co-authored-by: nsimonides
  • Loading branch information
nsmnds authored Dec 4, 2024
1 parent f5ee9a3 commit 197b3f8
Show file tree
Hide file tree
Showing 24 changed files with 140 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ public CustomEmitter(@NotNull Logger logger, boolean split) {
super(logger, split);
}

@NotNull
@Override
public String getSingleLineComment() {
return "//";
}

@NotNull
@Override
public String emitName(@NotNull Definition definition) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ fun String.removeJavaPrefix() = removePrefix("java.util.")

fun String.removeAngularBrackets() = filterNot { it == '<' || it == '>' }

fun String.removeCommasAndSpaces() = filterNot { it == ',' || it == ' '}
fun String.removeCommasAndSpaces() = filterNot { it == ',' || it == ' ' }

fun String.removeCommentMarkers(): String = removePrefix("/*").removeSuffix("*/").trim()
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,7 @@ open class JavaEmitter(
is Union -> emit(identifier)
}

override fun notYetImplemented() =
"""// TODO("Not yet implemented")
|
""".trimMargin()
override val singleLineComment = "//"

override fun emit(ast: AST): List<Emitted> =
super.emit(ast).map { (typeName, result) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ open class KotlinEmitter(
is Union -> emit(identifier)
}

override fun notYetImplemented() =
"""// TODO("Not yet implemented")
|
""".trimMargin()
override val singleLineComment = "//"

override fun emit(ast: AST): List<Emitted> =
super.emit(ast).map { (typeName, result) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ open class ScalaEmitter(
is Union -> emit(identifier)
}

override fun notYetImplemented() =
"""// TODO("Not yet implemented")
|
""".trimMargin()
override val singleLineComment = "//"

override fun emit(ast: AST): List<Emitted> =
super.emit(ast).map { (typeName, result) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package community.flock.wirespec.compiler.core.emit
import community.flock.wirespec.compiler.core.emit.common.DefinitionModelEmitter
import community.flock.wirespec.compiler.core.emit.common.Emitted
import community.flock.wirespec.compiler.core.emit.common.Emitter
import community.flock.wirespec.compiler.core.emit.common.Emitter.Companion.firstToLower
import community.flock.wirespec.compiler.core.emit.common.Spacer
import community.flock.wirespec.compiler.core.emit.shared.TypeScriptShared
import community.flock.wirespec.compiler.core.parse.AST
Expand Down Expand Up @@ -31,10 +30,7 @@ open class TypeScriptEmitter(logger: Logger = noLogger) : DefinitionModelEmitter
is Channel -> emit(identifier)
}

override fun notYetImplemented() =
"""// TODO("Not yet implemented")
|
""".trimMargin()
override val singleLineComment = "//"

override fun emit(ast: AST): List<Emitted> =
super.emit(ast).map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ open class WirespecEmitter(logger: Logger = noLogger) : DefinitionModelEmitter,
is Channel -> emit(identifier)
}

override fun notYetImplemented() = "\n"
override val singleLineComment = "\n"

override fun notYetImplemented() = singleLineComment

override fun emit(type: Type, ast: AST) = """
|type ${emit(type.identifier)} {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import community.flock.wirespec.compiler.utils.Logger

abstract class Emitter(
val logger: Logger,
val split: Boolean = false
val split: Boolean = false,
) : Emitters {

data class Param(
Expand All @@ -31,9 +31,7 @@ abstract class Emitter(
}
}

abstract fun Definition.emitName(): String

abstract fun notYetImplemented(): String
open fun Definition.emitName(): String = notYetImplemented()

open fun emit(ast: AST): List<Emitted> = ast
.map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ interface Emitters :
EndpointDefinitionEmitter,
UnionDefinitionEmitter,
IdentifierEmitter,
ChannelDefinitionEmitter
ChannelDefinitionEmitter,
NotYetImplemented

interface TypeDefinitionEmitter {
fun emit(type: Type, ast: AST): String
Expand Down Expand Up @@ -45,3 +46,8 @@ interface ChannelDefinitionEmitter {
interface IdentifierEmitter {
fun emit(identifier: Identifier): String
}

interface NotYetImplemented {
val singleLineComment: String
fun notYetImplemented() = "$singleLineComment TODO(\"Not yet implemented\")\n"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package community.flock.wirespec.compiler.core.parse

import community.flock.wirespec.compiler.core.Value
import community.flock.wirespec.compiler.core.removeBackticks
import community.flock.wirespec.compiler.core.removeCommentMarkers
import kotlin.jvm.JvmInline

sealed interface Node
Expand Down Expand Up @@ -112,7 +113,11 @@ data class Channel(
) : Definition

@JvmInline
value class Comment(override val value: String) : Value<String>
value class Comment private constructor(override val value: String) : Value<String> {
companion object {
operator fun invoke(comment: String) = Comment(comment.removeCommentMarkers())
}
}

sealed class Identifier(name: String) : Value<String> {
override val value = name.removeBackticks()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,17 @@ class UtilsTest {
"java.util.List<String>".concatGenerics() shouldBe "ListString"
}

@Test
fun testRemoveCommentMarkers() {
"/* Simple comment */".removeCommentMarkers() shouldBe "Simple comment"
"/* Padded */".removeCommentMarkers() shouldBe "Padded"
"/**/".removeCommentMarkers() shouldBe ""

"""/* Multiple
| Lines
| Here */""".trimMargin().removeCommentMarkers() shouldBe """Multiple
| Lines
| Here""".trimMargin()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ class ParseTest {
@Test
fun testParserWithDoubleComment() {
val source = """
|/**
| * This is comment 1
|/*
| This is comment 1
| */
|type Foo {
| foo: String
|}
|
|/**
| * This is comment 2
|/*
| This is comment 2
| */
|type Bar {
| bar: String
Expand All @@ -102,8 +102,8 @@ class ParseTest {
WirespecSpec.tokenize(source)
.let(parser()::parse)
.shouldBeRight().filterIsInstance<Definition>().map { it.comment?.value } shouldBe listOf(
"/**\n * This is comment 1\n */",
"/**\n * This is comment 2\n */",
"This is comment 1",
"This is comment 2",
)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package community.flock.wirespec.openapi

import community.flock.wirespec.compiler.core.emit.common.Emitter.Companion.firstToUpper
import kotlinx.serialization.json.Json

object Common {
fun className(vararg arg: String) = arg
Expand All @@ -9,4 +10,8 @@ object Common {

fun <K, V> Map<K, V?>.filterNotNullValues(): Map<K, V> =
mapNotNull { (key, value) -> value?.let { key to it } }.toMap()

val json = Json {
prettyPrint = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,46 @@ import community.flock.kotlinx.openapi.bindings.v2.SchemaObject
import community.flock.kotlinx.openapi.bindings.v2.SchemaOrReferenceObject
import community.flock.kotlinx.openapi.bindings.v2.StatusCode
import community.flock.kotlinx.openapi.bindings.v2.SwaggerObject
import community.flock.wirespec.compiler.core.emit.common.Emitted
import community.flock.wirespec.compiler.core.emit.common.Emitter
import community.flock.wirespec.compiler.core.parse.AST
import community.flock.wirespec.compiler.core.parse.Channel
import community.flock.wirespec.compiler.core.parse.Endpoint
import community.flock.wirespec.compiler.core.parse.Enum
import community.flock.wirespec.compiler.core.parse.Field
import community.flock.wirespec.compiler.core.parse.Identifier
import community.flock.wirespec.compiler.core.parse.Reference
import community.flock.wirespec.compiler.core.parse.Refined
import community.flock.wirespec.compiler.core.parse.Type
import community.flock.wirespec.compiler.core.parse.Union
import community.flock.wirespec.compiler.utils.noLogger
import community.flock.wirespec.openapi.Common.json
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonPrimitive
import community.flock.kotlinx.openapi.bindings.v2.Type as OpenApiType

object OpenApiV2Emitter {
object OpenApiV2Emitter: Emitter(noLogger) {

fun emit(ast: AST): SwaggerObject =
override val singleLineComment = ""

override fun emit(ast: AST): List<Emitted> =
listOf(Emitted("SwaggerObject", json.encodeToString(emitSwaggerObject(ast))))

override fun emit(type: Type, ast: AST) = notYetImplemented()

override fun emit(enum: Enum) = notYetImplemented()

override fun emit(refined: Refined) = notYetImplemented()

override fun emit(endpoint: Endpoint) = notYetImplemented()

override fun emit(union: Union) = notYetImplemented()

override fun emit(identifier: Identifier) = notYetImplemented()

override fun emit(channel: Channel) = notYetImplemented()

fun emitSwaggerObject(ast: AST): SwaggerObject =
SwaggerObject(
swagger = "2.0",
info = InfoObject(
Expand Down Expand Up @@ -83,6 +110,7 @@ object OpenApiV2Emitter {

private fun Endpoint.emit() = OperationObject(
operationId = identifier.value,
description = comment?.value,
consumes = requests.mapNotNull { it.content?.type }.distinct().ifEmpty { null },
produces = responses.mapNotNull { it.content?.type }.distinct().ifEmpty { null },
parameters = requests
Expand All @@ -103,7 +131,7 @@ object OpenApiV2Emitter {
responses = responses
.associate { response ->
StatusCode(response.status) to ResponseObject(
description = "$identifier ${response.status} response",
description = comment?.value ?: "${identifier.value} ${response.status} response",
headers = response.headers.associate {
it.identifier.value to HeaderObject(
type = it.reference.value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,51 @@ import community.flock.kotlinx.openapi.bindings.v3.ResponseObject
import community.flock.kotlinx.openapi.bindings.v3.SchemaObject
import community.flock.kotlinx.openapi.bindings.v3.SchemaOrReferenceObject
import community.flock.kotlinx.openapi.bindings.v3.StatusCode
import community.flock.wirespec.compiler.core.emit.common.Emitted
import community.flock.wirespec.compiler.core.emit.common.Emitter
import community.flock.wirespec.compiler.core.parse.AST
import community.flock.wirespec.compiler.core.parse.Channel
import community.flock.wirespec.compiler.core.parse.Definition
import community.flock.wirespec.compiler.core.parse.Endpoint
import community.flock.wirespec.compiler.core.parse.Enum
import community.flock.wirespec.compiler.core.parse.Field
import community.flock.wirespec.compiler.core.parse.Identifier
import community.flock.wirespec.compiler.core.parse.Reference
import community.flock.wirespec.compiler.core.parse.Refined
import community.flock.wirespec.compiler.core.parse.Type
import community.flock.wirespec.compiler.core.parse.Union
import community.flock.wirespec.compiler.utils.noLogger
import community.flock.wirespec.openapi.Common.json
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonPrimitive
import community.flock.kotlinx.openapi.bindings.v3.Type as OpenApiType

object OpenApiV3Emitter {
object OpenApiV3Emitter : Emitter(noLogger) {
data class Options(
val title: String,
val version: String
val version: String,
)

fun emit(ast: AST, options: Options? = null) = OpenAPIObject(
override val singleLineComment = ""

override fun emit(ast: AST): List<Emitted> =
listOf(Emitted("OpenAPIObject", json.encodeToString(emitOpenAPIObject(ast, null))))

override fun emit(type: Type, ast: AST) = notYetImplemented()

override fun emit(enum: Enum) = notYetImplemented()

override fun emit(refined: Refined) = notYetImplemented()

override fun emit(endpoint: Endpoint) = notYetImplemented()

override fun emit(union: Union) = notYetImplemented()

override fun emit(identifier: Identifier) = notYetImplemented()

override fun emit(channel: Channel) = notYetImplemented()

fun emitOpenAPIObject(ast: AST, options: Options? = null) = OpenAPIObject(
openapi = "3.0.0",
info = InfoObject(
title = options?.title ?: "Wirespec",
Expand Down Expand Up @@ -91,7 +116,7 @@ object OpenApiV3Emitter {

private fun Type.emit(): SchemaObject =
SchemaObject(

description = comment?.value,
properties = shape.value.associate { it.emitSchema() },
required = shape.value
.filter { !it.isNullable }
Expand All @@ -101,12 +126,14 @@ object OpenApiV3Emitter {

private fun Enum.emit(): SchemaObject =
SchemaObject(
description = comment?.value,
type = OpenApiType.STRING,
enum = entries.map { JsonPrimitive(it) }
)

private fun Union.emit(): SchemaObject =
SchemaObject(
description = comment?.value,
type = OpenApiType.STRING,
oneOf = entries.map { it.emitSchema() }
)
Expand All @@ -116,6 +143,7 @@ object OpenApiV3Emitter {

private fun Endpoint.emit(): OperationObject = OperationObject(
operationId = identifier.value,
description = comment?.value,
parameters = path.filterIsInstance<Endpoint.Segment.Param>()
.map { it.emitParameter() } + queries.map { it.emitParameter(ParameterLocation.QUERY) } + headers.map {
it.emitParameter(
Expand All @@ -131,7 +159,7 @@ object OpenApiV3Emitter {
.map { (statusCode, res) ->
StatusCode(statusCode) to ResponseObject(
headers = res.flatMap { it.headers }.associate { it.emitHeader() },
description = "$identifier $statusCode response",
description = "${identifier.value} $statusCode response",
content = res
.mapNotNull { it.content }
.associate { it.emit() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class OpenApiV2EmitterTest {
val petstoreOpenAPi = OpenAPI.decodeFromString(petstoreJson)
val petstoreAst = petstoreOpenAPi.parse()

val petstoreConvertedOpenApi = OpenApiV2Emitter.emit(petstoreAst)
val petstoreConvertedOpenApi = OpenApiV2Emitter.emitSwaggerObject(petstoreAst)
val petstoreConvertedOpenAPiAst = petstoreConvertedOpenApi.parse()

assertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class OpenApiV3EmitterTest {
val petstoreOpenAPi = OpenAPI.decodeFromString(petstoreJson)
val petstoreAst = petstoreOpenAPi.parse()

val petstoreConvertedOpenAPi = OpenApiV3Emitter.emit(petstoreAst)
val petstoreConvertedOpenAPi = OpenApiV3Emitter.emitOpenAPIObject(petstoreAst, null)
val petstoreConvertedOpenAPiAst = petstoreConvertedOpenAPi.parse()

assertEquals(petstoreAst, petstoreConvertedOpenAPiAst)
Expand Down
Loading

0 comments on commit 197b3f8

Please sign in to comment.