diff --git a/generated/src/main/scala/api/AppleReqDisc.scala b/generated/src/main/scala/api/AppleReqDisc.scala new file mode 100644 index 000000000000..f8b3e34f210f --- /dev/null +++ b/generated/src/main/scala/api/AppleReqDisc.scala @@ -0,0 +1,24 @@ +/** + * fruity + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +package api +import play.api.libs.json._ + + final case class AppleReqDisc extends Fruit ( + seeds: Int, + ) + + object AppleReqDisc { + implicit val format: Format[AppleReqDisc] = Json.format + } + + diff --git a/generated/src/main/scala/api/BananaReqDisc.scala b/generated/src/main/scala/api/BananaReqDisc.scala new file mode 100644 index 000000000000..6526b0da43d8 --- /dev/null +++ b/generated/src/main/scala/api/BananaReqDisc.scala @@ -0,0 +1,24 @@ +/** + * fruity + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +package api +import play.api.libs.json._ + + final case class BananaReqDisc extends Fruit ( + length: Int, + ) + + object BananaReqDisc { + implicit val format: Format[BananaReqDisc] = Json.format + } + + diff --git a/generated/src/main/scala/api/Fruit.scala b/generated/src/main/scala/api/Fruit.scala new file mode 100644 index 000000000000..c460da2d4553 --- /dev/null +++ b/generated/src/main/scala/api/Fruit.scala @@ -0,0 +1,39 @@ +/** + * fruity + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +package api +import play.api.libs.json._ + + trait Fruit ( + fruitType: String, + ) + + object Fruit { + private val discriminator = "fruitType" + + implicit val format: OFormat[Fruit] = new OFormat[Fruit]{ + override def reads(json: JsValue): JsResult[Fruit] = + json.validate[JsObject].flatMap( + _.value(discriminator).as[String] match { + case "AppleReqDisc" => json.validate[AppleReqDisc] + case "BananaReqDisc" => json.validate[BananaReqDisc] + } + ) + + override def writes(o: Fruit): JsObject = o match { + case a: AppleReqDisc => AppleReqDisc.format.writes(a) + (discriminator -> JsString("AppleReqDisc")) + case a: BananaReqDisc => BananaReqDisc.format.writes(a) + (discriminator -> JsString("BananaReqDisc")) + } + } + } + + diff --git a/generated/src/main/scala/api/FruitBasket.scala b/generated/src/main/scala/api/FruitBasket.scala new file mode 100644 index 000000000000..0d24f41e17ee --- /dev/null +++ b/generated/src/main/scala/api/FruitBasket.scala @@ -0,0 +1,25 @@ +/** + * fruity + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +package api +import play.api.libs.json._ + + final case class FruitBasket ( + quantity: Option[Int], + fruit: Option[Fruit] + ) + + object FruitBasket { + implicit val format: Format[FruitBasket] = Json.format + } + + diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaLagomServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaLagomServerCodegen.java index 1585c8383ba9..1f3a9800d29e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaLagomServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaLagomServerCodegen.java @@ -24,6 +24,7 @@ import org.slf4j.LoggerFactory; import java.util.*; +import java.util.stream.Collectors; import static org.openapitools.codegen.utils.StringUtils.camelize; import static org.openapitools.codegen.utils.StringUtils.underscore; @@ -163,6 +164,42 @@ public String toOperationId(String operationId) { return camelize(operationId, true); } + private CodegenModel codegenModel(Object o) { + return ((Map>>)o).get("models").get(0).get("model"); + } + + @Override + public Map postProcessAllModels(final Map objs) { + + for(Object value : objs.values()) { + if(value instanceof Map) { + CodegenModel m = codegenModel(value); + // for oneOf schemas + if(!m.oneOf.isEmpty() && m.getDiscriminator() != null) { + // add it as a parent to every single model and removed unnecessary discriminator prop + m.oneOf.forEach( + subclassName -> { + CodegenModel cm = codegenModel(objs.get(subclassName)); + cm.setParent(m.classname); + if(!cm.getVars().isEmpty()) { + cm.setVars( + cm.getVars().stream().filter(v -> !v.getName().equals(m.getDiscriminator().getPropertyName())).collect(Collectors.toList()) + ); + } + } + ); + // retain only discriminator prop + if(!m.getVars().isEmpty()) { + m.setVars( + m.getVars().stream().filter(v -> v.getName().equals(m.getDiscriminator().getPropertyName())).collect(Collectors.toList()) + ); + } + } + } + } + return objs; + } + @Override public Map postProcessModelsEnum(Map objs) { objs = super.postProcessModelsEnum(objs); diff --git a/modules/openapi-generator/src/main/resources/scala-lagom-server/model.mustache b/modules/openapi-generator/src/main/resources/scala-lagom-server/model.mustache index 5b362b5fcbe4..1ef11208d5ee 100644 --- a/modules/openapi-generator/src/main/resources/scala-lagom-server/model.mustache +++ b/modules/openapi-generator/src/main/resources/scala-lagom-server/model.mustache @@ -7,32 +7,66 @@ import {{import}} {{#models}} {{#model}} -case class {{classname}} ( -{{#vars}} {{#isEnum}} - {{{name}}}: Option[{{classname}}{{datatypeWithEnum}}.{{classname}}{{datatypeWithEnum}}]{{#hasMore}},{{/hasMore}} + {{classname}}{{datatypeWithEnum}} extends Enumeration { + val {{#allowableValues}} {{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}} = Value{{/allowableValues}} + type {{classname}}{{datatypeWithEnum}} = Value + implicit val format: Format[Value] = Format(Reads.enumNameReads(this), Writes.enumNameWrites[{{classname}}{{datatypeWithEnum}}.type]) + } {{/isEnum}} {{^isEnum}} - {{#description}} - /* {{{description}}} */ - {{/description}} - {{{name}}}: {{^required}}Option[{{/required}}{{dataType}}{{^required}}]{{/required}}{{#hasMore}},{{/hasMore}} - {{/isEnum}} -{{/vars}} -) + {{#discriminator}} + trait {{classname}} ( + {{/discriminator}} + {{^discriminator}} + final case class {{classname}} {{#parent}}extends{{/parent}} {{parent}} ( + {{/discriminator}} + {{#vars}} + {{#isEnum}} + {{{name}}}: Option[{{classname}}{{datatypeWithEnum}}.{{classname}}{{datatypeWithEnum}}]{{#hasMore}},{{/hasMore}} + {{/isEnum}} + {{^isEnum}} + {{#description}} + /* {{{description}}} */ + {{/description}} + {{{name}}}: {{^required}}Option[{{/required}}{{dataType}}{{^required}}]{{/required}}{{#hasMore}},{{/hasMore}} + {{/isEnum}} + {{/vars}} + ) + + object {{classname}} { + {{#discriminator}} + private val discriminator = "{{discriminator.propertyBaseName}}" + + implicit val format: OFormat[{{classname}}] = new OFormat[Fruit]{ + override def reads(json: JsValue): JsResult[{{classname}}] = + json.validate[JsObject].flatMap( + _.value(discriminator).as[String] match { + {{#oneOf}} + case "{{.}}" => json.validate[{{.}}] + {{/oneOf}} + } + ) -object {{classname}} { -implicit val format: Format[{{classname}}] = Json.format -} + override def writes(o: {{classname}}): JsObject = o match { + {{#oneOf}} + case a: {{.}} => {{.}}.format.writes(a) + (discriminator -> JsString("{{.}}")) + {{/oneOf}} + } + } + {{/discriminator}} + {{^discriminator}} + implicit val format: Format[{{classname}}] = Json.format + {{/discriminator}} + } -{{#vars}} - {{#isEnum}} -object {{classname}}{{datatypeWithEnum}} extends Enumeration { - val {{#allowableValues}} {{#values}}{{.}}{{^-last}}, {{/-last}}{{/values}} = Value{{/allowableValues}} - type {{classname}}{{datatypeWithEnum}} = Value - implicit val format: Format[Value] = Format(Reads.enumNameReads(this), Writes.enumNameWrites[{{classname}}{{datatypeWithEnum}}.type]) -} - {{/isEnum}} -{{/vars}} + {{#vars}} + {{#isEnum}} + object {{classname}}{{datatypeWithEnum}} extends Enumeration { + } + {{/isEnum}} + {{/vars}} + + {{/isEnum}} {{/model}} -{{/models}} +{{/models}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/3_0/oneoOfDiscriminatorSimple.yaml b/modules/openapi-generator/src/test/resources/3_0/oneoOfDiscriminatorSimple.yaml new file mode 100644 index 000000000000..26d6147317ee --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/oneoOfDiscriminatorSimple.yaml @@ -0,0 +1,48 @@ +openapi: 3.0.1 +info: + title: fruity + version: 0.0.1 +paths: + /: + get: + responses: + '200': + description: desc + content: + application/json: + schema: + $ref: '#/components/schemas/FruitBasket' +components: + schemas: + FruitBasket: + properties: + quantity: + type: int + fruit: + $ref: '#/components/schemas/Fruit' + Fruit: + oneOf: + - $ref: '#/components/schemas/AppleReqDisc' + - $ref: '#/components/schemas/BananaReqDisc' + discriminator: + propertyName: fruitType + AppleReqDisc: + type: object + required: + - seeds + - fruitType + properties: + seeds: + type: integer + fruitType: + type: string + BananaReqDisc: + type: object + required: + - length + - fruitType + properties: + length: + type: integer + fruitType: + type: string \ No newline at end of file