Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenAPI servers per endpoint #1636

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import scala.collection.immutable.ListMap
private[openapi] object EndpointToOpenAPIDocs {
def toOpenAPI(
api: Info,
es: Iterable[AnyEndpoint],
es: Iterable[EndpointWithDocsMetadata],
options: OpenAPIDocsOptions,
docsExtensions: List[DocsExtension[_]]
): OpenAPI = {
val es2 = es.filter(e => findWebSocket(e).isEmpty).map(nameAllPathCapturesInEndpoint)
val es2 = es
.filter(e => findWebSocket(e.endpoint).isEmpty)
.map(e => e.copy(endpoint = nameAllPathCapturesInEndpoint(e.endpoint)))
val toNamedSchemas = new ToNamedSchemas
val (keyToSchema, schemas) = new SchemasForEndpoints(es2, options.schemaName, toNamedSchemas).apply()
val securitySchemes = SecuritySchemesForEndpoints(es2)
val (keyToSchema, schemas) = new SchemasForEndpoints(es2.map(_.endpoint), options.schemaName, toNamedSchemas).apply()
val securitySchemes = SecuritySchemesForEndpoints(es2.map(_.endpoint))
val pathCreator = new EndpointToOpenAPIPaths(schemas, securitySchemes, options)
val componentsCreator = new EndpointToOpenAPIComponents(keyToSchema, securitySchemes)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ private[openapi] class EndpointToOpenAPIPaths(schemas: Schemas, securitySchemes:
private val codecToMediaType = new CodecToMediaType(schemas)
private val endpointToOperationResponse = new EndpointToOperationResponse(schemas, codecToMediaType, options)

def pathItem(e: AnyEndpoint): (String, PathItem) = {
def pathItem(endpointWithDocsMetadata: EndpointWithDocsMetadata): (String, PathItem) = {
import Method._

val e = endpointWithDocsMetadata.endpoint
val inputs = e.asVectorOfBasicInputs(includeAuth = false)
val pathComponents = namedPathComponents(inputs)
val method = e.method.getOrElse(Method.GET)

val defaultId = options.operationIdGenerator(e, pathComponents, method)

val operation = Some(endpointToOperation(defaultId, e, inputs))
val operation = Some(endpointToOperation(defaultId, endpointWithDocsMetadata, inputs))
val pathItem = PathItem(
get = if (method == GET) operation else None,
put = if (method == PUT) operation else None,
Expand All @@ -39,7 +40,12 @@ private[openapi] class EndpointToOpenAPIPaths(schemas: Schemas, securitySchemes:
(e.renderPathTemplate(renderQueryParam = None, includeAuth = false), pathItem)
}

private def endpointToOperation(defaultId: String, e: AnyEndpoint, inputs: Vector[EndpointInput.Basic[_]]): Operation = {
private def endpointToOperation(
defaultId: String,
endpointWithDocsMetadata: EndpointWithDocsMetadata,
inputs: Vector[EndpointInput.Basic[_]]
): Operation = {
val e = endpointWithDocsMetadata.endpoint
val parameters = operationParameters(inputs)
val body: Vector[ReferenceOr[RequestBody]] = operationInputBody(inputs)
val responses: ListMap[ResponsesKey, ReferenceOr[Response]] = endpointToOperationResponse(e)
Expand All @@ -54,6 +60,7 @@ private[openapi] class EndpointToOpenAPIPaths(schemas: Schemas, securitySchemes:
Responses(responses),
if (e.info.deprecated) Some(true) else None,
operationSecurity(e),
servers = endpointWithDocsMetadata.servers,
extensions = DocsExtensions.fromIterable(e.info.docsExtensions)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package sttp.tapir.docs.openapi
import sttp.tapir.AnyEndpoint
import sttp.tapir.openapi.Server

case class EndpointWithDocsMetadata(
endpoint: AnyEndpoint,
servers: List[Server] = Nil
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,71 @@ trait OpenAPIDocsInterpreter {
toOpenAPI(e, Info(title, version))

def toOpenAPI[A, I, E, O, R](e: Endpoint[A, I, E, O, R], info: Info): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(e), openAPIDocsOptions, List.empty)
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(EndpointWithDocsMetadata(e)), openAPIDocsOptions, List.empty)

def toOpenAPI[A, I, E, O, R](e: Endpoint[A, I, E, O, R], info: Info, docsExtensions: List[DocsExtension[_]]): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(e), openAPIDocsOptions, docsExtensions)
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(EndpointWithDocsMetadata(e)), openAPIDocsOptions, docsExtensions)

def toOpenAPI[R, F[_]](se: ServerEndpoint[R, F], title: String, version: String): OpenAPI =
toOpenAPI(se.endpoint, Info(title, version))

def toOpenAPI[R, F[_]](se: ServerEndpoint[R, F], info: Info): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(se.endpoint), openAPIDocsOptions, List.empty)
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(EndpointWithDocsMetadata(se.endpoint)), openAPIDocsOptions, List.empty)

def toOpenAPI[R, F[_]](
se: ServerEndpoint[R, F],
info: Info,
docsExtensions: List[DocsExtension[_]]
): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(se.endpoint), openAPIDocsOptions, docsExtensions)
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(EndpointWithDocsMetadata(se.endpoint)), openAPIDocsOptions, docsExtensions)

def toOpenAPI(e: EndpointWithDocsMetadata, title: String, version: String): OpenAPI =
toOpenAPI(e, Info(title, version))

def toOpenAPI(e: EndpointWithDocsMetadata, info: Info): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(e), openAPIDocsOptions, List.empty)

def toOpenAPI(
e: EndpointWithDocsMetadata,
info: Info,
docsExtensions: List[DocsExtension[_]]
): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, Seq(e), openAPIDocsOptions, docsExtensions)

def toOpenAPI(es: Iterable[AnyEndpoint], title: String, version: String): OpenAPI =
toOpenAPI(es, Info(title, version))

def toOpenAPI(es: Iterable[AnyEndpoint], info: Info): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, es, openAPIDocsOptions, List.empty)
EndpointToOpenAPIDocs.toOpenAPI(info, es.map(EndpointWithDocsMetadata(_)), openAPIDocsOptions, List.empty)

