diff --git a/kompendium/build.gradle.kts b/kompendium/build.gradle.kts index e065cdfe4..839d16d05 100644 --- a/kompendium/build.gradle.kts +++ b/kompendium/build.gradle.kts @@ -8,4 +8,5 @@ dependencies { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") testImplementation("org.jetbrains.kotlin:kotlin-test") testImplementation("org.jetbrains.kotlin:kotlin-test-junit") + testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.12.0") } diff --git a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfo.kt b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfo.kt index 5987b3dc2..0789e5e42 100644 --- a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfo.kt +++ b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfo.kt @@ -6,7 +6,7 @@ data class OpenApiSpecInfo( val title: String, val version: String, val description: String?, - val termsOfService: URI?, - val contact: OpenApiSpecInfoContact?, - val license: OpenApiSpecInfoLicense? + val termsOfService: URI? = null, + val contact: OpenApiSpecInfoContact? = null, + val license: OpenApiSpecInfoLicense? = null ) diff --git a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoContact.kt b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoContact.kt index c8d57eea5..a478ea1be 100644 --- a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoContact.kt +++ b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecInfoContact.kt @@ -4,6 +4,6 @@ import java.net.URI data class OpenApiSpecInfoContact( val name: String, - val url: URI?, - val email: String? // TODO Enforce email + val url: URI? = null, + val email: String? = null // TODO Enforce email? ) diff --git a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecMediaType.kt b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecMediaType.kt index 2d09bdb1c..b73369226 100644 --- a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecMediaType.kt +++ b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecMediaType.kt @@ -2,8 +2,8 @@ package org.leafygreens.kompendium.models // TODO Oof -> https://swagger.io/specification/#media-type-object data class OpenApiSpecMediaType( - val schema: String, // TODO sheesh -> https://swagger.io/specification/#schema-object needs to be referencable - val example: String, // TODO Enforce type? then serialize? - val examples: Map, // needs to be mutually exclusive with example - val encoding: Map // todo encoding object -> https://swagger.io/specification/#encoding-object + val schema: OpenApiSpecReferencable, // TODO sheesh -> https://swagger.io/specification/#schema-object needs to be referencable + val example: String? = null, // TODO Enforce type? then serialize? + val examples: Map? = null, // needs to be mutually exclusive with example + val encoding: Map? = null // todo encoding object -> https://swagger.io/specification/#encoding-object ) diff --git a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt index da9e9bc58..3bef065b2 100644 --- a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt +++ b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItem.kt @@ -1,17 +1,17 @@ package org.leafygreens.kompendium.models data class OpenApiSpecPathItem( - val `$ref`: String?, // TODO Maybe drop this? - val summary: String?, - val description: String?, - val get: OpenApiSpecPathItemOperation?, - val put: OpenApiSpecPathItemOperation?, - val post: OpenApiSpecPathItemOperation?, - val delete: OpenApiSpecPathItemOperation?, - val options: OpenApiSpecPathItemOperation?, - val head: OpenApiSpecPathItemOperation?, - val patch: OpenApiSpecPathItemOperation?, - val trace: OpenApiSpecPathItemOperation?, - val servers: List = emptyList(), - val parameters: List = emptyList() + // val `$ref`: String?, // TODO need example of this... or just make whole thing referencable? + val summary: String? = null, + val description: String? = null, + val get: OpenApiSpecPathItemOperation? = null, + val put: OpenApiSpecPathItemOperation? = null, + val post: OpenApiSpecPathItemOperation? = null, + val delete: OpenApiSpecPathItemOperation? = null, + val options: OpenApiSpecPathItemOperation? = null, + val head: OpenApiSpecPathItemOperation? = null, + val patch: OpenApiSpecPathItemOperation? = null, + val trace: OpenApiSpecPathItemOperation? = null, + val servers: List? = null, + val parameters: List? = null ) diff --git a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt index f56606d09..8c6af4193 100644 --- a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt +++ b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecPathItemOperation.kt @@ -2,15 +2,16 @@ package org.leafygreens.kompendium.models data class OpenApiSpecPathItemOperation( val tags: Set = emptySet(), - val summary: String?, - val description: String?, - val externalDocs: OpenApiSpecExternalDocumentation?, - val operationId: String?, - val parameters: List = emptyList(), - val requestBody: OpenApiSpecReferencable, - val responses: Map, // TODO How to enforce `default` requirement - val callbacks: Map, + val summary: String? = null, + val description: String? = null, + val externalDocs: OpenApiSpecExternalDocumentation? = null, + val operationId: String? = null, + val parameters: List? = null, + val requestBody: OpenApiSpecReferencable? = null, + val responses: Map? = null, // TODO How to enforce `default` requirement + val callbacks: Map? = null, val deprecated: Boolean = false, - val security: Map, // todo needs to reference objects in the security scheme 🤔 - val servers: List + val security: List>>? = null, // todo big yikes... also needs to reference objects in the security scheme 🤔 + val servers: List? = null, + val `x-codegen-request-body-name`: String? = null ) diff --git a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecReferencable.kt b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecReferencable.kt index 18131c8d2..206a3ec99 100644 --- a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecReferencable.kt +++ b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecReferencable.kt @@ -9,10 +9,10 @@ data class OpenApiSpecCallback( ) : OpenApiSpecReferencable() data class OpenApiSpecResponse( - val description: String, - val headers: Map, - val content: Map, - val links: Map + val description: String? = null, + val headers: Map? = null, + val content: Map? = null, + val links: Map? = null ) : OpenApiSpecReferencable() data class OpenApiSpecHeader( @@ -29,3 +29,9 @@ data class OpenApiSpecParameter( val deprecated: Boolean = false, val allowEmptyValue: Boolean = false ) : OpenApiSpecReferencable() + +data class OpenApiSpecRequest( + val description: String?, + val content: Map, + val required: Boolean = false +) : OpenApiSpecReferencable() diff --git a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecRequest.kt b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecRequest.kt deleted file mode 100644 index f2721ed1b..000000000 --- a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecRequest.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.leafygreens.kompendium.models - -data class OpenApiSpecRequest( - val description: String?, - val content: Map, - val required: Boolean = false -) diff --git a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServer.kt b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServer.kt index 6b544448d..60fa659bb 100644 --- a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServer.kt +++ b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecServer.kt @@ -1,7 +1,9 @@ package org.leafygreens.kompendium.models +import java.net.URI + data class OpenApiSpecServer( - val url: String, - val description: String?, - var variables: Map? + val url: URI, + val description: String? = null, + var variables: Map? = null ) diff --git a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTag.kt b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTag.kt index 97a779c02..21c8c11e5 100644 --- a/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTag.kt +++ b/kompendium/src/main/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTag.kt @@ -2,6 +2,6 @@ package org.leafygreens.kompendium.models data class OpenApiSpecTag( val name: String, - val description: String?, - val externalDocs: OpenApiSpecExternalDocumentation? + val description: String? = null, + val externalDocs: OpenApiSpecExternalDocumentation? = null ) diff --git a/kompendium/src/test/kotlin/kompendium/LibraryTest.kt b/kompendium/src/test/kotlin/kompendium/LibraryTest.kt index f732fe90f..0aff026b5 100644 --- a/kompendium/src/test/kotlin/kompendium/LibraryTest.kt +++ b/kompendium/src/test/kotlin/kompendium/LibraryTest.kt @@ -5,7 +5,6 @@ package kompendium import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertTrue class LibraryTest { @Test fun testSomeLibraryMethod() { diff --git a/kompendium/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt b/kompendium/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt new file mode 100644 index 000000000..b130eb281 --- /dev/null +++ b/kompendium/src/test/kotlin/org/leafygreens/kompendium/KompendiumTest.kt @@ -0,0 +1,14 @@ +package org.leafygreens.kompendium + +import kotlin.test.Test +import kotlin.test.assertEquals + +internal class KompendiumTest { + + @Test + fun `Kompendium can be instantiated with no details`() { + val kompendium = Kompendium() + assertEquals(kompendium.spec.openapi, "3.0.3", "Kompendium has a default spec version of 3.0.3") + } + +} diff --git a/kompendium/src/test/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTest.kt b/kompendium/src/test/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTest.kt new file mode 100644 index 000000000..cb17d3681 --- /dev/null +++ b/kompendium/src/test/kotlin/org/leafygreens/kompendium/models/OpenApiSpecTest.kt @@ -0,0 +1,27 @@ +package org.leafygreens.kompendium.models + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import kotlin.test.Test +import kotlin.test.assertEquals +import org.leafygreens.kompendium.util.TestData + +internal class OpenApiSpecTest { + + private val mapper = jacksonObjectMapper() + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .writerWithDefaultPrettyPrinter() + + @Test + fun `OpenApiSpec can be serialized into a valid Open API Spec`() { + // when + val spec = TestData.testSpec + + // do + val json = mapper.writeValueAsString(spec) + + // expect + val expected = TestData.getFileSnapshot("petstore.json").trim() + assertEquals(expected, json, "Should serialize an empty spec") + } +} diff --git a/kompendium/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt b/kompendium/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt new file mode 100644 index 000000000..2741a47dd --- /dev/null +++ b/kompendium/src/test/kotlin/org/leafygreens/kompendium/util/TestData.kt @@ -0,0 +1,145 @@ +package org.leafygreens.kompendium.util + +import java.io.File +import java.net.URI +import org.leafygreens.kompendium.models.OpenApiSpec +import org.leafygreens.kompendium.models.OpenApiSpecExternalDocumentation +import org.leafygreens.kompendium.models.OpenApiSpecInfo +import org.leafygreens.kompendium.models.OpenApiSpecInfoContact +import org.leafygreens.kompendium.models.OpenApiSpecInfoLicense +import org.leafygreens.kompendium.models.OpenApiSpecMediaType +import org.leafygreens.kompendium.models.OpenApiSpecPathItem +import org.leafygreens.kompendium.models.OpenApiSpecPathItemOperation +import org.leafygreens.kompendium.models.OpenApiSpecReferenceObject +import org.leafygreens.kompendium.models.OpenApiSpecRequest +import org.leafygreens.kompendium.models.OpenApiSpecResponse +import org.leafygreens.kompendium.models.OpenApiSpecServer +import org.leafygreens.kompendium.models.OpenApiSpecTag + +object TestData { + fun getFileSnapshot(fileName: String): String { + val snapshotPath = "src/test/resources" + val file = File("$snapshotPath/$fileName") + return file.readText() + } + + val testSpec = OpenApiSpec( + info = OpenApiSpecInfo( + title = "Swagger Petstore", + description = "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + termsOfService = URI("http://swagger.io/terms/"), + contact = OpenApiSpecInfoContact( + name = "Team Swag", + email = "apiteam@swagger.io" + ), + license = OpenApiSpecInfoLicense( + name = "Apache 2.0", + url = URI("http://www.apache.org/licenses/LICENSE-2.0.html") + ), + version = "1.0.0" + ), + externalDocs = OpenApiSpecExternalDocumentation( + description = "Find out more about Swagger", + url = URI("http://swagger.io") + ), + servers = listOf( + OpenApiSpecServer( + url = URI("https://petstore.swagger.io/v2") + ), + OpenApiSpecServer( + url = URI("http://petstore.swagger.io/v2") + ) + ), + tags = listOf( + OpenApiSpecTag( + name = "pet", + description = "Everything about your Pets", + externalDocs = OpenApiSpecExternalDocumentation( + description = "Find out more", + url = URI("http://swagger.io") + ) + ), + OpenApiSpecTag( + name = "store", + description = "Access to Petstore orders" + ), + OpenApiSpecTag( + name = "user", + description = "Operations about user", + externalDocs = OpenApiSpecExternalDocumentation( + description = "Find out more about our store", + url = URI("http://swagger.io") + ) + ) + ), + paths = mapOf( + "/pet" to OpenApiSpecPathItem( + put = OpenApiSpecPathItemOperation( + tags = setOf("pet"), + summary = "Update an existing pet", + operationId = "updatePet", + requestBody = OpenApiSpecRequest( + description = "Pet object that needs to be added to the store", + content = mapOf( + "application/json" to OpenApiSpecMediaType( + schema = OpenApiSpecReferenceObject(`$ref` = "#/components/schemas/Pet") + ), + "application/xml" to OpenApiSpecMediaType( + schema = OpenApiSpecReferenceObject(`$ref` = "#/components/schemas/Pet") + ) + ), + required = true + ), + responses = mapOf( + "400" to OpenApiSpecResponse( + description = "Invalid ID supplied", + content = emptyMap() + ), + "404" to OpenApiSpecResponse( + description = "Pet not found", + content = emptyMap() + ), + "405" to OpenApiSpecResponse( + description = "Validation exception", + content = emptyMap() + ) + ), + security = listOf( + mapOf( + "petstore_auth" to listOf("write:pets", "read:pets") + ) + ), + `x-codegen-request-body-name` = "body" + ), + post = OpenApiSpecPathItemOperation( + tags = setOf("pet"), + summary = "Add a new pet to the store", + operationId = "addPet", + requestBody = OpenApiSpecRequest( + description = "Pet object that needs to be added to the store", + content = mapOf( + "application/json" to OpenApiSpecMediaType( + schema = OpenApiSpecReferenceObject(`$ref` = "#/components/schemas/Pet") + ), + "application/xml" to OpenApiSpecMediaType( + schema = OpenApiSpecReferenceObject(`$ref` = "#/components/schemas/Pet") + ) + ) + ), + responses = mapOf( + "405" to OpenApiSpecResponse( + description = "Invalid Input", + content = emptyMap() + ) + ), + security = listOf( + mapOf( + "petstore_auth" to listOf("write:pets", "read:pets") + ) + ), + `x-codegen-request-body-name` = "body" + ) + ) + ) + ) +} diff --git a/kompendium/src/test/resources/petstore.json b/kompendium/src/test/resources/petstore.json new file mode 100644 index 000000000..9ef6c103d --- /dev/null +++ b/kompendium/src/test/resources/petstore.json @@ -0,0 +1,121 @@ +{ + "openapi" : "3.0.3", + "info" : { + "title" : "Swagger Petstore", + "version" : "1.0.0", + "description" : "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "termsOfService" : "http://swagger.io/terms/", + "contact" : { + "name" : "Team Swag", + "email" : "apiteam@swagger.io" + }, + "license" : { + "name" : "Apache 2.0", + "url" : "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "servers" : [ { + "url" : "https://petstore.swagger.io/v2" + }, { + "url" : "http://petstore.swagger.io/v2" + } ], + "paths" : { + "/pet" : { + "put" : { + "tags" : [ "pet" ], + "summary" : "Update an existing pet", + "operationId" : "updatePet", + "requestBody" : { + "description" : "Pet object that needs to be added to the store", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Pet" + } + }, + "application/xml" : { + "schema" : { + "$ref" : "#/components/schemas/Pet" + } + } + }, + "required" : true + }, + "responses" : { + "400" : { + "description" : "Invalid ID supplied", + "content" : { } + }, + "404" : { + "description" : "Pet not found", + "content" : { } + }, + "405" : { + "description" : "Validation exception", + "content" : { } + } + }, + "deprecated" : false, + "security" : [ { + "petstore_auth" : [ "write:pets", "read:pets" ] + } ], + "x-codegen-request-body-name" : "body" + }, + "post" : { + "tags" : [ "pet" ], + "summary" : "Add a new pet to the store", + "operationId" : "addPet", + "requestBody" : { + "description" : "Pet object that needs to be added to the store", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/Pet" + } + }, + "application/xml" : { + "schema" : { + "$ref" : "#/components/schemas/Pet" + } + } + }, + "required" : false + }, + "responses" : { + "405" : { + "description" : "Invalid Input", + "content" : { } + } + }, + "deprecated" : false, + "security" : [ { + "petstore_auth" : [ "write:pets", "read:pets" ] + } ], + "x-codegen-request-body-name" : "body" + } + } + }, + "security" : { }, + "tags" : [ { + "name" : "pet", + "description" : "Everything about your Pets", + "externalDocs" : { + "url" : "http://swagger.io", + "description" : "Find out more" + } + }, { + "name" : "store", + "description" : "Access to Petstore orders" + }, { + "name" : "user", + "description" : "Operations about user", + "externalDocs" : { + "url" : "http://swagger.io", + "description" : "Find out more about our store" + } + } ], + "externalDocs" : { + "url" : "http://swagger.io", + "description" : "Find out more about Swagger" + } +}