feat: v2-alpha (#112)

There are still some bugs, still some outstanding features, but I don't want to hold this back any longer, that way I can keep the future PRs much more focused
This commit is contained in:
Ryan Brink
2022-01-02 23:15:15 -05:00
committed by GitHub
parent d66880f9b2
commit c29567114d
229 changed files with 9172 additions and 7233 deletions

55
kompendium-oas/Module.md Normal file
View File

@ -0,0 +1,55 @@
# Module kompendium-oas
## Open Api Specification
This module contains the models that represent the Open Api Specification 3.0 (OAS).
It is a purely domain-based module, with no logic whatsoever.
The specification can be found [here](https://swagger.io/specification).
# Package io.bkbn.kompendium.oas
This is the root package that contains the top level spec that is ultimately serialized into the specification JSON
payload.
# Package io.bkbn.kompendium.oas.common
Here we house data models that will be used across the module.
# Package io.bkbn.kompendium.oas.component
This package correlates to the OAS Component layer, which at the moment is relatively bare bones. It will just contain a
reference to any security schemas, as adding objects here as components severely limits future ability to add cool
features such as route level object validations. Got issues with that, bring it up with the Open API Team :)
# Package io.bkbn.kompendium.oas.info
This package houses the data models for information metadata such as contact and licensing info
# Package io.bkbn.kompendium.oas.path
Now we're getting to the good stuff. This is where the details on each path level operation will live. Your `gets`,
your `puts`, so on and so forth.
# Package io.bkbn.kompendium.oas.payload
This is another good one, this is where the actual payload types live. Request and response body specifications,
parameter details, collection support. That all lives here.
# Package io.bkbn.kompendium.oas.schema
A bit confusingly, in the OAS, there is a distinction between a payload and a schema. You can think of payloads as
containing schemas. So here we dive into the true object level definitions that we want to map out. Models for
supporting collections, dictionaries, polymorphic classes, enums, along with your standard library classes all live
here.
# Package io.bkbn.kompendium.oas.security
Separated from the core schema models are the models that represent security schemas. Despite being referred to as
schemas, and despite living as part of the component data structure, these models are drastically different from your
core data model schemas, and thus earn their own package
# Package io.bkbn.kompendium.oas.server
Here we detail any server information that you wish to attach to your specification

View File

@ -0,0 +1,3 @@
plugins {
id("io.bkbn.sourdough.library")
}

View File

@ -0,0 +1,19 @@
package io.bkbn.kompendium.oas
import io.bkbn.kompendium.oas.common.ExternalDocumentation
import io.bkbn.kompendium.oas.common.Tag
import io.bkbn.kompendium.oas.component.Components
import io.bkbn.kompendium.oas.info.Info
import io.bkbn.kompendium.oas.path.Path
import io.bkbn.kompendium.oas.server.Server
data class OpenApiSpec(
val openapi: String = "3.0.3",
val info: Info,
val servers: MutableList<Server> = mutableListOf(),
val paths: MutableMap<String, Path> = mutableMapOf(),
val components: Components = Components(),
val security: MutableList<Map<String, List<String>>> = mutableListOf(),
val tags: MutableList<Tag> = mutableListOf(),
val externalDocs: ExternalDocumentation? = null
)

View File

@ -0,0 +1,8 @@
package io.bkbn.kompendium.oas.common
import java.net.URI
data class ExternalDocumentation(
val url: URI,
val description: String?
)

View File

@ -0,0 +1,7 @@
package io.bkbn.kompendium.oas.common
data class Tag(
val name: String,
val description: String? = null,
val externalDocs: ExternalDocumentation? = null
)

View File

@ -0,0 +1,7 @@
package io.bkbn.kompendium.oas.component
import io.bkbn.kompendium.oas.security.SecuritySchema
data class Components(
val securitySchemes: MutableMap<String, SecuritySchema> = mutableMapOf()
)

View File

@ -0,0 +1,9 @@
package io.bkbn.kompendium.oas.info
import java.net.URI
data class Contact(
var name: String,
var url: URI? = null,
var email: String? = null // TODO Enforce email?
)

View File

@ -0,0 +1,12 @@
package io.bkbn.kompendium.oas.info
import java.net.URI
data class Info(
var title: String? = null,
var version: String? = null,
var description: String? = null,
var termsOfService: URI? = null,
var contact: Contact? = null,
var license: License? = null
)

View File

@ -0,0 +1,8 @@
package io.bkbn.kompendium.oas.info
import java.net.URI
data class License(
var name: String,
var url: URI? = null
)

View File

@ -0,0 +1,17 @@
package io.bkbn.kompendium.oas.path
import io.bkbn.kompendium.oas.payload.Parameter
import io.bkbn.kompendium.oas.server.Server
data class Path(
var get: PathOperation? = null,
var put: PathOperation? = null,
var post: PathOperation? = null,
var delete: PathOperation? = null,
var options: PathOperation? = null,
var head: PathOperation? = null,
var patch: PathOperation? = null,
var trace: PathOperation? = null,
var servers: List<Server>? = null,
var parameters: List<Parameter>? = null
)

View File

@ -0,0 +1,25 @@
package io.bkbn.kompendium.oas.path
import io.bkbn.kompendium.oas.common.ExternalDocumentation
import io.bkbn.kompendium.oas.payload.Parameter
import io.bkbn.kompendium.oas.payload.Payload
import io.bkbn.kompendium.oas.payload.Request
import io.bkbn.kompendium.oas.payload.Response
import io.bkbn.kompendium.oas.server.Server
data class PathOperation(
var tags: Set<String> = emptySet(),
var summary: String? = null,
var description: String? = null,
var externalDocs: ExternalDocumentation? = null,
var operationId: String? = null,
var parameters: List<Parameter>? = null,
var requestBody: Request<*>? = null,
// TODO How to enforce `default` requirement 🧐
var responses: Map<Int, Response<*>>? = null,
var callbacks: Map<String, Payload>? = null, // todo what is this?
var deprecated: Boolean = false,
var security: List<Map<String, List<String>>>? = null,
var servers: List<Server>? = null,
var `x-codegen-request-body-name`: String? = null
)

View File

@ -0,0 +1,5 @@
package io.bkbn.kompendium.oas.payload
import io.bkbn.kompendium.oas.schema.ComponentSchema
data class AnyOfPayload(val anyOf: List<ComponentSchema>) : Payload

View File

@ -0,0 +1,10 @@
package io.bkbn.kompendium.oas.payload
import io.bkbn.kompendium.oas.schema.ComponentSchema
data class MediaType<T>(
val schema: ComponentSchema,
val examples: Map<String, Example<T>>? = null
) {
data class Example<T>(val value: T)
}

View File

@ -0,0 +1,15 @@
package io.bkbn.kompendium.oas.payload
import io.bkbn.kompendium.oas.schema.ComponentSchema
data class Parameter(
val name: String,
val `in`: String, // TODO Enum? "query", "header", "path" or "cookie"
val schema: ComponentSchema,
val description: String? = null,
val required: Boolean = true,
val deprecated: Boolean = false,
val allowEmptyValue: Boolean? = null,
val style: String? = null,
val explode: Boolean? = null
)

View File

@ -0,0 +1,3 @@
package io.bkbn.kompendium.oas.payload
sealed interface Payload

View File

@ -0,0 +1,7 @@
package io.bkbn.kompendium.oas.payload
data class Request<T>(
val description: String?,
val content: Map<String, MediaType<T>>,
val required: Boolean = false
) : Payload

View File

@ -0,0 +1,8 @@
package io.bkbn.kompendium.oas.payload
data class Response<T>(
val description: String? = null,
val headers: Map<String, Payload>? = null,
val content: Map<String, MediaType<T>>? = null,
val links: Map<String, Payload>? = null
) : Payload

View File

@ -0,0 +1,3 @@
package io.bkbn.kompendium.oas.schema
data class AnyOfSchema(val anyOf: List<ComponentSchema>, override val description: String? = null) : ComponentSchema

View File

@ -0,0 +1,14 @@
package io.bkbn.kompendium.oas.schema
data class ArraySchema(
val items: ComponentSchema,
override val default: Any? = null,
override val description: String? = null,
override val nullable: Boolean? = null,
// constraints
val minItems: Int? = null,
val maxItems: Int? = null,
val uniqueItems: Boolean? = null
) : TypedSchema {
override val type: String = "array"
}

View File

@ -0,0 +1,31 @@
package io.bkbn.kompendium.oas.schema
sealed interface ComponentSchema {
val description: String?
get() = null
val default: Any?
get() = null
fun addDefault(default: Any?): ComponentSchema = when (this) {
is AnyOfSchema -> error("Cannot add default to anyOf reference") // todo is this true though?
is ArraySchema -> this.copy(default = default)
is DictionarySchema -> this.copy(default = default)
is EnumSchema -> this.copy(default = default)
is FormattedSchema -> this.copy(default = default)
is ObjectSchema -> this.copy(default = default)
is SimpleSchema -> this.copy(default = default)
else -> error("Compiler bug??")
}
fun setDescription(description: String): ComponentSchema = when (this) {
is AnyOfSchema -> this.copy(description = description)
is ArraySchema -> this.copy(description = description)
is DictionarySchema -> this.copy(description = description)
is EnumSchema -> this.copy(description = description)
is FormattedSchema -> this.copy(description = description)
is ObjectSchema -> this.copy(description = description)
is SimpleSchema -> this.copy(description = description)
else -> error("Compiler bug??")
}
}

View File

@ -0,0 +1,10 @@
package io.bkbn.kompendium.oas.schema
data class DictionarySchema(
val additionalProperties: ComponentSchema,
override val default: Any? = null,
override val description: String? = null,
override val nullable: Boolean? = null
) : TypedSchema {
override val type: String = "object"
}

View File

@ -0,0 +1,10 @@
package io.bkbn.kompendium.oas.schema
data class EnumSchema(
val `enum`: Set<String>,
override val default: Any? = null,
override val description: String? = null,
override val nullable: Boolean? = null
) : TypedSchema {
override val type: String = "string"
}

View File

@ -0,0 +1,15 @@
package io.bkbn.kompendium.oas.schema
data class FormattedSchema(
val format: String,
override val type: String,
override val default: Any? = null,
override val description: String? = null,
override val nullable: Boolean? = null,
// Constraints
val minimum: Number? = null,
val maximum: Number? = null,
val exclusiveMinimum: Boolean? = null,
val exclusiveMaximum: Boolean? = null,
val multipleOf: Number? = null,
) : TypedSchema

View File

@ -0,0 +1,12 @@
package io.bkbn.kompendium.oas.schema
data class FreeFormSchema(
override val nullable: Boolean? = null,
// constraints
val minProperties: Int? = null,
val maxProperties: Int? = null
) : TypedSchema {
val additionalProperties: Boolean = true
override val type: String = "object"
override val default: Any? = null
}

View File

@ -0,0 +1,12 @@
package io.bkbn.kompendium.oas.schema
data class ObjectSchema(
val properties: Map<String, ComponentSchema>,
override val default: Any? = null,
override val description: String? = null,
override val nullable: Boolean? = null,
// constraints
val required: List<String>? = null
) : TypedSchema {
override val type = "object"
}

View File

@ -0,0 +1,13 @@
package io.bkbn.kompendium.oas.schema
data class SimpleSchema(
override val type: String,
override val default: Any? = null,
override val description: String? = null,
override val nullable: Boolean? = null,
// Constraints
val minLength: Int? = null,
val maxLength: Int? = null,
val pattern: String? = null,
val format: String? = null
) : TypedSchema

View File

@ -0,0 +1,7 @@
package io.bkbn.kompendium.oas.schema
sealed interface TypedSchema : ComponentSchema {
val type: String
val nullable: Boolean?
override val default: Any?
}

View File

@ -0,0 +1,18 @@
package io.bkbn.kompendium.oas.security
import java.util.Locale
// TODO... is there even an official ktor api auth mechanism??
@Suppress("UnusedPrivateMember")
class ApiKeyAuth(val `in`: ApiKeyLocation, name: String) : SecuritySchema {
val type: String = "apiKey"
enum class ApiKeyLocation {
HEADER,
QUERY,
COOKIE;
override fun toString(): String = name.lowercase(Locale.getDefault())
}
}

View File

@ -0,0 +1,6 @@
package io.bkbn.kompendium.oas.security
class BasicAuth : SecuritySchema {
val type: String = "http"
val scheme: String = "basic"
}

View File

@ -0,0 +1,6 @@
package io.bkbn.kompendium.oas.security
data class BearerAuth(val bearerFormat: String? = null): SecuritySchema {
val type: String = "http"
val scheme: String = "bearer"
}

View File

@ -0,0 +1,49 @@
package io.bkbn.kompendium.oas.security
data class OAuth(val description: String? = null, val flows: Flows) : SecuritySchema {
val type: String = "oauth2"
data class Flows(
val implicit: Implicit? = null,
val authorizationCode: AuthorizationCode? = null,
val password: Password? = null,
val clientCredentials: ClientCredential? = null,
) {
sealed interface Flow {
val authorizationUrl: String?
get() = null
val tokenUrl: String?
get() = null
val refreshUrl: String?
get() = null
val scopes: Map<String, String>
get() = emptyMap()
}
data class Implicit(
override val authorizationUrl: String,
override val refreshUrl: String? = null,
override val scopes: Map<String, String> = emptyMap()
) : Flow
data class AuthorizationCode(
override val authorizationUrl: String,
override val tokenUrl: String? = null,
override val refreshUrl: String? = null,
override val scopes: Map<String, String> = emptyMap()
) : Flow
data class Password(
override val tokenUrl: String? = null,
override val refreshUrl: String? = null,
override val scopes: Map<String, String> = emptyMap()
) : Flow
data class ClientCredential(
override val tokenUrl: String? = null,
override val refreshUrl: String? = null,
override val scopes: Map<String, String> = emptyMap()
) : Flow
}
}

View File

@ -0,0 +1,3 @@
package io.bkbn.kompendium.oas.security
sealed interface SecuritySchema

View File

@ -0,0 +1,9 @@
package io.bkbn.kompendium.oas.server
import java.net.URI
data class Server(
val url: URI,
val description: String? = null,
var variables: Map<String, ServerVariable>? = null
)

View File

@ -0,0 +1,7 @@
package io.bkbn.kompendium.oas.server
data class ServerVariable(
val `enum`: Set<String>, // todo enforce not empty
val default: String,
val description: String?
)