Skip to content

Commit

Permalink
More OpenAPI generation tests
Browse files Browse the repository at this point in the history
  • Loading branch information
987Nabil committed Oct 21, 2023
1 parent c4c8ecc commit 0143477
Show file tree
Hide file tree
Showing 3 changed files with 559 additions and 61 deletions.
11 changes: 7 additions & 4 deletions zio-http/src/main/scala/zio/http/endpoint/openapi/OpenAPI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,15 @@ object OpenAPI {
externalDocs = None,
)

implicit val statusSchema: Schema[Status] =
implicit def statusSchema: Schema[Status] =
zio.schema
.Schema[String]
.transformOrFail[Status](
s => Status.fromInt(s.toInt).toRight("Invalid Status"),
p => Right(p.text),
)

implicit val pathMapSchema: Schema[Map[Path, PathItem]] =
implicit def pathMapSchema: Schema[Map[Path, PathItem]] =
DeriveSchema
.gen[Map[String, PathItem]]
.transformOrFail(
Expand Down Expand Up @@ -206,7 +206,7 @@ object OpenAPI {
(m: Map[StatusOrDefault, T]) => Right(m.map { case (k, v) => k.text -> v }),
)

implicit val mediaTypeTupleSchema: Schema[(String, MediaType)] =
implicit def mediaTypeTupleSchema: Schema[(String, MediaType)] =
zio.schema
.Schema[Map[String, MediaType]]
.transformOrFail(
Expand Down Expand Up @@ -418,7 +418,10 @@ object OpenAPI {
case class Path private (name: String)

object Path {
implicit val schema: Schema[Path] = DeriveSchema.gen[Path]
implicit val schema: Schema[Path] = Schema[String].transformOrFail[Path](
s => fromString(s).toRight(s"Invalid Path $s"),
p => Right(p.name),
)

// todo maybe not the best regex, but the old one was not working at all
val validPath: Regex = """/[/a-zA-Z0-9\-_{}]*""".r
Expand Down
52 changes: 24 additions & 28 deletions zio-http/src/main/scala/zio/http/endpoint/openapi/OpenAPIGen.scala
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
package zio.http.endpoint.openapi

import java.util.UUID

import scala.annotation.tailrec
import scala.collection.{immutable, mutable}

import zio.Chunk
import zio.json.EncoderOps
import zio.json.ast.Json

import zio.schema.Schema.Record
import zio.schema.codec.JsonCodec
import zio.schema.{Schema, TypeId}

import zio.http._
import zio.http.codec.HttpCodec.Metadata
import zio.http.codec._
import zio.http.endpoint.openapi.JsonSchema.SchemaStyle
import zio.http.endpoint.{Endpoint, EndpointMiddleware}
import zio.http.endpoint._

object OpenAPIGen {
private val PathWildcard = "pathWildcard"
Expand Down Expand Up @@ -154,7 +150,7 @@ object OpenAPIGen {
.flatMap(_.docsOpt)
.map(_.flattened)
.reduceOption(_ intersect _)
.map(_.reduce(_ + _))
.flatMap(_.reduceOption(_ + _))
.getOrElse(Doc.empty)

def optimize: AtomizedMetaCodecs =
Expand Down Expand Up @@ -390,50 +386,50 @@ object OpenAPIGen {
.toOption
.get

def fromEndpoints[PathInput, Input, Err, Output, Middleware <: EndpointMiddleware](
endpoint1: Endpoint[PathInput, Input, Err, Output, Middleware],
endpoints: Endpoint[PathInput, Input, Err, Output, Middleware]*,
def fromEndpoints(
endpoint1: Endpoint[_, _, _, _, _],
endpoints: Endpoint[_, _, _, _, _]*,
): OpenAPI = fromEndpoints(endpoint1 +: endpoints)

def fromEndpoints[PathInput, Input, Err, Output, Middleware <: EndpointMiddleware](
def fromEndpoints(
title: String,
version: String,
endpoint1: Endpoint[PathInput, Input, Err, Output, Middleware],
endpoints: Endpoint[PathInput, Input, Err, Output, Middleware]*,
endpoint1: Endpoint[_, _, _, _, _],
endpoints: Endpoint[_, _, _, _, _]*,
): OpenAPI = fromEndpoints(title, version, endpoint1 +: endpoints)

def fromEndpoints[PathInput, Input, Err, Output, Middleware <: EndpointMiddleware](
def fromEndpoints(
title: String,
version: String,
referenceType: SchemaStyle,
endpoint1: Endpoint[PathInput, Input, Err, Output, Middleware],
endpoints: Endpoint[PathInput, Input, Err, Output, Middleware]*,
endpoint1: Endpoint[_, _, _, _, _],
endpoints: Endpoint[_, _, _, _, _]*,
): OpenAPI = fromEndpoints(title, version, referenceType, endpoint1 +: endpoints)

def fromEndpoints[PathInput, Input, Err, Output, Middleware <: EndpointMiddleware](
def fromEndpoints(
referenceType: SchemaStyle,
endpoints: Iterable[Endpoint[PathInput, Input, Err, Output, Middleware]],
endpoints: Iterable[Endpoint[_, _, _, _, _]],
): OpenAPI = if (endpoints.isEmpty) OpenAPI.empty else endpoints.map(gen(_, referenceType)).reduce(_ ++ _)

def fromEndpoints[PathInput, Input, Err, Output, Middleware <: EndpointMiddleware](
endpoints: Iterable[Endpoint[PathInput, Input, Err, Output, Middleware]],
def fromEndpoints(
endpoints: Iterable[Endpoint[_, _, _, _, _]],
): OpenAPI = if (endpoints.isEmpty) OpenAPI.empty else endpoints.map(gen(_, SchemaStyle.Compact)).reduce(_ ++ _)

def fromEndpoints[PathInput, Input, Err, Output, Middleware <: EndpointMiddleware](
def fromEndpoints(
title: String,
version: String,
endpoints: Iterable[Endpoint[PathInput, Input, Err, Output, Middleware]],
endpoints: Iterable[Endpoint[_, _, _, _, _]],
): OpenAPI = fromEndpoints(endpoints).title(title).version(version)

def fromEndpoints[PathInput, Input, Err, Output, Middleware <: EndpointMiddleware](
def fromEndpoints(
title: String,
version: String,
referenceType: SchemaStyle,
endpoints: Iterable[Endpoint[PathInput, Input, Err, Output, Middleware]],
endpoints: Iterable[Endpoint[_, _, _, _, _]],
): OpenAPI = fromEndpoints(referenceType, endpoints).title(title).version(version)

def gen[PathInput, Input, Err, Output, Middleware <: EndpointMiddleware](
endpoint: Endpoint[PathInput, Input, Err, Output, Middleware],
def gen(
endpoint: Endpoint[_, _, _, _, _],
referenceType: SchemaStyle = SchemaStyle.Compact,
): OpenAPI = {
val inAtoms = AtomizedMetaCodecs.flatten(endpoint.input)
Expand Down Expand Up @@ -658,9 +654,9 @@ object OpenAPIGen {
}

def componentSchemas: Map[OpenAPI.Key, OpenAPI.ReferenceOr[JsonSchema]] =
(endpoint.input.alternatives.map(_._1).map(AtomizedMetaCodecs.flatten).flatMap(_.content)
++ endpoint.error.alternatives.map(_._1).map(AtomizedMetaCodecs.flatten).flatMap(_.content)
++ endpoint.output.alternatives.map(_._1).map(AtomizedMetaCodecs.flatten).flatMap(_.content)).collect {
(endpoint.input.alternatives.map(_._1).map(AtomizedMetaCodecs.flatten(_)).flatMap(_.content)
++ endpoint.error.alternatives.map(_._1).map(AtomizedMetaCodecs.flatten(_)).flatMap(_.content)
++ endpoint.output.alternatives.map(_._1).map(AtomizedMetaCodecs.flatten(_)).flatMap(_.content)).collect {
case MetaCodec(HttpCodec.Content(schema, _, _, _), _) if nominal(schema, referenceType).isDefined =>
OpenAPI.Key.fromString(nominal(schema, referenceType).get).get ->
OpenAPI.ReferenceOr.Or(JsonSchema.fromZSchema(schema).discriminator(genDiscriminator(schema)))
Expand Down
Loading

0 comments on commit 0143477

Please sign in to comment.