diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java index 1ac6e0a3feab8..b257e2eba22d3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java @@ -18,6 +18,7 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.RequestBody; import org.apache.commons.io.FileUtils; import org.openapitools.codegen.*; import org.openapitools.codegen.model.ModelMap; @@ -394,6 +395,12 @@ public void processOpenAPI(OpenAPI openAPI) { } + /** + * This class is used in pathExtractorParams.mustache. + * + * It exposes some methods which make it more readable + * for that mustache snippet, and also isolates the logic needed for the path extractors + */ public static class ParamPart { final CodegenParameter param; final String name; @@ -416,7 +423,9 @@ public ParamPart(String name, CodegenParameter param) { } /** - * Cask will compile but 'initialize' can throw a route overlap exception: + * This data structure is here to manually identify and fix routes which will overlap (e.g. GET /foo/bar and GET /foo/bazz) + * + * If we added these as individual routes, then Cask itself will compile, but calling 'initialize' throws a route overlap exception: *

* {{{ * Routes overlap with wildcards: get /user/logout, get /user/:username, get /user/login @@ -672,9 +681,12 @@ private void postProcessModel(CodegenModel model) { model.getVars().forEach(this::postProcessProperty); model.getAllVars().forEach(this::postProcessProperty); + + + model.vendorExtensions.put("x-has-one-of", model.oneOf != null && !model.oneOf.isEmpty()); } - private static void postProcessOperation(CodegenOperation op) { + private static void postProcessOperation(final CodegenOperation op) { // force http method to lower case op.httpMethod = op.httpMethod.toLowerCase(Locale.ROOT); @@ -710,9 +722,33 @@ private static void postProcessOperation(CodegenOperation op) { .collect(Collectors.toCollection(LinkedHashSet::new)); var responseType = responses.isEmpty() ? "Unit" : String.join(" | ", responses); + op.vendorExtensions.put("x-import-response-implicits", importResponseImplicits(op)); op.vendorExtensions.put("x-response-type", responseType); } + /** + * We need to bring the response type into scope in order to use the upickle implicits + * only if the response type has a 'oneOf' type, which means it's a union type with a + * companion object containing the ReadWriter + * + * @param op + * @return true if we need to provide an import + */ + private static boolean importResponseImplicits(final CodegenOperation op) { + final Set importBlacklist = Set.of("File"); + + boolean doImport = false; + for (var response : op.responses) { + // we should ignore generic types like Seq[...] or Map[..] types + var isPolymorphic = response.dataType != null && response.dataType.contains("["); + if (response.isModel && !importBlacklist.contains(response.dataType) && !isPolymorphic) { + doImport = true; + break; + } + } + return doImport; + } + /** * primitive or enum types don't have Data representations * @param p the property @@ -747,6 +783,10 @@ private static boolean isByteArray(final CodegenProperty p) { return "byte".equalsIgnoreCase(p.dataFormat); // && } + private static boolean wrapInOptional(CodegenProperty p) { + return !p.required && !p.isArray && !p.isMap; + } + /** * this parameter is used to create the function: * {{{ @@ -761,19 +801,18 @@ private static boolean isByteArray(final CodegenProperty p) { * and then back again */ private static String asDataCode(final CodegenProperty p, final Set typesWhichDoNotNeedMapping) { - final var wrapInOptional = !p.required && !p.isArray && !p.isMap; String code = ""; String dv = defaultValueNonOption(p, p.defaultValue); if (doesNotNeedMapping(p, typesWhichDoNotNeedMapping)) { - if (wrapInOptional) { + if (wrapInOptional(p)) { code = String.format(Locale.ROOT, "%s.getOrElse(%s) /* 1 */", p.name, dv); } else { code = String.format(Locale.ROOT, "%s /* 2 */", p.name); } } else { - if (wrapInOptional) { + if (wrapInOptional(p)) { if (isByteArray(p)) { code = String.format(Locale.ROOT, "%s.getOrElse(%s) /* 3 */", p.name, dv); } else { @@ -782,11 +821,15 @@ private static String asDataCode(final CodegenProperty p, final Set type } else if (p.isArray) { if (isByteArray(p)) { code = String.format(Locale.ROOT, "%s /* 5 */", p.name); + } else if (!isObjectArray(p)) { + code = String.format(Locale.ROOT, "%s /* 5.1 */", p.name); } else { code = String.format(Locale.ROOT, "%s.map(_.asData) /* 6 */", p.name); } + } else if (p.isMap) { + code = String.format(Locale.ROOT, "%s /* 7 */", p.name); } else { - code = String.format(Locale.ROOT, "%s.asData /* 7 */", p.name); + code = String.format(Locale.ROOT, "%s.asData /* 8 */", p.name); } } return code; @@ -807,17 +850,16 @@ private static String asDataCode(final CodegenProperty p, final Set type * @return */ private static String asModelCode(final CodegenProperty p, final Set typesWhichDoNotNeedMapping) { - final var wrapInOptional = !p.required && !p.isArray && !p.isMap; String code = ""; if (doesNotNeedMapping(p, typesWhichDoNotNeedMapping)) { - if (wrapInOptional) { + if (wrapInOptional(p)) { code = String.format(Locale.ROOT, "Option(%s) /* 1 */", p.name); } else { code = String.format(Locale.ROOT, "%s /* 2 */", p.name); } } else { - if (wrapInOptional) { + if (wrapInOptional(p)) { if (isByteArray(p)) { code = String.format(Locale.ROOT, "Option(%s) /* 3 */", p.name); } else { @@ -825,6 +867,8 @@ private static String asModelCode(final CodegenProperty p, final Set typ } } else if (p.isArray) { code = String.format(Locale.ROOT, "%s.map(_.asModel) /* 5 */", p.name); + } else if (p.isMap) { + code = String.format(Locale.ROOT, "%s /* 5.1 */", p.name); } else { code = String.format(Locale.ROOT, "%s.asModel /* 6 */", p.name); } @@ -863,8 +907,17 @@ private String ensureNonKeyword(String text) { return text; } + private static boolean hasItemModel(final CodegenProperty p) { + return p.items != null && p.items.isModel; + } + + private static boolean isObjectArray(final CodegenProperty p) { + return p.isArray && hasItemModel(p); + } + private void postProcessProperty(final CodegenProperty p) { - p.vendorExtensions.put("x-datatype-model", asScalaDataType(p, p.required, false)); + + p.vendorExtensions.put("x-datatype-model", asScalaDataType(p, p.required, false, wrapInOptional(p))); p.vendorExtensions.put("x-defaultValue-model", defaultValue(p, p.required, p.defaultValue)); final String dataTypeData = asScalaDataType(p, p.required, true); p.vendorExtensions.put("x-datatype-data", dataTypeData); @@ -878,7 +931,7 @@ private void postProcessProperty(final CodegenProperty p) { p._enum = p._enum.stream().map(this::ensureNonKeyword).collect(Collectors.toList()); } - /** + /* * This is a fix for the enum property "type" declared like this: * {{{ * type: @@ -908,6 +961,9 @@ private void postProcessProperty(final CodegenProperty p) { )).collect(Collectors.toSet()); typesWhichShouldNotBeMapped.add("byte"); + // when deserialising map objects, the logic is tricky. + p.vendorExtensions.put("x-deserialize-asModelMap", p.isMap && hasItemModel(p)); + // the 'asModel' logic for modelData.mustache // // if it's optional (not required), then wrap the value in Option() @@ -916,16 +972,6 @@ private void postProcessProperty(final CodegenProperty p) { p.vendorExtensions.put("x-asData", asDataCode(p, typesWhichShouldNotBeMapped)); p.vendorExtensions.put("x-asModel", asModelCode(p, typesWhichShouldNotBeMapped)); - // if it's an array or optional, we need to map it as a model -- unless it's a map, - // in which case we have to map the values - boolean hasItemModel = p.items != null && p.items.isModel; - boolean isObjectArray = p.isArray && hasItemModel; - boolean isOptionalObj = !p.required && p.isModel; - p.vendorExtensions.put("x-map-asModel", (isOptionalObj || isObjectArray) && !p.isMap); - - // when deserialising map objects, the logic is tricky. - p.vendorExtensions.put("x-deserialize-asModelMap", p.isMap && hasItemModel); - // for some reason, an openapi spec with pattern field like this: // pattern: '^[A-Za-z]+$' // will result in the pattern property text of @@ -934,6 +980,20 @@ private void postProcessProperty(final CodegenProperty p) { p.pattern = p.pattern.substring(1, p.pattern.length() - 1); } + // in our model class definition laid out in modelClass.mustache, we use 'Option' for non-required + // properties only when they don't have a sensible 'empty' value (e.g. maps and lists). + // + // that is to say, we're trying to avoid having: + // + // someOptionalField : Option[Seq[Foo]] + // + // when we could just have e.g. + // + // someOptionalField : Seq[Foo] + // + // with an empty value + p.vendorExtensions.put("x-model-needs-option", wrapInOptional(p)); + } diff --git a/modules/openapi-generator/src/main/resources/scala-cask/apiRoutes.mustache b/modules/openapi-generator/src/main/resources/scala-cask/apiRoutes.mustache index 53dc07104945f..b2167d0cd5304 100644 --- a/modules/openapi-generator/src/main/resources/scala-cask/apiRoutes.mustache +++ b/modules/openapi-generator/src/main/resources/scala-cask/apiRoutes.mustache @@ -45,6 +45,10 @@ class {{classname}}Routes(service : {{classname}}Service[Try]) extends cask.Rout val result = {{>parseHttpParams}} + {{#vendorExtensions.x-import-response-implicits}} + import {{vendorExtensions.x-response-type}}.{given, *} // this brings in upickle in the case of union (oneOf) types + {{/vendorExtensions.x-import-response-implicits}} + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) {{#responses}} diff --git a/modules/openapi-generator/src/main/resources/scala-cask/model.mustache b/modules/openapi-generator/src/main/resources/scala-cask/model.mustache index 6e8551c5b0592..31b4875987753 100644 --- a/modules/openapi-generator/src/main/resources/scala-cask/model.mustache +++ b/modules/openapi-generator/src/main/resources/scala-cask/model.mustache @@ -13,12 +13,24 @@ import upickle.default.* {{#models}} {{#model}} -{{#isEnum}} -{{>modelEnum}} -{{/isEnum}} -{{^isEnum}} -{{>modelClass}} -{{/isEnum}} +{{#vendorExtensions.x-has-one-of}} + +type {{classname}} = {{#oneOf}}{{{.}}}{{^-last}} | {{/-last}}{{/oneOf}} +object {{{classname}}} { + + given RW[{{{classname}}}] = RW.merge({{#oneOf}}summon[RW[{{{.}}}]]{{^-last}}, {{/-last}}{{/oneOf}}) +} + +{{/vendorExtensions.x-has-one-of}} +{{^vendorExtensions.x-has-one-of}} + {{#isEnum}} + {{>modelEnum}} + {{/isEnum}} + {{^isEnum}} + {{>modelClass}} + {{/isEnum}} +{{/vendorExtensions.x-has-one-of}} + {{/model}} {{/models}} diff --git a/modules/openapi-generator/src/main/resources/scala-cask/modelClass.mustache b/modules/openapi-generator/src/main/resources/scala-cask/modelClass.mustache index 9cf9cf3102fc5..12f4ced6b0840 100644 --- a/modules/openapi-generator/src/main/resources/scala-cask/modelClass.mustache +++ b/modules/openapi-generator/src/main/resources/scala-cask/modelClass.mustache @@ -4,7 +4,7 @@ case class {{classname}}( {{#description}} /* {{{description}}} */ {{/description}} - {{name}}: {{#isEnum}}{{^required}}Option[{{/required}}{{classname}}.{{datatypeWithEnum}}{{^required}}]{{/required}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.x-datatype-model}}}{{/isEnum}}{{^required}} = {{{vendorExtensions.x-defaultValue-model}}} {{/required}}{{^-last}},{{/-last}} + {{name}}: {{#isEnum}}{{#vendorExtensions.x-model-needs-option}}Option[{{/vendorExtensions.x-model-needs-option}}{{classname}}.{{datatypeWithEnum}}{{#vendorExtensions.x-model-needs-option}}]{{/vendorExtensions.x-model-needs-option}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.x-datatype-model}}}{{/isEnum}}{{^required}} = {{{vendorExtensions.x-defaultValue-model}}} {{/required}}{{^-last}},{{/-last}} {{/vars}} {{#isAdditionalPropertiesTrue}}, additionalProperties : ujson.Value = ujson.Null{{/isAdditionalPropertiesTrue}} diff --git a/modules/openapi-generator/src/main/resources/scala-cask/modelData.mustache b/modules/openapi-generator/src/main/resources/scala-cask/modelData.mustache index 14a8e12ee91a9..91c580352a192 100644 --- a/modules/openapi-generator/src/main/resources/scala-cask/modelData.mustache +++ b/modules/openapi-generator/src/main/resources/scala-cask/modelData.mustache @@ -13,11 +13,40 @@ import upickle.default.* {{#models}} {{#model}} +{{#vendorExtensions.x-has-one-of}} +type {{{classname}}}Data = {{#oneOf}}{{{.}}}Data{{^-last}} | {{/-last}}{{/oneOf}} + +object {{{classname}}}Data { + + def validated(d8a : {{{classname}}}Data, failFast: Boolean) : Try[{{{classname}}}] = { + d8a match { + {{#oneOf}} + case value : {{{.}}}Data => value.validated(failFast) + {{/oneOf}} + } + } + + def fromJsonString(jason : String) = fromJson { + try { + read[ujson.Value](jason) + } catch { + case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e") + } + } + + def fromJson(jason : ujson.Value) : {{{classname}}}Data = { + val attempt = {{#oneOf}}{{^-first}}.orElse({{/-first}} Try({{{.}}}Data.fromJson(jason)) {{^-first}}) /* not first */{{/-first}} {{/oneOf}} + attempt.get + } +} +{{/vendorExtensions.x-has-one-of}} +{{^vendorExtensions.x-has-one-of}} {{#isEnum}} {{>modelDataEnum}} {{/isEnum}} {{^isEnum}} {{>modelDataClass}} {{/isEnum}} +{{/vendorExtensions.x-has-one-of}} {{/model}} {{/models}} diff --git a/modules/openapi-generator/src/main/resources/scala-cask/modelDataClass.mustache b/modules/openapi-generator/src/main/resources/scala-cask/modelDataClass.mustache index 72017e6356379..a61b223fb1daf 100644 --- a/modules/openapi-generator/src/main/resources/scala-cask/modelDataClass.mustache +++ b/modules/openapi-generator/src/main/resources/scala-cask/modelDataClass.mustache @@ -26,73 +26,73 @@ case class {{classname}}Data( } def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = { - val errors = scala.collection.mutable.ListBuffer[ValidationError]() + val _allValidationErrors = scala.collection.mutable.ListBuffer[ValidationError]() {{#vars}} // ================== {{name}} validation ================== {{#pattern}} // validate against pattern '{{{pattern}}}' - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { val regex = """{{{pattern}}}""" if {{name}} == null || !regex.r.matches({{name}}) then - errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' doesn't match pattern $regex") + _allValidationErrors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' doesn't match pattern $regex") } {{/pattern}} {{#minimum}} // validate against {{#exclusiveMinimum}}exclusive {{/exclusiveMinimum}}minimum {{minimum}} - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { if !({{name}} >{{^exclusiveMinimum}}={{/exclusiveMinimum}} {{minimum}}) then - errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' is not greater than the {{#exclusiveMinimum}}exclusive {{/exclusiveMinimum}}minimum value {{minimum}}") + _allValidationErrors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' is not greater than the {{#exclusiveMinimum}}exclusive {{/exclusiveMinimum}}minimum value {{minimum}}") } {{/minimum}} {{#maximum}} // validate against {{#exclusiveMaximum}}exclusive {{/exclusiveMaximum}}maximum {{maximum}} - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { if !({{name}} <{{^exclusiveMaximum}}={{/exclusiveMaximum}} {{maximum}}) then - errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' is not greater than the {{#exclusiveMaximum}}exclusive {{/exclusiveMaximum}}maximum value {{maximum}}") + _allValidationErrors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' is not greater than the {{#exclusiveMaximum}}exclusive {{/exclusiveMaximum}}maximum value {{maximum}}") } {{/maximum}} {{#minLength}} // validate min length {{minLength}} - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { val len = if {{name}} == null then 0 else {{name}}.length if (len < {{minLength}}) { - errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"length $len is shorter than the min length {{minLength}}") + _allValidationErrors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"length $len is shorter than the min length {{minLength}}") } } {{/minLength}} {{#maxLength}} // validate max length {{maxLength}} - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { val len = if {{name}} == null then 0 else {{name}}.length if (len < {{maxLength}}) { - errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"length $len is longer than the max length {{maxLength}}") + _allValidationErrors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"length $len is longer than the max length {{maxLength}}") } } {{/maxLength}} {{#isEmail}} // validate {{name}} is a valid email address - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { val emailRegex = """^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$""" // validate {{name}} is email if ({{name}} == null || !emailRegex.r.matches({{name}})) { - errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"${{name}} is not a valid email address according to the pattern $emailRegex") + _allValidationErrors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"${{name}} is not a valid email address according to the pattern $emailRegex") } } {{/isEmail}} {{#required}}{{^isPrimitiveType}} - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { if ({{name}} == null) { - errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, "{{name}} is a required field and cannot be null") + _allValidationErrors += ValidationError(path :+ {{classname}}.Fields.{{name}}, "{{name}} is a required field and cannot be null") } } {{/isPrimitiveType}}{{/required}} {{#uniqueItems}} // validate {{name}} has unique items - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { if ({{name}} != null) { {{name}}.foldLeft(Set[{{{vendorExtensions.x-containertype-data}}}]()) { case (set, next) if set.contains(next) => - errors += ValidationError( + _allValidationErrors += ValidationError( path :+ {{classname}}.Fields.{{name}}, s"duplicate value: $next" ) @@ -103,10 +103,10 @@ case class {{classname}}Data( } {{/uniqueItems}} {{#multipleOf}} - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { // validate {{name}} multiple of {{multipleOf}} if ({{name}} % {{multipleOf}} != 0) { - errors += ValidationError( + _allValidationErrors += ValidationError( path :+ {{classname}}.Fields.{{name}}, s"${{name}} is not a multiple of {{multipleOf}}" ) @@ -115,30 +115,30 @@ case class {{classname}}Data( {{/multipleOf}} {{#minItems}} // validate min items {{minItems}} - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { val len = if {{name}} == null then 0 else {{name}}.size if (len < {{minItems}}) { - errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"{{name}} has $len, which is less than the min items {{minItems}}") + _allValidationErrors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"{{name}} has $len, which is less than the min items {{minItems}}") } } {{/minItems}} {{#maxItems}} // validate min items {{maxItems}} - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { val len = if {{name}} == null then 0 else {{name}}.size if (len > {{maxItems}}) { - errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"{{name}} has $len, which is greater than the max items {{maxItems}}") + _allValidationErrors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"{{name}} has $len, which is greater than the max items {{maxItems}}") } } {{/maxItems}} {{#minProperties}} TODO - minProperties {{/minProperties}} {{#maxProperties}} TODO - maxProperties {{/maxProperties}} {{#items}}{{#isModel}} - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { if ({{name}} != null) { {{name}}.zipWithIndex.foreach { - case (value, i) if errors.isEmpty || !failFast => - errors ++= value.validationErrors( + case (value, i) if _allValidationErrors.isEmpty || !failFast => + _allValidationErrors ++= value.validationErrors( path :+ {{classname}}.Fields.{{name}} :+ Field(i.toString), failFast) case (value, i) => @@ -148,13 +148,13 @@ case class {{classname}}Data( {{/isModel}}{{/items}} {{#isModel}} // validating {{name}} - if (errors.isEmpty || !failFast) { - if {{name}} != null then errors ++= {{name}}.validationErrors(path :+ {{classname}}.Fields.{{name}}, failFast) + if (_allValidationErrors.isEmpty || !failFast) { + if {{name}} != null then _allValidationErrors ++= {{name}}.validationErrors(path :+ {{classname}}.Fields.{{name}}, failFast) } {{/isModel}} {{/vars}} - errors.toSeq + _allValidationErrors.toSeq } /** @@ -180,6 +180,8 @@ case class {{classname}}Data( object {{classname}}Data { + def validated(d8a : {{classname}}Data, failFast : Boolean) : scala.util.Try[{{classname}}] = d8a.validated(failFast) + def fromJson(jason : ujson.Value) : {{classname}}Data = try { val data = read[{{classname}}Data](jason) {{^isAdditionalPropertiesTrue}} diff --git a/modules/openapi-generator/src/main/resources/scala-cask/modelTest.mustache b/modules/openapi-generator/src/main/resources/scala-cask/modelTest.mustache index 7b1d95b5aaba6..bed1a908d2eaa 100644 --- a/modules/openapi-generator/src/main/resources/scala-cask/modelTest.mustache +++ b/modules/openapi-generator/src/main/resources/scala-cask/modelTest.mustache @@ -28,7 +28,8 @@ class {{classname}}Test extends AnyWordSpec with Matchers { err.getMessage should startWith ("Error parsing json 'invalid jason'") } """parse {{example}}""" ignore { - val Failure(err : ValidationErrors) = {{classname}}Data.fromJsonString("""{{example}}""").validated() + val d8a = {{classname}}Data.fromJsonString("""{{example}}""") + val Failure(err : ValidationErrors) = {{classname}}Data.validated(d8a, true) sys.error("TODO") } diff --git a/modules/openapi-generator/src/main/resources/scala-cask/parseHttpParams.mustache b/modules/openapi-generator/src/main/resources/scala-cask/parseHttpParams.mustache index ebbd63e2c05e6..7957edeb5bb7b 100644 --- a/modules/openapi-generator/src/main/resources/scala-cask/parseHttpParams.mustache +++ b/modules/openapi-generator/src/main/resources/scala-cask/parseHttpParams.mustache @@ -40,7 +40,10 @@ {{^isMap}} {{paramName}}Json <- Parsed.fromTry(request.bodyAsJson) {{paramName}}Data <- Parsed.eval({{vendorExtensions.x-container-type}}Data.fromJson({{paramName}}Json)) /* not array or map */ - {{paramName}} <- Parsed.fromTry({{paramName}}Data.validated(failFast)) + {{paramName}}{{^required}}Opt{{/required}} <- Parsed.fromTry({{vendorExtensions.x-container-type}}Data.validated({{paramName}}Data, failFast)) + {{^required}} + {{paramName}} = Option({{paramName}}Opt) + {{/required}} {{/isMap}} {{/isArray}} {{/vendorExtensions.x-consumes-json}} diff --git a/modules/openapi-generator/src/main/resources/scala-cask/project/build.properties b/modules/openapi-generator/src/main/resources/scala-cask/project/build.properties index 04267b14af698..bc7390601f4e6 100644 --- a/modules/openapi-generator/src/main/resources/scala-cask/project/build.properties +++ b/modules/openapi-generator/src/main/resources/scala-cask/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.9 +sbt.version=1.10.3 diff --git a/modules/openapi-generator/src/main/resources/scala-cask/project/plugins.sbt b/modules/openapi-generator/src/main/resources/scala-cask/project/plugins.sbt index ece317dfad9ba..9c4d009e2906e 100644 --- a/modules/openapi-generator/src/main/resources/scala-cask/project/plugins.sbt +++ b/modules/openapi-generator/src/main/resources/scala-cask/project/plugins.sbt @@ -1,4 +1,4 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0") \ No newline at end of file diff --git a/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/PetRoutes.scala b/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/PetRoutes.scala index da02bbb338d69..972b15d30e98d 100644 --- a/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/PetRoutes.scala +++ b/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/PetRoutes.scala @@ -63,11 +63,13 @@ class PetRoutes(service : PetService[Try]) extends cask.Routes { val result = for { petJson <- Parsed.fromTry(request.bodyAsJson) petData <- Parsed.eval(PetData.fromJson(petJson)) /* not array or map */ - pet <- Parsed.fromTry(petData.validated(failFast)) + pet <- Parsed.fromTry(PetData.validated(petData, failFast)) resultTry <- Parsed.eval(service.addPet(pet)) result <- Parsed.fromTry(resultTry) } yield result + import Pet.{given, *} // this brings in upickle in the case of union (oneOf) types + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : Pet) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) @@ -90,6 +92,7 @@ class PetRoutes(service : PetService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(other) => cask.Response(s"$other", 200) @@ -109,6 +112,7 @@ class PetRoutes(service : PetService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : List[Pet]) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) @@ -129,6 +133,7 @@ class PetRoutes(service : PetService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : List[Pet]) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) @@ -150,6 +155,8 @@ class PetRoutes(service : PetService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + import Pet.{given, *} // this brings in upickle in the case of union (oneOf) types + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : Pet) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) @@ -168,11 +175,13 @@ class PetRoutes(service : PetService[Try]) extends cask.Routes { val result = for { petJson <- Parsed.fromTry(request.bodyAsJson) petData <- Parsed.eval(PetData.fromJson(petJson)) /* not array or map */ - pet <- Parsed.fromTry(petData.validated(failFast)) + pet <- Parsed.fromTry(PetData.validated(petData, failFast)) resultTry <- Parsed.eval(service.updatePet(pet)) result <- Parsed.fromTry(resultTry) } yield result + import Pet.{given, *} // this brings in upickle in the case of union (oneOf) types + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : Pet) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) @@ -196,6 +205,7 @@ class PetRoutes(service : PetService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(other) => cask.Response(s"$other", 200) @@ -218,6 +228,8 @@ class PetRoutes(service : PetService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + import ApiResponse.{given, *} // this brings in upickle in the case of union (oneOf) types + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : ApiResponse) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) diff --git a/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/StoreRoutes.scala b/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/StoreRoutes.scala index 5c3414564e5ff..5cea1d14b35cc 100644 --- a/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/StoreRoutes.scala +++ b/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/StoreRoutes.scala @@ -43,6 +43,7 @@ class StoreRoutes(service : StoreService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(other) => cask.Response(s"$other", 200) @@ -62,6 +63,7 @@ class StoreRoutes(service : StoreService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : Map[String, Int]) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) @@ -82,6 +84,8 @@ class StoreRoutes(service : StoreService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + import Order.{given, *} // this brings in upickle in the case of union (oneOf) types + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : Order) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) @@ -99,11 +103,13 @@ class StoreRoutes(service : StoreService[Try]) extends cask.Routes { val result = for { orderJson <- Parsed.fromTry(request.bodyAsJson) orderData <- Parsed.eval(OrderData.fromJson(orderJson)) /* not array or map */ - order <- Parsed.fromTry(orderData.validated(failFast)) + order <- Parsed.fromTry(OrderData.validated(orderData, failFast)) resultTry <- Parsed.eval(service.placeOrder(order)) result <- Parsed.fromTry(resultTry) } yield result + import Order.{given, *} // this brings in upickle in the case of union (oneOf) types + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : Order) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) diff --git a/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/UserRoutes.scala b/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/UserRoutes.scala index 63c01ea5583e7..5cb1b56288c0e 100644 --- a/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/UserRoutes.scala +++ b/samples/server/petstore/scala-cask/jvm/src/main/scala/sample/cask/api/UserRoutes.scala @@ -52,11 +52,12 @@ class UserRoutes(service : UserService[Try]) extends cask.Routes { val result = for { userJson <- Parsed.fromTry(request.bodyAsJson) userData <- Parsed.eval(UserData.fromJson(userJson)) /* not array or map */ - user <- Parsed.fromTry(userData.validated(failFast)) + user <- Parsed.fromTry(UserData.validated(userData, failFast)) resultTry <- Parsed.eval(service.createUser(user)) result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(other) => cask.Response(s"$other", 200) @@ -77,6 +78,7 @@ class UserRoutes(service : UserService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(other) => cask.Response(s"$other", 200) @@ -97,6 +99,7 @@ class UserRoutes(service : UserService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(other) => cask.Response(s"$other", 200) @@ -117,6 +120,7 @@ class UserRoutes(service : UserService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(other) => cask.Response(s"$other", 200) @@ -136,6 +140,8 @@ class UserRoutes(service : UserService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + import User.{given, *} // this brings in upickle in the case of union (oneOf) types + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : User) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) @@ -155,6 +161,7 @@ class UserRoutes(service : UserService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(value : String) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json")) @@ -175,6 +182,7 @@ class UserRoutes(service : UserService[Try]) extends cask.Routes { result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(other) => cask.Response(s"$other", 200) @@ -193,11 +201,12 @@ class UserRoutes(service : UserService[Try]) extends cask.Routes { username <- Parsed(username) userJson <- Parsed.fromTry(request.bodyAsJson) userData <- Parsed.eval(UserData.fromJson(userJson)) /* not array or map */ - user <- Parsed.fromTry(userData.validated(failFast)) + user <- Parsed.fromTry(UserData.validated(userData, failFast)) resultTry <- Parsed.eval(service.updateUser(username, user)) result <- Parsed.fromTry(resultTry) } yield result + (result : @unchecked) match { case Left(error) => cask.Response(error, 500) case Right(other) => cask.Response(s"$other", 200) diff --git a/samples/server/petstore/scala-cask/project/build.properties b/samples/server/petstore/scala-cask/project/build.properties index 04267b14af698..bc7390601f4e6 100644 --- a/samples/server/petstore/scala-cask/project/build.properties +++ b/samples/server/petstore/scala-cask/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.9 +sbt.version=1.10.3 diff --git a/samples/server/petstore/scala-cask/project/plugins.sbt b/samples/server/petstore/scala-cask/project/plugins.sbt index ece317dfad9ba..9c4d009e2906e 100644 --- a/samples/server/petstore/scala-cask/project/plugins.sbt +++ b/samples/server/petstore/scala-cask/project/plugins.sbt @@ -1,4 +1,4 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.17.0") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0") \ No newline at end of file diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/ApiResponse.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/ApiResponse.scala index cafa77116452e..0307a91039da8 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/ApiResponse.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/ApiResponse.scala @@ -21,7 +21,7 @@ import upickle.default.{ReadWriter => RW, macroRW} import upickle.default.* - + case class ApiResponse( code: Option[Int] = None , `type`: Option[String] = None , @@ -55,3 +55,4 @@ enum Fields(val fieldName : String) extends Field(fieldName) { } + diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/ApiResponseData.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/ApiResponseData.scala index 4c80e1c743fe7..df95d64e14129 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/ApiResponseData.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/ApiResponseData.scala @@ -40,7 +40,7 @@ case class ApiResponseData( } def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = { - val errors = scala.collection.mutable.ListBuffer[ValidationError]() + val _allValidationErrors = scala.collection.mutable.ListBuffer[ValidationError]() // ================== code validation ================== @@ -59,7 +59,7 @@ case class ApiResponseData( - errors.toSeq + _allValidationErrors.toSeq } /** @@ -85,6 +85,8 @@ case class ApiResponseData( object ApiResponseData { + def validated(d8a : ApiResponseData, failFast : Boolean) : scala.util.Try[ApiResponse] = d8a.validated(failFast) + def fromJson(jason : ujson.Value) : ApiResponseData = try { val data = read[ApiResponseData](jason) data diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Category.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Category.scala index e89cbd7e81e6e..34eb6b98be430 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Category.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Category.scala @@ -21,7 +21,7 @@ import upickle.default.{ReadWriter => RW, macroRW} import upickle.default.* - + case class Category( id: Option[Long] = None , name: Option[String] = None @@ -52,3 +52,4 @@ enum Fields(val fieldName : String) extends Field(fieldName) { } + diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/CategoryData.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/CategoryData.scala index 7eb8a94897019..ab187bf5f6690 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/CategoryData.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/CategoryData.scala @@ -39,7 +39,7 @@ case class CategoryData( } def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = { - val errors = scala.collection.mutable.ListBuffer[ValidationError]() + val _allValidationErrors = scala.collection.mutable.ListBuffer[ValidationError]() // ================== id validation ================== @@ -48,17 +48,17 @@ case class CategoryData( // ================== name validation ================== // validate against pattern '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$' - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { val regex = """^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$""" if name == null || !regex.r.matches(name) then - errors += ValidationError(path :+ Category.Fields.name, s"value '$name' doesn't match pattern $regex") + _allValidationErrors += ValidationError(path :+ Category.Fields.name, s"value '$name' doesn't match pattern $regex") } - errors.toSeq + _allValidationErrors.toSeq } /** @@ -83,6 +83,8 @@ case class CategoryData( object CategoryData { + def validated(d8a : CategoryData, failFast : Boolean) : scala.util.Try[Category] = d8a.validated(failFast) + def fromJson(jason : ujson.Value) : CategoryData = try { val data = read[CategoryData](jason) data diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Order.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Order.scala index 77dbcd6793b40..03b50eae2a9c1 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Order.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Order.scala @@ -22,7 +22,7 @@ import upickle.default.{ReadWriter => RW, macroRW} import upickle.default.* - + case class Order( id: Option[Long] = None , petId: Option[Long] = None , @@ -73,3 +73,4 @@ enum Fields(val fieldName : String) extends Field(fieldName) { } + diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/OrderData.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/OrderData.scala index 1330c2111c5a8..2ef08f14aa69d 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/OrderData.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/OrderData.scala @@ -45,7 +45,7 @@ case class OrderData( } def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = { - val errors = scala.collection.mutable.ListBuffer[ValidationError]() + val _allValidationErrors = scala.collection.mutable.ListBuffer[ValidationError]() // ================== id validation ================== @@ -82,7 +82,7 @@ case class OrderData( - errors.toSeq + _allValidationErrors.toSeq } /** @@ -111,6 +111,8 @@ case class OrderData( object OrderData { + def validated(d8a : OrderData, failFast : Boolean) : scala.util.Try[Order] = d8a.validated(failFast) + def fromJson(jason : ujson.Value) : OrderData = try { val data = read[OrderData](jason) data diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Pet.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Pet.scala index 58725e10dccc4..041aab7f6ed80 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Pet.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Pet.scala @@ -21,7 +21,7 @@ import upickle.default.{ReadWriter => RW, macroRW} import upickle.default.* - + case class Pet( id: Option[Long] = None , category: Option[Category] = None , @@ -72,3 +72,4 @@ enum Fields(val fieldName : String) extends Field(fieldName) { } + diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/PetData.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/PetData.scala index 755d33c288d6b..126c81abe8857 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/PetData.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/PetData.scala @@ -44,7 +44,7 @@ case class PetData( } def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = { - val errors = scala.collection.mutable.ListBuffer[ValidationError]() + val _allValidationErrors = scala.collection.mutable.ListBuffer[ValidationError]() // ================== id validation ================== @@ -57,8 +57,8 @@ case class PetData( // validating category - if (errors.isEmpty || !failFast) { - if category != null then errors ++= category.validationErrors(path :+ Pet.Fields.category, failFast) + if (_allValidationErrors.isEmpty || !failFast) { + if category != null then _allValidationErrors ++= category.validationErrors(path :+ Pet.Fields.category, failFast) } // ================== name validation ================== @@ -78,11 +78,11 @@ case class PetData( - if (errors.isEmpty || !failFast) { + if (_allValidationErrors.isEmpty || !failFast) { if (tags != null) { tags.zipWithIndex.foreach { - case (value, i) if errors.isEmpty || !failFast => - errors ++= value.validationErrors( + case (value, i) if _allValidationErrors.isEmpty || !failFast => + _allValidationErrors ++= value.validationErrors( path :+ Pet.Fields.tags :+ Field(i.toString), failFast) case (value, i) => @@ -97,7 +97,7 @@ case class PetData( - errors.toSeq + _allValidationErrors.toSeq } /** @@ -126,6 +126,8 @@ case class PetData( object PetData { + def validated(d8a : PetData, failFast : Boolean) : scala.util.Try[Pet] = d8a.validated(failFast) + def fromJson(jason : ujson.Value) : PetData = try { val data = read[PetData](jason) data diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Tag.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Tag.scala index fa6805f6c33c9..05ff4fcc9d6b5 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Tag.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/Tag.scala @@ -21,7 +21,7 @@ import upickle.default.{ReadWriter => RW, macroRW} import upickle.default.* - + case class Tag( id: Option[Long] = None , name: Option[String] = None @@ -52,3 +52,4 @@ enum Fields(val fieldName : String) extends Field(fieldName) { } + diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/TagData.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/TagData.scala index 972b21dd518f2..35db0cdb3828b 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/TagData.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/TagData.scala @@ -39,7 +39,7 @@ case class TagData( } def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = { - val errors = scala.collection.mutable.ListBuffer[ValidationError]() + val _allValidationErrors = scala.collection.mutable.ListBuffer[ValidationError]() // ================== id validation ================== @@ -52,7 +52,7 @@ case class TagData( - errors.toSeq + _allValidationErrors.toSeq } /** @@ -77,6 +77,8 @@ case class TagData( object TagData { + def validated(d8a : TagData, failFast : Boolean) : scala.util.Try[Tag] = d8a.validated(failFast) + def fromJson(jason : ujson.Value) : TagData = try { val data = read[TagData](jason) data diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/User.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/User.scala index 55614de4f8734..5e432c0b0430e 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/User.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/User.scala @@ -21,7 +21,7 @@ import upickle.default.{ReadWriter => RW, macroRW} import upickle.default.* - + case class User( id: Option[Long] = None , username: Option[String] = None , @@ -71,3 +71,4 @@ enum Fields(val fieldName : String) extends Field(fieldName) { } + diff --git a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/UserData.scala b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/UserData.scala index 07b5cb1de4176..dea3a5927b133 100644 --- a/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/UserData.scala +++ b/samples/server/petstore/scala-cask/shared/src/main/scala/sample/cask/model/UserData.scala @@ -46,7 +46,7 @@ case class UserData( } def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = { - val errors = scala.collection.mutable.ListBuffer[ValidationError]() + val _allValidationErrors = scala.collection.mutable.ListBuffer[ValidationError]() // ================== id validation ================== @@ -95,7 +95,7 @@ case class UserData( - errors.toSeq + _allValidationErrors.toSeq } /** @@ -126,6 +126,8 @@ case class UserData( object UserData { + def validated(d8a : UserData, failFast : Boolean) : scala.util.Try[User] = d8a.validated(failFast) + def fromJson(jason : ujson.Value) : UserData = try { val data = read[UserData](jason) data