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