def toOpenAPI(es: Iterable[AnyEndpoint], info: Info, docsExtensions: List[DocsExtension[_]]): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, es, openAPIDocsOptions, docsExtensions)
EndpointToOpenAPIDocs.toOpenAPI(info, es.map(EndpointWithDocsMetadata(_)), openAPIDocsOptions, docsExtensions)

def serverEndpointsToOpenAPI[F[_]](ses: Iterable[ServerEndpoint[_, F]], title: String, version: String): OpenAPI =
serverEndpointsToOpenAPI(ses, Info(title, version))

def serverEndpointsToOpenAPI[F[_]](ses: Iterable[ServerEndpoint[_, F]], info: Info): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, ses.map(_.endpoint), openAPIDocsOptions, List.empty)
EndpointToOpenAPIDocs.toOpenAPI(info, ses.map(e => EndpointWithDocsMetadata(e.endpoint)), openAPIDocsOptions, List.empty)

def serverEndpointsToOpenAPI[F[_]](
ses: Iterable[ServerEndpoint[_, F]],
info: Info,
docsExtensions: List[DocsExtension[_]]
): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, ses.map(_.endpoint), openAPIDocsOptions, docsExtensions)
EndpointToOpenAPIDocs.toOpenAPI(info, ses.map(e => EndpointWithDocsMetadata(e.endpoint)), openAPIDocsOptions, docsExtensions)

def endpointsWithDocsMetadataToOpenAPI(es: Iterable[EndpointWithDocsMetadata], title: String, version: String): OpenAPI =
endpointsWithDocsMetadataToOpenAPI(es, Info(title, version))

def endpointsWithDocsMetadataToOpenAPI(es: Iterable[EndpointWithDocsMetadata], info: Info): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, es, openAPIDocsOptions, List.empty)

def endpointsWithDocsMetadataToOpenAPI(
es: Iterable[EndpointWithDocsMetadata],
info: Info,
docsExtensions: List[DocsExtension[_]]
): OpenAPI =
EndpointToOpenAPIDocs.toOpenAPI(info, es, openAPIDocsOptions, docsExtensions)
}

object OpenAPIDocsInterpreter {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package sttp.tapir.docs
import sttp.tapir.AnyEndpoint
import sttp.tapir.openapi.Server

package object openapi {

implicit class EndpointWithDocsMetadataImplicits(endpoint: AnyEndpoint) {
def withServers(servers: List[Server]): EndpointWithDocsMetadata = EndpointWithDocsMetadata(endpoint, servers)
def withoutServers(): EndpointWithDocsMetadata = EndpointWithDocsMetadata(endpoint)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package sttp.tapir.swagger.bundle

import sttp.tapir.{AnyEndpoint, DocsExtension}
import sttp.tapir.docs.openapi.{OpenAPIDocsInterpreter, OpenAPIDocsOptions}
import sttp.tapir.docs.openapi.{EndpointWithDocsMetadata, OpenAPIDocsInterpreter, OpenAPIDocsOptions}
import sttp.tapir.openapi.Info
import sttp.tapir.openapi.circe.yaml._
import sttp.tapir.server.ServerEndpoint
Expand All @@ -19,7 +19,7 @@ trait SwaggerInterpreter {
info: Info
): List[ServerEndpoint[Any, F]] = {
val yaml = OpenAPIDocsInterpreter(docsOptions).toOpenAPI(endpoints, info).toYaml
SwaggerUI(yaml, prefix, yamlName, basePrefix)
swaggerUI[F](yaml)
}

def fromEndpoints[F[_]](
Expand All @@ -40,6 +40,22 @@ trait SwaggerInterpreter {
version: String
): List[ServerEndpoint[Any, F]] =
fromEndpoints(endpoints.map(_.endpoint), Info(title, version))

def fromEndpointsWithDocsMetadata[F[_]](
endpoints: List[EndpointWithDocsMetadata],
info: Info
): List[ServerEndpoint[Any, F]] = {
val yaml = OpenAPIDocsInterpreter(docsOptions).endpointsWithDocsMetadataToOpenAPI(endpoints, info).toYaml
swaggerUI[F](yaml)
}

def fromEndpointsWithDocsMetadata[F[_]](
endpoints: List[EndpointWithDocsMetadata],
title: String,
version: String
): List[ServerEndpoint[Any, F]] = fromEndpointsWithDocsMetadata(endpoints, Info(title, version))

private def swaggerUI[F[_]](yaml: String): List[ServerEndpoint[Any, F]] = SwaggerUI(yaml, prefix, yamlName, basePrefix)
}

object SwaggerInterpreter {
Expand Down