Skip to content

Commit

Permalink
simplified RestStructure
Browse files Browse the repository at this point in the history
  • Loading branch information
ghik committed Oct 11, 2018
1 parent 63848f1 commit 2caae61
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ abstract class RestDataCompanion[T](implicit
) extends {
implicit lazy val codec: GenCodec[T] = instances(DefaultRestImplicits, this).codec
implicit lazy val restStructure: RestStructure[T] = instances(DefaultRestImplicits, this).structure
implicit lazy val restSchema: RestSchema[T] = restStructure.standaloneSchema // lazy on restStructure
implicit lazy val restSchema: RestSchema[T] = RestSchema.lazySchema(restStructure.standaloneSchema)
}

trait ClientInstances[Real] {
Expand All @@ -53,7 +53,7 @@ trait OpenApiInstances[Real] {
trait OpenApiServerInstances[Real] extends ServerInstances[Real] with OpenApiInstances[Real]
trait OpenApiFullInstances[Real] extends FullInstances[Real] with OpenApiInstances[Real]

/** @see [[RestApiCompanion]] */
/** @see [[RestApiCompanion]]*/
abstract class RestClientApiCompanion[Implicits, Real](protected val implicits: Implicits)(
implicit inst: MacroInstances[Implicits, ClientInstances[Real]]
) {
Expand All @@ -64,7 +64,7 @@ abstract class RestClientApiCompanion[Implicits, Real](protected val implicits:
RawRest.fromHandleRequest(handleRequest)
}

/** @see [[RestApiCompanion]] */
/** @see [[RestApiCompanion]]*/
abstract class RestServerApiCompanion[Implicits, Real](protected val implicits: Implicits)(
implicit inst: MacroInstances[Implicits, ServerInstances[Real]]
) {
Expand All @@ -75,7 +75,7 @@ abstract class RestServerApiCompanion[Implicits, Real](protected val implicits:
RawRest.asHandleRequest(real)
}

/** @see [[RestApiCompanion]] */
/** @see [[RestApiCompanion]]*/
abstract class RestServerOpenApiCompanion[Implicits, Real](protected val implicits: Implicits)(
implicit inst: MacroInstances[Implicits, OpenApiServerInstances[Real]]
) {
Expand Down Expand Up @@ -107,7 +107,7 @@ abstract class RestApiCompanion[Implicits, Real](protected val implicits: Implic
RawRest.asHandleRequest(real)
}

/** @see [[RestApiCompanion]] */
/** @see [[RestApiCompanion]]*/
abstract class RestOpenApiCompanion[Implicits, Real](protected val implicits: Implicits)(
implicit inst: MacroInstances[Implicits, OpenApiFullInstances[Real]]
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ object RestSchema {
def ref[T](refstr: String): RestSchema[T] =
RestSchema.create(_ => RefOr.ref(refstr))

def lazySchema[T](actual: => RestSchema[T]): RestSchema[T] =
new RestSchema[T] {
private lazy val actualSchema = actual
def createSchema(resolver: SchemaResolver): RefOr[Schema] = actualSchema.createSchema(resolver)
def name: Opt[String] = actualSchema.name
}

implicit lazy val NothingSchema: RestSchema[Nothing] =
RestSchema.create(_ => throw new NotImplementedError("RestSchema[Nothing]"))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,74 +11,36 @@ import com.avsystem.commons.serialization._

sealed trait RestStructure[T] extends TypedMetadata[T] {
def schemaAdjusters: List[SchemaAdjuster]
def standaloneSchema: RestSchema[T]
def info: GenInfo[T]

protected def applyAdjusters(schema: Schema): Schema =
schemaAdjusters.foldRight(schema)(_ adjustSchema _)
}
object RestStructure extends AdtMetadataCompanion[RestStructure] {
implicit class LazyRestStructureOps[T](restStructure: => RestStructure[T]) {
def standaloneSchema: RestSchema[T] = new RestSchema[T] {
def createSchema(resolver: SchemaResolver): RefOr[Schema] = restStructure match {
case union: Union[T] => union.createSchema(resolver)
case record: Record[T] => record.createSchema(resolver, Opt.Empty)
case singleton: Singleton[T] => singleton.createSchema(resolver, Opt.Empty)
}
def name: Opt[String] = restStructure match {
case _: Singleton[_] => Opt.Empty
case s => s.info.rawName.opt
}
}
}

@positioned(positioned.here) case class Union[T](
@multi @reifyAnnot schemaAdjusters: List[SchemaAdjuster],
@adtCaseMetadata @multi cases: List[Case[_]],
@composite info: GenUnionInfo[T]
) extends RestStructure[T] {

def createSchema(resolver: SchemaResolver): RefOr[Schema] = {
def standaloneSchema: RestSchema[T] =
RestSchema.create(createSchema, info.rawName)

private def createSchema(resolver: SchemaResolver): RefOr[Schema] = {
val caseFieldOpt = info.flatten.map(_.caseFieldName)
val caseSchemas = caseFieldOpt match {
case Opt(caseFieldName) => cases.map { cs =>
val caseName = cs.info.rawName
val caseRestSchema = cs match {
case record: Record[_] => RestSchema.create(record.createSchema(_, caseFieldOpt), caseName)
case singleton: Singleton[_] => RestSchema.create(singleton.createSchema(_, caseFieldOpt), caseName)
case custom: CustomCase[_] =>
val caseFieldSchema = RefOr(Schema.enumOf(List(caseName)))
custom.restSchema.map({
case RefOr.Value(caseSchema) => caseSchema.copy(
properties = caseSchema.properties + (caseFieldName -> caseFieldSchema),
required = caseFieldName :: caseSchema.required
)
case ref => Schema(allOf = List(RefOr(Schema(
`type` = DataType.Object,
properties = Mapping(caseFieldName -> caseFieldSchema),
required = List(caseFieldName)
)), ref))
}, custom.taggedName)
}
resolver.resolve(caseRestSchema)
}
case Opt.Empty => cases.map { cs =>
val caseName = cs.info.rawName
val caseSchema = cs match {
case record: Record[_] => record.createSchema(resolver, Opt.Empty)
case singleton: Singleton[_] => singleton.createSchema(resolver, Opt.Empty)
case custom: CustomCase[_] => resolver.resolve(custom.restSchema)
}
RefOr(Schema(
`type` = DataType.Object,
properties = Mapping(caseName -> caseSchema),
required = List(caseName)
))
}
val caseSchemas = cases.map { c =>
val baseSchema = resolver.resolve(c.caseSchema(caseFieldOpt))
if (caseFieldOpt.nonEmpty) baseSchema
else RefOr(Schema(
`type` = DataType.Object,
properties = Mapping(c.info.rawName -> baseSchema),
required = List(c.info.rawName)
))
}
val disc = caseFieldOpt.map { caseFieldName =>
val mapping = Mapping(cases.collect {
case custom: CustomCase[_] if custom.taggedName != custom.info.rawName =>
(custom.info.rawName, custom.taggedName)
val mapping = Mapping((cases zip caseSchemas).collect {
case (c, RefOr.Ref(ref)) => (c.info.rawName, ref)
})
Discriminator(caseFieldName, mapping)
}
Expand All @@ -89,6 +51,7 @@ object RestStructure extends AdtMetadataCompanion[RestStructure] {

sealed trait Case[T] extends TypedMetadata[T] {
def info: GenCaseInfo[T]
def caseSchema(caseFieldName: Opt[String]): RestSchema[T]
}
object Case extends AdtMetadataCompanion[Case]

Expand All @@ -99,9 +62,24 @@ object RestStructure extends AdtMetadataCompanion[RestStructure] {
@checked @infer restSchema: RestSchema[T],
@composite info: GenCaseInfo[T]
) extends Case[T] {
def taggedName: String =
if (restSchema.name.contains(info.rawName)) s"tagged${info.rawName}"
else info.rawName
def caseSchema(caseFieldName: Opt[String]): RestSchema[T] =
caseFieldName.fold(restSchema) { cfn =>
val caseFieldSchema = RefOr(Schema.enumOf(List(info.rawName)))
val taggedName =
if (restSchema.name.contains(info.rawName)) s"tagged${info.rawName}"
else info.rawName
restSchema.map({
case RefOr.Value(caseSchema) => caseSchema.copy(
properties = caseSchema.properties + (cfn -> caseFieldSchema),
required = cfn :: caseSchema.required
)
case ref => Schema(allOf = List(RefOr(Schema(
`type` = DataType.Object,
properties = Mapping(cfn -> caseFieldSchema),
required = List(cfn)
)), ref))
}, taggedName)
}
}

/**
Expand All @@ -113,7 +91,13 @@ object RestStructure extends AdtMetadataCompanion[RestStructure] {
@composite info: GenCaseInfo[T]
) extends RestStructure[T] with Case[T] {

def createSchema(resolver: SchemaResolver, caseFieldName: Opt[String]): RefOr[Schema] =
def standaloneSchema: RestSchema[T] =
RestSchema.create(createSchema(_, Opt.Empty), info.rawName)

def caseSchema(caseFieldName: Opt[String]): RestSchema[T] =
RestSchema.create(createSchema(_, caseFieldName), caseFieldName.map(_ => info.rawName).toOptArg)

private def createSchema(resolver: SchemaResolver, caseFieldName: Opt[String]): RefOr[Schema] =
(fields, caseFieldName) match {
case (single :: Nil, Opt.Empty) if info.transparent =>
SchemaAdjuster.adjustRef(schemaAdjusters, resolver.resolve(single.restSchema))
Expand All @@ -139,6 +123,12 @@ object RestStructure extends AdtMetadataCompanion[RestStructure] {
@composite info: GenCaseInfo[T]
) extends RestStructure[T] with Case[T] {

def standaloneSchema: RestSchema[T] =
RestSchema.create(createSchema(_, Opt.Empty))

def caseSchema(caseFieldName: Opt[String]): RestSchema[T] =
RestSchema.create(createSchema(_, caseFieldName), caseFieldName.map(_ => info.rawName).toOptArg)

def createSchema(resolver: SchemaResolver, caseFieldName: Opt[String]): RefOr[Schema] =
RefOr(applyAdjusters(Schema(`type` = DataType.Object,
properties = Mapping(caseFieldName.map(cfn => (cfn, RefOr(Schema.enumOf(List(info.rawName))))).toList),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,9 @@ class OpenApiGenerationTest extends FunSuite {
| "discriminator": {
| "propertyName": "_case",
| "mapping": {
| "RestEntity": "taggedRestEntity"
| "RestEntity": "#/components/schemas/taggedRestEntity",
| "RestOtherEntity": "#/components/schemas/RestOtherEntity",
| "SingletonEntity": "#/components/schemas/SingletonEntity"
| }
| }
| },
Expand Down

0 comments on commit 2caae61

Please sign in to comment.