Skip to content
Permalink
Browse files

Merge pull request #234 from avast/issue222

issue #222
  • Loading branch information...
blast-hardcheese committed May 3, 2019
2 parents 70b4a47 + aa2ba14 commit 8f57075526484e39f83cf44a1e37743456969a22
@@ -41,6 +41,7 @@ val exampleCases: List[(java.io.File, String, Boolean, List[String])] = List(
(sampleResource("issues/issue164.yaml"), "issues.issue164", false, List.empty),
(sampleResource("issues/issue215.yaml"), "issues.issue215", false, List.empty),
(sampleResource("issues/issue218.yaml"), "issues.issue218", false, List.empty),
(sampleResource("issues/issue222.yaml"), "issues.issue222", false, List.empty),
(sampleResource("issues/issue249.yaml"), "issues.issue249", false, List.empty),
(sampleResource("issues/issue264.yaml"), "issues.issue264", false, List.empty),
(sampleResource("multipart-form-data.yaml"), "multipartFormData", false, List.empty),
@@ -104,9 +104,12 @@ object CirceProtocolGenerator {
(swagger match {
case m: ObjectSchema => Target.pure(Option(m.getProperties))
case comp: ComposedSchema =>
Target.pure(Option(comp.getAllOf()).toList.flatMap(_.asScala.toList).lastOption.flatMap(prop => Option(prop.getProperties)))
val extractedProps =
Option(comp.getAllOf()).toList.flatMap(_.asScala.toList).flatMap(e => Option(e.getProperties).map(_.asScala.toMap))
val mergedProps = extractedProps.fold(Map.empty)(_ ++ _)
Target.pure(Option(mergedProps.asJava))
case comp: Schema[_] if Option(comp.get$ref).isDefined =>
Target.error(s"Attempted to extractProperties for a ${comp.getClass()}, unsure what to do here")
Target.raiseError(s"Attempted to extractProperties for a ${comp.getClass()}, unsure what to do here")
case _ => Target.pure(None)
}).map(_.map(_.asScala.toList).toList.flatten)

@@ -171,11 +174,12 @@ object CirceProtocolGenerator {

case RenderDTOClass(clsName, selfParams, parents) =>
val discriminators = parents.flatMap(_.discriminators)
val parenOpt = parents.headOption
val parentOpt = if (parents.exists(s => s.discriminators.nonEmpty)) { parents.headOption } else { None }
val terms = (parents.reverse.flatMap(_.params.map(_.term)) ++ selfParams.map(_.term)).filterNot(
param => discriminators.contains(param.name.value)
)
val code = parenOpt

val code = parentOpt
.fold(q"""case class ${Type.Name(clsName)}(..${terms})""")(
parent =>
q"""case class ${Type.Name(clsName)}(..${terms}) extends ${template"..${init"${Type.Name(parent.clsName)}(...$Nil)" :: parent.interfaces
@@ -366,23 +370,23 @@ object CirceProtocolGenerator {
object PolyProtocolTermInterp extends (PolyProtocolTerm[ScalaLanguage, ?] ~> Target) {
override def apply[A](fa: PolyProtocolTerm[ScalaLanguage, A]): Target[A] = fa match {
case ExtractSuperClass(swagger, definitions) =>
def allParents(model: Schema[_]): List[(String, Schema[_], List[Schema[_]])] =
def allParents(model: Schema[_]): Target[List[(String, Schema[_], List[Schema[_]])]] =
model match {
case elem: ComposedSchema =>
Option(elem.getAllOf).map(_.asScala.toList).getOrElse(List.empty) match {
case head :: tail =>
definitions
.collectFirst({
case (clsName, e) if Option(head.get$ref).exists(_.endsWith(s"/$clsName")) =>
(clsName, e, tail.toList) :: allParents(e)
val thisParent = (clsName, e, tail)
allParents(e).map(otherParents => thisParent :: otherParents)
})
.getOrElse(List.empty)
case _ => List.empty
.getOrElse(Target.raiseError(s"Reference ${head.get$ref()} not found among definitions"))
case _ => Target.pure(List.empty)
}
case _ => List.empty
case _ => Target.pure(List.empty)
}

Target.pure(allParents(swagger))
allParents(swagger)

case RenderADTStaticDefns(clsName, discriminator, encoder, decoder) =>
Target.pure(
@@ -0,0 +1,210 @@
package core.issues

import com.twilio.guardrail.generators.Http4s
import com.twilio.guardrail.languages.ScalaLanguage
import com.twilio.guardrail.{ClassDefinition, Context, ProtocolDefinitions}
import org.scalatest.{Assertion, FunSuite, Matchers}
import support.SwaggerSpecRunner

class Issue222 extends FunSuite with Matchers with SwaggerSpecRunner {

import scala.meta._

val swagger: String =
s"""
|swagger: '2.0'
|info:
| title: someapp
| description: someapp
| version: '1'
|basePath: "/v1"
|schemes:
| - http
|produces:
| - application/json
|paths: {}
|definitions:
| Request:
| description: Request fields with id
| allOf:
| - "$$ref": "#/definitions/RequestFields"
| - type: object
| properties:
| id:
| type: string
| RequestFields:
| description: Request fields
| type: object
| properties:
| state:
| type: integer
| required: [state]
| Request2:
| description: Request fields with id
| allOf:
| - "$$ref": "#/definitions/RequestFields2"
| - type: object
| properties:
| id:
| type: string
| - type: object
| properties:
| id2:
| type: string
| required: [id2]
| RequestFields2:
| description: Request fields
| type: object
| properties:
| state2:
| type: integer
| Request3:
| description: Request fields with id
| allOf:
| - "$$ref": "#/definitions/RequestFields"
| - type: object
| properties:
| id:
| type: string
| - type: object
| properties:
| id2:
| type: string
| required: [id2]
| - "$$ref": "#/definitions/RequestFields2"
|""".stripMargin

test("Ensure case-to-case inheritance is not generated") {
val (ProtocolDefinitions(List(request: ClassDefinition[ScalaLanguage], requestFields: ClassDefinition[ScalaLanguage], _, _, _), _, _, _), _, _) = runSwaggerSpec(swagger)(Context.empty, Http4s)

val List(reqEncoder, reqDecoder) = request.staticDefns.definitions

val expectedRequestTpe = t"""Request"""

val expectedRequestCls = q"""case class Request(state: BigInt, id: Option[String] = None)"""

val expectedRequestEncoder =
q"""
implicit val encodeRequest = {
val readOnlyKeys = Set[String]()
Encoder.forProduct2("state", "id")((o: Request) => (o.state, o.id)).mapJsonObject(_.filterKeys(key => !(readOnlyKeys contains key)))
}
"""
val expectedRequestDecoder =
q"""
implicit val decodeRequest = Decoder.forProduct2("state", "id")(Request.apply _)
"""


compare(request.tpe, expectedRequestTpe)
compare(request.cls, expectedRequestCls)
compare(reqEncoder, expectedRequestEncoder)
compare(reqDecoder, expectedRequestDecoder)

val expectedFieldsTpe = t"""RequestFields"""
val expectedFieldsCls = q"""case class RequestFields(state: BigInt)"""

val List(fieldsEncoder, fieldsDecoder) = requestFields.staticDefns.definitions

val expectedFieldsEncoder =
q"""
implicit val encodeRequestFields = {
val readOnlyKeys = Set[String]()
Encoder.forProduct1("state")((o: RequestFields) => o.state).mapJsonObject(_.filterKeys(key => !(readOnlyKeys contains key)))
}
"""
val expectedFieldsDecoder =
q"""
implicit val decodeRequestFields = Decoder.forProduct1("state")(RequestFields.apply _)
"""

compare(requestFields.tpe, expectedFieldsTpe)
compare(requestFields.cls, expectedFieldsCls)
compare(fieldsEncoder, expectedFieldsEncoder)
compare(fieldsDecoder, expectedFieldsDecoder)
}

test("Ensure case-to-case inheritance is not generated, extends two objects") {
val (ProtocolDefinitions(List(_, _, request: ClassDefinition[ScalaLanguage], requestFields: ClassDefinition[ScalaLanguage], _), _, _, _), _, _) = runSwaggerSpec(swagger)(Context.empty, Http4s)

val List(reqEncoder, reqDecoder) = request.staticDefns.definitions

val expectedRequestTpe = t"""Request2"""

val expectedRequestCls = q"""case class Request2(state2: Option[BigInt] = None, id: Option[String] = None, id2: String)"""

val expectedRequestEncoder =
q"""
implicit val encodeRequest2 = {
val readOnlyKeys = Set[String]()
Encoder.forProduct3("state2", "id", "id2")((o: Request2) => (o.state2, o.id, o.id2)).mapJsonObject(_.filterKeys(key => !(readOnlyKeys contains key)))
}
"""
val expectedRequestDecoder =
q"""
implicit val decodeRequest2 = Decoder.forProduct3("state2", "id", "id2")(Request2.apply _)
"""


compare(request.tpe, expectedRequestTpe)
compare(request.cls, expectedRequestCls)
compare(reqEncoder, expectedRequestEncoder)
compare(reqDecoder, expectedRequestDecoder)

val expectedFieldsTpe = t"""RequestFields2"""
val expectedFieldsCls = q"""case class RequestFields2(state2: Option[BigInt] = None)"""

val List(fieldsEncoder, fieldsDecoder) = requestFields.staticDefns.definitions

val expectedFieldsEncoder =
q"""
implicit val encodeRequestFields2 = {
val readOnlyKeys = Set[String]()
Encoder.forProduct1("state2")((o: RequestFields2) => o.state2).mapJsonObject(_.filterKeys(key => !(readOnlyKeys contains key)))
}
"""
val expectedFieldsDecoder =
q"""
implicit val decodeRequestFields2 = Decoder.forProduct1("state2")(RequestFields2.apply _)
"""

compare(requestFields.tpe, expectedFieldsTpe)
compare(requestFields.cls, expectedFieldsCls)
compare(fieldsEncoder, expectedFieldsEncoder)
compare(fieldsDecoder, expectedFieldsDecoder)
}

test("Ensure case-to-case inheritance is not generated, extends two objects and two classes") {
val (ProtocolDefinitions(List(_, _, _, _, request: ClassDefinition[ScalaLanguage]), _, _, _), _, _) = runSwaggerSpec(swagger)(Context.empty, Http4s)

val List(reqEncoder, reqDecoder) = request.staticDefns.definitions

val expectedRequestTpe = t"""Request3"""

val expectedRequestCls = q"""case class Request3(state: BigInt, state2: Option[BigInt] = None, id: Option[String] = None, id2: String)"""

val expectedRequestEncoder =
q"""
implicit val encodeRequest3 = {
val readOnlyKeys = Set[String]()
Encoder.forProduct4("state", "state2", "id", "id2")((o: Request3) => (o.state, o.state2, o.id, o.id2)).mapJsonObject(_.filterKeys(key => !(readOnlyKeys contains key)))
}
"""
val expectedRequestDecoder =
q"""
implicit val decodeRequest3 = Decoder.forProduct4("state", "state2", "id", "id2")(Request3.apply _)
"""


compare(request.tpe, expectedRequestTpe)
compare(request.cls, expectedRequestCls)
compare(reqEncoder, expectedRequestEncoder)
compare(reqDecoder, expectedRequestDecoder)

}


private def compare(t1: Tree, t2: Tree): Assertion = {
t1.structure shouldEqual t2.structure
}
}
@@ -0,0 +1,26 @@
swagger: '2.0'
info:
title: someapp
description: someapp
version: '1'
basePath: "/v1"
schemes:
- http
produces:
- application/json
paths: {}
definitions:
Request:
description: Request fields with id
allOf:
- "$ref": "#/definitions/RequestFields"
- type: object
properties:
id:
type: string
RequestFields:
description: Request fields
type: object
properties:
state:
type: integer

0 comments on commit 8f57075

Please sign in to comment.
You can’t perform that action at this time.