forked from loafle/openapi-generator-original
scala-cask fix: Added support for 'additionalProperties:true' (#19767)
* Added support for 'additionalProperties:true' to scala-cask generator additionalProperties means the request can contain arbitrary additional properties, and so this change adds an 'additionalProperties' field to request objects which is a json type. * fixed warning in example scala-cli project * updated samples * addressed codegen comments
This commit is contained in:
parent
d60200de38
commit
31be9b9207
@ -3158,7 +3158,7 @@ public class DefaultCodegen implements CodegenConfig {
|
|||||||
additionalPropertiesIsAnyType = true;
|
additionalPropertiesIsAnyType = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if additioanl properties is set (e.g. free form object, any type, string, etc)
|
// if additional properties is set (e.g. free form object, any type, string, etc)
|
||||||
addPropProp = fromProperty(getAdditionalPropertiesName(), (Schema) schema.getAdditionalProperties(), false);
|
addPropProp = fromProperty(getAdditionalPropertiesName(), (Schema) schema.getAdditionalProperties(), false);
|
||||||
additionalPropertiesIsAnyType = true;
|
additionalPropertiesIsAnyType = true;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,10 @@ import static org.openapitools.codegen.utils.StringUtils.camelize;
|
|||||||
public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements CodegenConfig {
|
public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements CodegenConfig {
|
||||||
public static final String PROJECT_NAME = "projectName";
|
public static final String PROJECT_NAME = "projectName";
|
||||||
|
|
||||||
|
// this is our opinionated json type - ujson.Value - which is a first-class
|
||||||
|
// citizen of cask
|
||||||
|
private static final String AdditionalPropertiesType = "Value";
|
||||||
|
|
||||||
private final Logger LOGGER = LoggerFactory.getLogger(ScalaCaskServerCodegen.class);
|
private final Logger LOGGER = LoggerFactory.getLogger(ScalaCaskServerCodegen.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -115,6 +119,8 @@ public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements Code
|
|||||||
|
|
||||||
typeMapping.put("integer", "Int");
|
typeMapping.put("integer", "Int");
|
||||||
typeMapping.put("long", "Long");
|
typeMapping.put("long", "Long");
|
||||||
|
typeMapping.put("AnyType", AdditionalPropertiesType);
|
||||||
|
|
||||||
//TODO binary should be mapped to byte array
|
//TODO binary should be mapped to byte array
|
||||||
// mapped to String as a workaround
|
// mapped to String as a workaround
|
||||||
typeMapping.put("binary", "String");
|
typeMapping.put("binary", "String");
|
||||||
@ -241,6 +247,7 @@ public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements Code
|
|||||||
importMapping.put("OffsetDateTime", "java.time.OffsetDateTime");
|
importMapping.put("OffsetDateTime", "java.time.OffsetDateTime");
|
||||||
importMapping.put("LocalTime", "java.time.LocalTime");
|
importMapping.put("LocalTime", "java.time.LocalTime");
|
||||||
importMapping.put("Value", "ujson.Value");
|
importMapping.put("Value", "ujson.Value");
|
||||||
|
importMapping.put(AdditionalPropertiesType, "ujson.Value");
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean consumesMimetype(CodegenOperation op, String mimetype) {
|
static boolean consumesMimetype(CodegenOperation op, String mimetype) {
|
||||||
@ -614,7 +621,7 @@ public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements Code
|
|||||||
if (p.getIsEnumOrRef()) {
|
if (p.getIsEnumOrRef()) {
|
||||||
p.defaultValue = "null";
|
p.defaultValue = "null";
|
||||||
} else {
|
} else {
|
||||||
p.defaultValue = defaultValueNonOption(p);
|
p.defaultValue = defaultValueNonOption(p, "null");
|
||||||
}
|
}
|
||||||
} else if (p.defaultValue.contains("Seq.empty")) {
|
} else if (p.defaultValue.contains("Seq.empty")) {
|
||||||
p.defaultValue = "Nil";
|
p.defaultValue = "Nil";
|
||||||
@ -767,6 +774,23 @@ public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements Code
|
|||||||
return defaultValueNonOption(p, fallbackDefaultValue);
|
return defaultValueNonOption(p, fallbackDefaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the subtypes of IJsonSchemaValidationProperties have an 'isNumeric', but that's not a method on IJsonSchemaValidationProperties.
|
||||||
|
*
|
||||||
|
* This helper method tries to isolate that noisy logic in a safe way so we can ask 'is this IJsonSchemaValidationProperties numeric'?
|
||||||
|
* @param p the property
|
||||||
|
* @return true if the property is numeric
|
||||||
|
*/
|
||||||
|
private static boolean isNumeric(IJsonSchemaValidationProperties p) {
|
||||||
|
if (p instanceof CodegenParameter) {
|
||||||
|
return ((CodegenParameter)p).isNumeric;
|
||||||
|
} else if (p instanceof CodegenProperty) {
|
||||||
|
return ((CodegenProperty)p).isNumeric;
|
||||||
|
} else {
|
||||||
|
return p.getIsNumber() || p.getIsFloat() || p.getIsDecimal() || p.getIsDouble() || p.getIsInteger() || p.getIsLong() || p.getIsUnboundedInteger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String defaultValueNonOption(IJsonSchemaValidationProperties p, String fallbackDefaultValue) {
|
private static String defaultValueNonOption(IJsonSchemaValidationProperties p, String fallbackDefaultValue) {
|
||||||
if (p.getIsArray()) {
|
if (p.getIsArray()) {
|
||||||
if (p.getUniqueItems()) {
|
if (p.getUniqueItems()) {
|
||||||
@ -777,7 +801,7 @@ public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements Code
|
|||||||
if (p.getIsMap()) {
|
if (p.getIsMap()) {
|
||||||
return "Map.empty";
|
return "Map.empty";
|
||||||
}
|
}
|
||||||
if (p.getIsNumber()) {
|
if (isNumeric(p)) {
|
||||||
return "0";
|
return "0";
|
||||||
}
|
}
|
||||||
if (p.getIsEnum()) {
|
if (p.getIsEnum()) {
|
||||||
@ -792,37 +816,12 @@ public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements Code
|
|||||||
if (p.getIsString()) {
|
if (p.getIsString()) {
|
||||||
return "\"\"";
|
return "\"\"";
|
||||||
}
|
}
|
||||||
|
if (fallbackDefaultValue != null && !fallbackDefaultValue.trim().isEmpty()) {
|
||||||
return fallbackDefaultValue;
|
return fallbackDefaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String defaultValueNonOption(CodegenProperty p) {
|
|
||||||
if (p.getIsArray()) {
|
|
||||||
return "Nil";
|
|
||||||
}
|
|
||||||
if (p.getIsMap()) {
|
|
||||||
return "Map.empty";
|
|
||||||
}
|
|
||||||
if (p.isNumber || p.isNumeric) {
|
|
||||||
return "0";
|
|
||||||
}
|
|
||||||
if (p.isBoolean) {
|
|
||||||
return "false";
|
|
||||||
}
|
|
||||||
if (p.isUuid) {
|
|
||||||
return "java.util.UUID.randomUUID()";
|
|
||||||
}
|
|
||||||
if (p.isModel) {
|
|
||||||
return "null";
|
return "null";
|
||||||
}
|
}
|
||||||
if (p.isDate || p.isDateTime) {
|
|
||||||
return "null";
|
|
||||||
}
|
|
||||||
if (p.isString) {
|
|
||||||
return "\"\"";
|
|
||||||
}
|
|
||||||
return p.defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodegenProperty fromProperty(String name, Schema schema) {
|
public CodegenProperty fromProperty(String name, Schema schema) {
|
||||||
@ -847,9 +846,9 @@ public class ScalaCaskServerCodegen extends AbstractScalaCodegen implements Code
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toModelImport(String name) {
|
public String toModelImport(String name) {
|
||||||
final String result = super.toModelImport(name);
|
String result = super.toModelImport(name);
|
||||||
if (importMapping.containsKey(name)) {
|
if (importMapping.containsKey(name)) {
|
||||||
return importMapping.get(name);
|
result = importMapping.get(name);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import java.time.LocalDate
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
import scala.util.*
|
import scala.util.*
|
||||||
|
import upickle.default.*
|
||||||
|
|
||||||
// needed for BigDecimal params
|
// needed for BigDecimal params
|
||||||
given cask.endpoints.QueryParamReader.SimpleParam[BigDecimal](BigDecimal.apply)
|
given cask.endpoints.QueryParamReader.SimpleParam[BigDecimal](BigDecimal.apply)
|
||||||
@ -143,6 +144,15 @@ extension (request: cask.Request) {
|
|||||||
|
|
||||||
def bodyAsString = new String(request.readAllBytes(), "UTF-8")
|
def bodyAsString = new String(request.readAllBytes(), "UTF-8")
|
||||||
|
|
||||||
|
def bodyAsJson : Try[ujson.Value] = {
|
||||||
|
val jason = bodyAsString
|
||||||
|
try {
|
||||||
|
Success(read[ujson.Value](jason))
|
||||||
|
} catch {
|
||||||
|
case scala.util.control.NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def pathValue(index: Int, paramName: String, required : Boolean): Parsed[String] = {
|
def pathValue(index: Int, paramName: String, required : Boolean): Parsed[String] = {
|
||||||
request
|
request
|
||||||
.remainingPathSegments
|
.remainingPathSegments
|
||||||
|
@ -44,7 +44,7 @@ class {{classname}}Routes(service : {{classname}}Service) extends cask.Routes {
|
|||||||
|
|
||||||
val result = {{>parseHttpParams}}
|
val result = {{>parseHttpParams}}
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
{{#responses}}
|
{{#responses}}
|
||||||
{{#dataType}}
|
{{#dataType}}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//> using scala "3.3.1"
|
//> using scala "3.3.1"
|
||||||
//> using lib "com.lihaoyi::cask:0.9.2"
|
//> using dep "com.lihaoyi::cask:0.9.2"
|
||||||
//> using lib "com.lihaoyi::scalatags:0.8.2"
|
//> using dep "com.lihaoyi::scalatags:0.8.2"
|
||||||
{{>licenseInfo}}
|
{{>licenseInfo}}
|
||||||
|
|
||||||
// this file was generated from app.mustache
|
// this file was generated from app.mustache
|
||||||
@ -11,11 +11,21 @@ package {{packageName}}
|
|||||||
import _root_.{{modelPackage}}.*
|
import _root_.{{modelPackage}}.*
|
||||||
import _root_.{{apiPackage}}.*
|
import _root_.{{apiPackage}}.*
|
||||||
|
|
||||||
|
/** an example of how you can add your own additional routes to your app */
|
||||||
|
object MoreRoutes extends cask.Routes {
|
||||||
|
@cask.get("/echo")
|
||||||
|
def more(request: cask.Request) = s"request was ${request.bodyAsString}"
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an example of how you might extends BaseApp for a runnable application.
|
* This is an example of how you might extends BaseApp for a runnable application.
|
||||||
*
|
*
|
||||||
* See the README.md for how to create your own app
|
* See the README.md for how to create your own app
|
||||||
*/
|
*/
|
||||||
object ExampleApp extends BaseApp() {
|
object ExampleApp extends BaseApp() {
|
||||||
|
// override to include our additional route
|
||||||
|
override def allRoutes = super.allRoutes ++ Option(MoreRoutes)
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
|
@ -17,26 +17,28 @@ case class {{classname}}(
|
|||||||
/* {{{description}}} */
|
/* {{{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}}{{^required}}Option[{{/required}}{{classname}}.{{datatypeWithEnum}}{{^required}}]{{/required}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.x-datatype-model}}}{{/isEnum}}{{^required}} = {{{vendorExtensions.x-defaultValue-model}}} {{/required}}{{^-last}},{{/-last}}
|
||||||
|
{{/vars}}
|
||||||
|
|
||||||
{{/vars}}) {
|
{{#isAdditionalPropertiesTrue}}, additionalProperties : ujson.Value = ujson.Null{{/isAdditionalPropertiesTrue}}
|
||||||
|
) {
|
||||||
|
|
||||||
def asJson: String = asData.asJson
|
def asJsonString: String = asData.asJsonString
|
||||||
|
def asJson: ujson.Value = asData.asJson
|
||||||
|
|
||||||
def asData : {{classname}}Data = {
|
def asData : {{classname}}Data = {
|
||||||
{{classname}}Data(
|
{{classname}}Data(
|
||||||
{{#vars}}
|
{{#vars}}
|
||||||
{{name}} = {{name}}{{#vendorExtensions.x-map-asModel}}.map(_.asData){{/vendorExtensions.x-map-asModel}}{{#vendorExtensions.x-wrap-in-optional}}.getOrElse({{{defaultValue}}}){{/vendorExtensions.x-wrap-in-optional}}{{^-last}},{{/-last}}
|
{{name}} = {{name}}{{#vendorExtensions.x-map-asModel}}.map(_.asData){{/vendorExtensions.x-map-asModel}}{{#vendorExtensions.x-wrap-in-optional}}.getOrElse({{{defaultValue}}}){{/vendorExtensions.x-wrap-in-optional}}{{^-last}},{{/-last}}
|
||||||
{{/vars}}
|
{{/vars}}
|
||||||
|
{{#isAdditionalPropertiesTrue}}, additionalProperties{{/isAdditionalPropertiesTrue}}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object {{classname}} {
|
object {{classname}} {
|
||||||
|
given RW[{{classname}}] = summon[RW[ujson.Value]].bimap[{{classname}}](_.asJson, json => read[{{classname}}Data](json).asModel)
|
||||||
|
|
||||||
given RW[{{classname}}] = {{classname}}Data.readWriter.bimap[{{classname}}](_.asData, _.asModel)
|
enum Fields(val fieldName : String) extends Field(fieldName) {
|
||||||
|
|
||||||
enum Fields(fieldName : String) extends Field(fieldName) {
|
|
||||||
{{#vars}}
|
{{#vars}}
|
||||||
case {{name}} extends Fields("{{name}}")
|
case {{name}} extends Fields("{{name}}")
|
||||||
{{/vars}}
|
{{/vars}}
|
||||||
|
@ -21,16 +21,28 @@ case class {{classname}}Data(
|
|||||||
/* {{{description}}} */
|
/* {{{description}}} */
|
||||||
{{/description}}
|
{{/description}}
|
||||||
{{name}}: {{#isEnum}}{{classname}}.{{datatypeWithEnum}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.x-datatype-data}}}{{/isEnum}}{{^required}} = {{{vendorExtensions.x-defaultValue-data}}} {{/required}}{{^-last}},{{/-last}}
|
{{name}}: {{#isEnum}}{{classname}}.{{datatypeWithEnum}}{{/isEnum}}{{^isEnum}}{{{vendorExtensions.x-datatype-data}}}{{/isEnum}}{{^required}} = {{{vendorExtensions.x-defaultValue-data}}} {{/required}}{{^-last}},{{/-last}}
|
||||||
|
{{/vars}}
|
||||||
|
{{#isAdditionalPropertiesTrue}}, additionalProperties : ujson.Value = ujson.Null{{/isAdditionalPropertiesTrue}}
|
||||||
|
|
||||||
{{/vars}}) {
|
) derives RW {
|
||||||
|
|
||||||
def asJson: String = write(this)
|
def asJsonString: String = asJson.toString()
|
||||||
|
|
||||||
|
def asJson : ujson.Value = {
|
||||||
|
val jason = writeJs(this)
|
||||||
|
{{#isAdditionalPropertiesTrue}}
|
||||||
|
jason.obj.remove("additionalProperties")
|
||||||
|
jason.mergeWith(additionalProperties)
|
||||||
|
{{/isAdditionalPropertiesTrue}}
|
||||||
|
{{^isAdditionalPropertiesTrue}}
|
||||||
|
jason
|
||||||
|
{{/isAdditionalPropertiesTrue}}
|
||||||
|
}
|
||||||
|
|
||||||
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
||||||
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
||||||
{{#vars}}
|
{{#vars}}
|
||||||
// ==================
|
// ================== {{name}} validation ==================
|
||||||
// {{name}}
|
|
||||||
{{#pattern}}
|
{{#pattern}}
|
||||||
// validate against pattern '{{{pattern}}}'
|
// validate against pattern '{{{pattern}}}'
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
@ -39,7 +51,6 @@ case class {{classname}}Data(
|
|||||||
errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' doesn't match pattern $regex")
|
errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' doesn't match pattern $regex")
|
||||||
}
|
}
|
||||||
{{/pattern}}
|
{{/pattern}}
|
||||||
|
|
||||||
{{#minimum}}
|
{{#minimum}}
|
||||||
// validate against {{#exclusiveMinimum}}exclusive {{/exclusiveMinimum}}minimum {{minimum}}
|
// validate against {{#exclusiveMinimum}}exclusive {{/exclusiveMinimum}}minimum {{minimum}}
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
@ -47,7 +58,6 @@ case class {{classname}}Data(
|
|||||||
errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' is not greater than the {{#exclusiveMinimum}}exclusive {{/exclusiveMinimum}}minimum value {{minimum}}")
|
errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' is not greater than the {{#exclusiveMinimum}}exclusive {{/exclusiveMinimum}}minimum value {{minimum}}")
|
||||||
}
|
}
|
||||||
{{/minimum}}
|
{{/minimum}}
|
||||||
|
|
||||||
{{#maximum}}
|
{{#maximum}}
|
||||||
// validate against {{#exclusiveMaximum}}exclusive {{/exclusiveMaximum}}maximum {{maximum}}
|
// validate against {{#exclusiveMaximum}}exclusive {{/exclusiveMaximum}}maximum {{maximum}}
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
@ -55,7 +65,6 @@ case class {{classname}}Data(
|
|||||||
errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' is not greater than the {{#exclusiveMaximum}}exclusive {{/exclusiveMaximum}}maximum value {{maximum}}")
|
errors += ValidationError(path :+ {{classname}}.Fields.{{name}}, s"value '${{name}}' is not greater than the {{#exclusiveMaximum}}exclusive {{/exclusiveMaximum}}maximum value {{maximum}}")
|
||||||
}
|
}
|
||||||
{{/maximum}}
|
{{/maximum}}
|
||||||
|
|
||||||
{{#minLength}}
|
{{#minLength}}
|
||||||
// validate min length {{minLength}}
|
// validate min length {{minLength}}
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
@ -65,7 +74,6 @@ case class {{classname}}Data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{/minLength}}
|
{{/minLength}}
|
||||||
|
|
||||||
{{#maxLength}}
|
{{#maxLength}}
|
||||||
// validate max length {{maxLength}}
|
// validate max length {{maxLength}}
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
@ -75,7 +83,6 @@ case class {{classname}}Data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{/maxLength}}
|
{{/maxLength}}
|
||||||
|
|
||||||
{{#isEmail}}
|
{{#isEmail}}
|
||||||
// validate {{name}} is a valid email address
|
// validate {{name}} is a valid email address
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
@ -86,7 +93,6 @@ case class {{classname}}Data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{/isEmail}}
|
{{/isEmail}}
|
||||||
|
|
||||||
{{#required}}{{^isPrimitiveType}}
|
{{#required}}{{^isPrimitiveType}}
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
if ({{name}} == null) {
|
if ({{name}} == null) {
|
||||||
@ -94,7 +100,6 @@ case class {{classname}}Data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{/isPrimitiveType}}{{/required}}
|
{{/isPrimitiveType}}{{/required}}
|
||||||
|
|
||||||
{{#uniqueItems}}
|
{{#uniqueItems}}
|
||||||
// validate {{name}} has unique items
|
// validate {{name}} has unique items
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
@ -111,7 +116,6 @@ case class {{classname}}Data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{/uniqueItems}}
|
{{/uniqueItems}}
|
||||||
|
|
||||||
{{#multipleOf}}
|
{{#multipleOf}}
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
// validate {{name}} multiple of {{multipleOf}}
|
// validate {{name}} multiple of {{multipleOf}}
|
||||||
@ -123,7 +127,6 @@ case class {{classname}}Data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{/multipleOf}}
|
{{/multipleOf}}
|
||||||
|
|
||||||
{{#minItems}}
|
{{#minItems}}
|
||||||
// validate min items {{minItems}}
|
// validate min items {{minItems}}
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
@ -133,7 +136,6 @@ case class {{classname}}Data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{/minItems}}
|
{{/minItems}}
|
||||||
|
|
||||||
{{#maxItems}}
|
{{#maxItems}}
|
||||||
// validate min items {{maxItems}}
|
// validate min items {{maxItems}}
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
@ -143,15 +145,8 @@ case class {{classname}}Data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{{/maxItems}}
|
{{/maxItems}}
|
||||||
|
{{#minProperties}} TODO - minProperties {{/minProperties}}
|
||||||
{{#minProperties}}
|
{{#maxProperties}} TODO - maxProperties {{/maxProperties}}
|
||||||
TODO - minProperties
|
|
||||||
{{/minProperties}}
|
|
||||||
|
|
||||||
{{#maxProperties}}
|
|
||||||
TODO - maxProperties
|
|
||||||
{{/maxProperties}}
|
|
||||||
|
|
||||||
{{#items}}{{#isModel}}
|
{{#items}}{{#isModel}}
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
if ({{name}} != null) {
|
if ({{name}} != null) {
|
||||||
@ -192,19 +187,35 @@ case class {{classname}}Data(
|
|||||||
{{#vendorExtensions.x-wrap-in-optional}}){{/vendorExtensions.x-wrap-in-optional}}
|
{{#vendorExtensions.x-wrap-in-optional}}){{/vendorExtensions.x-wrap-in-optional}}
|
||||||
{{#vendorExtensions.x-map-asModel}}.map(_.asModel){{/vendorExtensions.x-map-asModel}}{{^-last}},{{/-last}}
|
{{#vendorExtensions.x-map-asModel}}.map(_.asModel){{/vendorExtensions.x-map-asModel}}{{^-last}},{{/-last}}
|
||||||
{{/vars}}
|
{{/vars}}
|
||||||
|
{{#isAdditionalPropertiesTrue}}, additionalProperties{{/isAdditionalPropertiesTrue}}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object {{classname}}Data {
|
object {{classname}}Data {
|
||||||
|
|
||||||
given readWriter : RW[{{classname}}Data] = macroRW
|
def fromJson(jason : ujson.Value) : {{classname}}Data = try {
|
||||||
|
val data = read[{{classname}}Data](jason)
|
||||||
|
{{^isAdditionalPropertiesTrue}}
|
||||||
|
data
|
||||||
|
{{/isAdditionalPropertiesTrue}}
|
||||||
|
{{#isAdditionalPropertiesTrue}}
|
||||||
|
val obj = jason.obj
|
||||||
|
{{classname}}.Fields.values.foreach(v => obj.value.subtractOne(v.fieldName))
|
||||||
|
data.copy(additionalProperties = obj)
|
||||||
|
{{/isAdditionalPropertiesTrue}}
|
||||||
|
} catch {
|
||||||
|
case NonFatal(e) => sys.error(s"Error creating {{classname}}Data from json '$jason': $e")
|
||||||
|
}
|
||||||
|
|
||||||
def fromJsonString(jason : String) : {{classname}}Data = try {
|
def fromJsonString(jason : String) : {{classname}}Data = {
|
||||||
read[{{classname}}Data](jason)
|
val parsed = try {
|
||||||
|
read[ujson.Value](jason)
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
||||||
}
|
}
|
||||||
|
fromJson(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
def manyFromJsonString(jason : String) : Seq[{{classname}}Data] = try {
|
def manyFromJsonString(jason : String) : Seq[{{classname}}Data] = try {
|
||||||
read[List[{{classname}}Data]](jason)
|
read[List[{{classname}}Data]](jason)
|
||||||
|
@ -51,3 +51,20 @@ given ReadWriter[OffsetDateTime] = readwriter[String].bimap[OffsetDateTime](
|
|||||||
OffsetDateTime.parse(str, DateTimeFormatter.ISO_INSTANT)
|
OffsetDateTime.parse(str, DateTimeFormatter.ISO_INSTANT)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
extension (json: ujson.Value) {
|
||||||
|
def mergeWith(other: ujson.Value): ujson.Value = (json, other) match {
|
||||||
|
case (ujson.Obj(aMap), ujson.Obj(bMap)) =>
|
||||||
|
val mergedMap: scala.collection.mutable.Map[String, ujson.Value] = aMap ++ bMap.map {
|
||||||
|
case (k, v) => k -> (aMap.get(k) match {
|
||||||
|
case Some(aValue) => aValue.mergeWith(v)
|
||||||
|
case None => v
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ujson.Obj.from(mergedMap)
|
||||||
|
case (ujson.Arr(aArray), ujson.Arr(bArray)) => ujson.Arr(aArray ++ bArray)
|
||||||
|
case (aValue, ujson.Null) => aValue
|
||||||
|
case (_, bValue) => bValue
|
||||||
|
}
|
||||||
|
}
|
@ -63,7 +63,7 @@ class OpenApiRoutes(localPort: Int, swaggerUrl: Option[String]) extends cask.Rou
|
|||||||
val urlConn = new URL(url).openConnection()
|
val urlConn = new URL(url).openConnection()
|
||||||
urlConn.setRequestProperty("User-Agent", "Mozilla/5.0")
|
urlConn.setRequestProperty("User-Agent", "Mozilla/5.0")
|
||||||
|
|
||||||
Using(urlConn.getInputStream) { inputStream =>
|
val extracted = Using(urlConn.getInputStream) { inputStream =>
|
||||||
val zipIn = new ZipInputStream(new BufferedInputStream(inputStream))
|
val zipIn = new ZipInputStream(new BufferedInputStream(inputStream))
|
||||||
LazyList.continually(zipIn.getNextEntry).takeWhile(_ != null).foreach { entry =>
|
LazyList.continually(zipIn.getNextEntry).takeWhile(_ != null).foreach { entry =>
|
||||||
|
|
||||||
@ -77,6 +77,12 @@ class OpenApiRoutes(localPort: Int, swaggerUrl: Option[String]) extends cask.Rou
|
|||||||
zipIn.closeEntry()
|
zipIn.closeEntry()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extracted.isFailure) {
|
||||||
|
println(s"Error extracting swagger: ${extracted}")
|
||||||
|
} else {
|
||||||
|
println(s"Extracting swagger: ${extracted}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def extractFile(name: String, zipIn: ZipInputStream, filePath: String): Unit = {
|
def extractFile(name: String, zipIn: ZipInputStream, filePath: String): Unit = {
|
||||||
|
@ -38,7 +38,8 @@
|
|||||||
{{/vendorExtensions.x-deserialize-asModelMap}}
|
{{/vendorExtensions.x-deserialize-asModelMap}}
|
||||||
{{/isMap}}
|
{{/isMap}}
|
||||||
{{^isMap}}
|
{{^isMap}}
|
||||||
{{paramName}}Data <- Parsed.eval({{vendorExtensions.x-container-type}}Data.fromJsonString(request.bodyAsString)).mapError(e => s"Error parsing json as {{vendorExtensions.x-container-type}} from >${request.bodyAsString}< : ${e}") /* not array or map */
|
{{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}} <- Parsed.fromTry({{paramName}}Data.validated(failFast))
|
||||||
{{/isMap}}
|
{{/isMap}}
|
||||||
{{/isArray}}
|
{{/isArray}}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//> using scala "3.3.1"
|
//> using scala "3.3.1"
|
||||||
//> using lib "com.lihaoyi::cask:0.9.2"
|
//> using dep "com.lihaoyi::cask:0.9.2"
|
||||||
//> using lib "com.lihaoyi::scalatags:0.8.2"
|
//> using dep "com.lihaoyi::scalatags:0.8.2"
|
||||||
/**
|
/**
|
||||||
* OpenAPI Petstore
|
* OpenAPI Petstore
|
||||||
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
|
||||||
@ -21,11 +21,21 @@ package cask.groupId.server
|
|||||||
import _root_.sample.cask.model.*
|
import _root_.sample.cask.model.*
|
||||||
import _root_.sample.cask.api.*
|
import _root_.sample.cask.api.*
|
||||||
|
|
||||||
|
/** an example of how you can add your own additional routes to your app */
|
||||||
|
object MoreRoutes extends cask.Routes {
|
||||||
|
@cask.get("/echo")
|
||||||
|
def more(request: cask.Request) = s"request was ${request.bodyAsString}"
|
||||||
|
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an example of how you might extends BaseApp for a runnable application.
|
* This is an example of how you might extends BaseApp for a runnable application.
|
||||||
*
|
*
|
||||||
* See the README.md for how to create your own app
|
* See the README.md for how to create your own app
|
||||||
*/
|
*/
|
||||||
object ExampleApp extends BaseApp() {
|
object ExampleApp extends BaseApp() {
|
||||||
|
// override to include our additional route
|
||||||
|
override def allRoutes = super.allRoutes ++ Option(MoreRoutes)
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ class OpenApiRoutes(localPort: Int, swaggerUrl: Option[String]) extends cask.Rou
|
|||||||
val urlConn = new URL(url).openConnection()
|
val urlConn = new URL(url).openConnection()
|
||||||
urlConn.setRequestProperty("User-Agent", "Mozilla/5.0")
|
urlConn.setRequestProperty("User-Agent", "Mozilla/5.0")
|
||||||
|
|
||||||
Using(urlConn.getInputStream) { inputStream =>
|
val extracted = Using(urlConn.getInputStream) { inputStream =>
|
||||||
val zipIn = new ZipInputStream(new BufferedInputStream(inputStream))
|
val zipIn = new ZipInputStream(new BufferedInputStream(inputStream))
|
||||||
LazyList.continually(zipIn.getNextEntry).takeWhile(_ != null).foreach { entry =>
|
LazyList.continually(zipIn.getNextEntry).takeWhile(_ != null).foreach { entry =>
|
||||||
|
|
||||||
@ -89,6 +89,12 @@ class OpenApiRoutes(localPort: Int, swaggerUrl: Option[String]) extends cask.Rou
|
|||||||
zipIn.closeEntry()
|
zipIn.closeEntry()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extracted.isFailure) {
|
||||||
|
println(s"Error extracting swagger: ${extracted}")
|
||||||
|
} else {
|
||||||
|
println(s"Extracting swagger: ${extracted}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def extractFile(name: String, zipIn: ZipInputStream, filePath: String): Unit = {
|
def extractFile(name: String, zipIn: ZipInputStream, filePath: String): Unit = {
|
||||||
|
@ -60,12 +60,13 @@ class PetRoutes(service : PetService) extends cask.Routes {
|
|||||||
def failFast = request.queryParams.keySet.contains("failFast")
|
def failFast = request.queryParams.keySet.contains("failFast")
|
||||||
|
|
||||||
val result = for {
|
val result = for {
|
||||||
petData <- Parsed.eval(PetData.fromJsonString(request.bodyAsString)).mapError(e => s"Error parsing json as Pet from >${request.bodyAsString}< : ${e}") /* not array or map */
|
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(failFast))
|
||||||
result <- Parsed.eval(service.addPet(pet))
|
result <- Parsed.eval(service.addPet(pet))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : Pet) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : Pet) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
@ -86,7 +87,7 @@ class PetRoutes(service : PetService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.deletePet(petId, apiKey))
|
result <- Parsed.eval(service.deletePet(petId, apiKey))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
}
|
}
|
||||||
@ -104,7 +105,7 @@ class PetRoutes(service : PetService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.findPetsByStatus(status))
|
result <- Parsed.eval(service.findPetsByStatus(status))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : List[Pet]) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : List[Pet]) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
@ -123,7 +124,7 @@ class PetRoutes(service : PetService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.findPetsByTags(tags))
|
result <- Parsed.eval(service.findPetsByTags(tags))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : List[Pet]) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : List[Pet]) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
@ -143,7 +144,7 @@ class PetRoutes(service : PetService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.getPetById(petId))
|
result <- Parsed.eval(service.getPetById(petId))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : Pet) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : Pet) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
@ -159,12 +160,13 @@ class PetRoutes(service : PetService) extends cask.Routes {
|
|||||||
def failFast = request.queryParams.keySet.contains("failFast")
|
def failFast = request.queryParams.keySet.contains("failFast")
|
||||||
|
|
||||||
val result = for {
|
val result = for {
|
||||||
petData <- Parsed.eval(PetData.fromJsonString(request.bodyAsString)).mapError(e => s"Error parsing json as Pet from >${request.bodyAsString}< : ${e}") /* not array or map */
|
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(failFast))
|
||||||
result <- Parsed.eval(service.updatePet(pet))
|
result <- Parsed.eval(service.updatePet(pet))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : Pet) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : Pet) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
@ -186,7 +188,7 @@ class PetRoutes(service : PetService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.updatePetWithForm(petId, name, status))
|
result <- Parsed.eval(service.updatePetWithForm(petId, name, status))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
}
|
}
|
||||||
@ -207,7 +209,7 @@ class PetRoutes(service : PetService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.uploadFile(petId, additionalMetadata, file))
|
result <- Parsed.eval(service.uploadFile(petId, additionalMetadata, file))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : ApiResponse) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : ApiResponse) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
|
@ -41,7 +41,7 @@ class StoreRoutes(service : StoreService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.deleteOrder(orderId))
|
result <- Parsed.eval(service.deleteOrder(orderId))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ class StoreRoutes(service : StoreService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.getInventory())
|
result <- Parsed.eval(service.getInventory())
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
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"))
|
case Right(value : Map[String, Int]) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
@ -78,7 +78,7 @@ class StoreRoutes(service : StoreService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.getOrderById(orderId))
|
result <- Parsed.eval(service.getOrderById(orderId))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : Order) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : Order) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
@ -93,12 +93,13 @@ class StoreRoutes(service : StoreService) extends cask.Routes {
|
|||||||
def failFast = request.queryParams.keySet.contains("failFast")
|
def failFast = request.queryParams.keySet.contains("failFast")
|
||||||
|
|
||||||
val result = for {
|
val result = for {
|
||||||
orderData <- Parsed.eval(OrderData.fromJsonString(request.bodyAsString)).mapError(e => s"Error parsing json as Order from >${request.bodyAsString}< : ${e}") /* not array or map */
|
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(failFast))
|
||||||
result <- Parsed.eval(service.placeOrder(order))
|
result <- Parsed.eval(service.placeOrder(order))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : Order) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : Order) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
|
@ -49,12 +49,13 @@ class UserRoutes(service : UserService) extends cask.Routes {
|
|||||||
def failFast = request.queryParams.keySet.contains("failFast")
|
def failFast = request.queryParams.keySet.contains("failFast")
|
||||||
|
|
||||||
val result = for {
|
val result = for {
|
||||||
userData <- Parsed.eval(UserData.fromJsonString(request.bodyAsString)).mapError(e => s"Error parsing json as User from >${request.bodyAsString}< : ${e}") /* not array or map */
|
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(failFast))
|
||||||
result <- Parsed.eval(service.createUser(user))
|
result <- Parsed.eval(service.createUser(user))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
}
|
}
|
||||||
@ -73,7 +74,7 @@ class UserRoutes(service : UserService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.createUsersWithArrayInput(user))
|
result <- Parsed.eval(service.createUsersWithArrayInput(user))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
}
|
}
|
||||||
@ -92,7 +93,7 @@ class UserRoutes(service : UserService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.createUsersWithListInput(user))
|
result <- Parsed.eval(service.createUsersWithListInput(user))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
}
|
}
|
||||||
@ -111,7 +112,7 @@ class UserRoutes(service : UserService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.deleteUser(username))
|
result <- Parsed.eval(service.deleteUser(username))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
}
|
}
|
||||||
@ -129,7 +130,7 @@ class UserRoutes(service : UserService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.getUserByName(username))
|
result <- Parsed.eval(service.getUserByName(username))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : User) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : User) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
@ -147,7 +148,7 @@ class UserRoutes(service : UserService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.loginUser(username, password))
|
result <- Parsed.eval(service.loginUser(username, password))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(value : String) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
case Right(value : String) => cask.Response(data = write(value), 200, headers = Seq("Content-Type" -> "application/json"))
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
@ -166,7 +167,7 @@ class UserRoutes(service : UserService) extends cask.Routes {
|
|||||||
result <- Parsed.eval(service.logoutUser())
|
result <- Parsed.eval(service.logoutUser())
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
}
|
}
|
||||||
@ -182,12 +183,13 @@ class UserRoutes(service : UserService) extends cask.Routes {
|
|||||||
|
|
||||||
val result = for {
|
val result = for {
|
||||||
username <- Parsed(username)
|
username <- Parsed(username)
|
||||||
userData <- Parsed.eval(UserData.fromJsonString(request.bodyAsString)).mapError(e => s"Error parsing json as User from >${request.bodyAsString}< : ${e}") /* not array or map */
|
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(failFast))
|
||||||
result <- Parsed.eval(service.updateUser(username, user))
|
result <- Parsed.eval(service.updateUser(username, user))
|
||||||
} yield result
|
} yield result
|
||||||
|
|
||||||
result match {
|
(result : @unchecked) match {
|
||||||
case Left(error) => cask.Response(error, 500)
|
case Left(error) => cask.Response(error, 500)
|
||||||
case Right(other) => cask.Response(s"$other", 200)
|
case Right(other) => cask.Response(s"$other", 200)
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import java.time.LocalDate
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
import scala.util.*
|
import scala.util.*
|
||||||
|
import upickle.default.*
|
||||||
|
|
||||||
// needed for BigDecimal params
|
// needed for BigDecimal params
|
||||||
given cask.endpoints.QueryParamReader.SimpleParam[BigDecimal](BigDecimal.apply)
|
given cask.endpoints.QueryParamReader.SimpleParam[BigDecimal](BigDecimal.apply)
|
||||||
@ -155,6 +156,15 @@ extension (request: cask.Request) {
|
|||||||
|
|
||||||
def bodyAsString = new String(request.readAllBytes(), "UTF-8")
|
def bodyAsString = new String(request.readAllBytes(), "UTF-8")
|
||||||
|
|
||||||
|
def bodyAsJson : Try[ujson.Value] = {
|
||||||
|
val jason = bodyAsString
|
||||||
|
try {
|
||||||
|
Success(read[ujson.Value](jason))
|
||||||
|
} catch {
|
||||||
|
case scala.util.control.NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def pathValue(index: Int, paramName: String, required : Boolean): Parsed[String] = {
|
def pathValue(index: Int, paramName: String, required : Boolean): Parsed[String] = {
|
||||||
request
|
request
|
||||||
.remainingPathSegments
|
.remainingPathSegments
|
||||||
|
@ -21,30 +21,29 @@ import upickle.default.*
|
|||||||
|
|
||||||
case class ApiResponse(
|
case class ApiResponse(
|
||||||
code: Option[Int] = None ,
|
code: Option[Int] = None ,
|
||||||
|
|
||||||
`type`: Option[String] = None ,
|
`type`: Option[String] = None ,
|
||||||
|
|
||||||
message: Option[String] = None
|
message: Option[String] = None
|
||||||
|
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def asJson: String = asData.asJson
|
def asJsonString: String = asData.asJsonString
|
||||||
|
def asJson: ujson.Value = asData.asJson
|
||||||
|
|
||||||
def asData : ApiResponseData = {
|
def asData : ApiResponseData = {
|
||||||
ApiResponseData(
|
ApiResponseData(
|
||||||
code = code.getOrElse(0),
|
code = code.getOrElse(0),
|
||||||
`type` = `type`.getOrElse(""),
|
`type` = `type`.getOrElse(""),
|
||||||
message = message.getOrElse("")
|
message = message.getOrElse("")
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object ApiResponse {
|
object ApiResponse {
|
||||||
|
given RW[ApiResponse] = summon[RW[ujson.Value]].bimap[ApiResponse](_.asJson, json => read[ApiResponseData](json).asModel)
|
||||||
|
|
||||||
given RW[ApiResponse] = ApiResponseData.readWriter.bimap[ApiResponse](_.asData, _.asModel)
|
enum Fields(val fieldName : String) extends Field(fieldName) {
|
||||||
|
|
||||||
enum Fields(fieldName : String) extends Field(fieldName) {
|
|
||||||
case code extends Fields("code")
|
case code extends Fields("code")
|
||||||
case `type` extends Fields("`type`")
|
case `type` extends Fields("`type`")
|
||||||
case message extends Fields("message")
|
case message extends Fields("message")
|
||||||
|
@ -25,66 +25,34 @@ import upickle.default.*
|
|||||||
*/
|
*/
|
||||||
case class ApiResponseData(
|
case class ApiResponseData(
|
||||||
code: Int = 0 ,
|
code: Int = 0 ,
|
||||||
|
|
||||||
`type`: String = "" ,
|
`type`: String = "" ,
|
||||||
|
|
||||||
message: String = ""
|
message: String = ""
|
||||||
|
|
||||||
) {
|
|
||||||
|
|
||||||
def asJson: String = write(this)
|
) derives RW {
|
||||||
|
|
||||||
|
def asJsonString: String = asJson.toString()
|
||||||
|
|
||||||
|
def asJson : ujson.Value = {
|
||||||
|
val jason = writeJs(this)
|
||||||
|
jason
|
||||||
|
}
|
||||||
|
|
||||||
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
||||||
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
||||||
// ==================
|
// ================== code validation ==================
|
||||||
// code
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// `type`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// message
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== `type` validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== message validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -115,19 +83,28 @@ case class ApiResponseData(
|
|||||||
message
|
message
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object ApiResponseData {
|
object ApiResponseData {
|
||||||
|
|
||||||
given readWriter : RW[ApiResponseData] = macroRW
|
def fromJson(jason : ujson.Value) : ApiResponseData = try {
|
||||||
|
val data = read[ApiResponseData](jason)
|
||||||
|
data
|
||||||
|
} catch {
|
||||||
|
case NonFatal(e) => sys.error(s"Error creating ApiResponseData from json '$jason': $e")
|
||||||
|
}
|
||||||
|
|
||||||
def fromJsonString(jason : String) : ApiResponseData = try {
|
def fromJsonString(jason : String) : ApiResponseData = {
|
||||||
read[ApiResponseData](jason)
|
val parsed = try {
|
||||||
|
read[ujson.Value](jason)
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
||||||
}
|
}
|
||||||
|
fromJson(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
def manyFromJsonString(jason : String) : Seq[ApiResponseData] = try {
|
def manyFromJsonString(jason : String) : Seq[ApiResponseData] = try {
|
||||||
read[List[ApiResponseData]](jason)
|
read[List[ApiResponseData]](jason)
|
||||||
|
@ -21,27 +21,27 @@ import upickle.default.*
|
|||||||
|
|
||||||
case class Category(
|
case class Category(
|
||||||
id: Option[Long] = None ,
|
id: Option[Long] = None ,
|
||||||
|
|
||||||
name: Option[String] = None
|
name: Option[String] = None
|
||||||
|
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def asJson: String = asData.asJson
|
def asJsonString: String = asData.asJsonString
|
||||||
|
def asJson: ujson.Value = asData.asJson
|
||||||
|
|
||||||
def asData : CategoryData = {
|
def asData : CategoryData = {
|
||||||
CategoryData(
|
CategoryData(
|
||||||
id = id.getOrElse(0),
|
id = id.getOrElse(0),
|
||||||
name = name.getOrElse("")
|
name = name.getOrElse("")
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Category {
|
object Category {
|
||||||
|
given RW[Category] = summon[RW[ujson.Value]].bimap[Category](_.asJson, json => read[CategoryData](json).asModel)
|
||||||
|
|
||||||
given RW[Category] = CategoryData.readWriter.bimap[Category](_.asData, _.asModel)
|
enum Fields(val fieldName : String) extends Field(fieldName) {
|
||||||
|
|
||||||
enum Fields(fieldName : String) extends Field(fieldName) {
|
|
||||||
case id extends Fields("id")
|
case id extends Fields("id")
|
||||||
case name extends Fields("name")
|
case name extends Fields("name")
|
||||||
}
|
}
|
||||||
|
@ -25,35 +25,27 @@ import upickle.default.*
|
|||||||
*/
|
*/
|
||||||
case class CategoryData(
|
case class CategoryData(
|
||||||
id: Long = 0 ,
|
id: Long = 0 ,
|
||||||
|
|
||||||
name: String = ""
|
name: String = ""
|
||||||
|
|
||||||
) {
|
|
||||||
|
|
||||||
def asJson: String = write(this)
|
) derives RW {
|
||||||
|
|
||||||
|
def asJsonString: String = asJson.toString()
|
||||||
|
|
||||||
|
def asJson : ujson.Value = {
|
||||||
|
val jason = writeJs(this)
|
||||||
|
jason
|
||||||
|
}
|
||||||
|
|
||||||
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
||||||
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
||||||
// ==================
|
// ================== id validation ==================
|
||||||
// id
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== name validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// name
|
|
||||||
// validate against pattern '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$'
|
// validate against pattern '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$'
|
||||||
if (errors.isEmpty || !failFast) {
|
if (errors.isEmpty || !failFast) {
|
||||||
val regex = """^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$"""
|
val regex = """^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$"""
|
||||||
@ -65,17 +57,6 @@ case class CategoryData(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
errors.toSeq
|
errors.toSeq
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,19 +78,28 @@ case class CategoryData(
|
|||||||
name
|
name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object CategoryData {
|
object CategoryData {
|
||||||
|
|
||||||
given readWriter : RW[CategoryData] = macroRW
|
def fromJson(jason : ujson.Value) : CategoryData = try {
|
||||||
|
val data = read[CategoryData](jason)
|
||||||
|
data
|
||||||
|
} catch {
|
||||||
|
case NonFatal(e) => sys.error(s"Error creating CategoryData from json '$jason': $e")
|
||||||
|
}
|
||||||
|
|
||||||
def fromJsonString(jason : String) : CategoryData = try {
|
def fromJsonString(jason : String) : CategoryData = {
|
||||||
read[CategoryData](jason)
|
val parsed = try {
|
||||||
|
read[ujson.Value](jason)
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
||||||
}
|
}
|
||||||
|
fromJson(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
def manyFromJsonString(jason : String) : Seq[CategoryData] = try {
|
def manyFromJsonString(jason : String) : Seq[CategoryData] = try {
|
||||||
read[List[CategoryData]](jason)
|
read[List[CategoryData]](jason)
|
||||||
|
@ -22,21 +22,18 @@ import upickle.default.*
|
|||||||
|
|
||||||
case class Order(
|
case class Order(
|
||||||
id: Option[Long] = None ,
|
id: Option[Long] = None ,
|
||||||
|
|
||||||
petId: Option[Long] = None ,
|
petId: Option[Long] = None ,
|
||||||
|
|
||||||
quantity: Option[Int] = None ,
|
quantity: Option[Int] = None ,
|
||||||
|
|
||||||
shipDate: Option[OffsetDateTime] = None ,
|
shipDate: Option[OffsetDateTime] = None ,
|
||||||
|
|
||||||
/* Order Status */
|
/* Order Status */
|
||||||
status: Option[Order.StatusEnum] = None ,
|
status: Option[Order.StatusEnum] = None ,
|
||||||
|
|
||||||
complete: Option[Boolean] = None
|
complete: Option[Boolean] = None
|
||||||
|
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def asJson: String = asData.asJson
|
def asJsonString: String = asData.asJsonString
|
||||||
|
def asJson: ujson.Value = asData.asJson
|
||||||
|
|
||||||
def asData : OrderData = {
|
def asData : OrderData = {
|
||||||
OrderData(
|
OrderData(
|
||||||
@ -46,16 +43,15 @@ case class Order(
|
|||||||
shipDate = shipDate.getOrElse(null),
|
shipDate = shipDate.getOrElse(null),
|
||||||
status = status.getOrElse(null),
|
status = status.getOrElse(null),
|
||||||
complete = complete.getOrElse(false)
|
complete = complete.getOrElse(false)
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Order {
|
object Order {
|
||||||
|
given RW[Order] = summon[RW[ujson.Value]].bimap[Order](_.asJson, json => read[OrderData](json).asModel)
|
||||||
|
|
||||||
given RW[Order] = OrderData.readWriter.bimap[Order](_.asData, _.asModel)
|
enum Fields(val fieldName : String) extends Field(fieldName) {
|
||||||
|
|
||||||
enum Fields(fieldName : String) extends Field(fieldName) {
|
|
||||||
case id extends Fields("id")
|
case id extends Fields("id")
|
||||||
case petId extends Fields("petId")
|
case petId extends Fields("petId")
|
||||||
case quantity extends Fields("quantity")
|
case quantity extends Fields("quantity")
|
||||||
|
@ -26,127 +26,56 @@ import upickle.default.*
|
|||||||
*/
|
*/
|
||||||
case class OrderData(
|
case class OrderData(
|
||||||
id: Long = 0 ,
|
id: Long = 0 ,
|
||||||
|
|
||||||
petId: Long = 0 ,
|
petId: Long = 0 ,
|
||||||
|
|
||||||
quantity: Int = 0 ,
|
quantity: Int = 0 ,
|
||||||
|
|
||||||
shipDate: OffsetDateTime = null ,
|
shipDate: OffsetDateTime = null ,
|
||||||
|
|
||||||
/* Order Status */
|
/* Order Status */
|
||||||
status: Order.StatusEnum = null ,
|
status: Order.StatusEnum = null ,
|
||||||
|
|
||||||
complete: Boolean = false
|
complete: Boolean = false
|
||||||
|
|
||||||
) {
|
|
||||||
|
|
||||||
def asJson: String = write(this)
|
) derives RW {
|
||||||
|
|
||||||
|
def asJsonString: String = asJson.toString()
|
||||||
|
|
||||||
|
def asJson : ujson.Value = {
|
||||||
|
val jason = writeJs(this)
|
||||||
|
jason
|
||||||
|
}
|
||||||
|
|
||||||
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
||||||
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
||||||
// ==================
|
// ================== id validation ==================
|
||||||
// id
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== petId validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== quantity validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== shipDate validation ==================
|
||||||
// ==================
|
|
||||||
// petId
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// quantity
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// shipDate
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// status
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// complete
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== status validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== complete validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -189,19 +118,28 @@ case class OrderData(
|
|||||||
complete
|
complete
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object OrderData {
|
object OrderData {
|
||||||
|
|
||||||
given readWriter : RW[OrderData] = macroRW
|
def fromJson(jason : ujson.Value) : OrderData = try {
|
||||||
|
val data = read[OrderData](jason)
|
||||||
|
data
|
||||||
|
} catch {
|
||||||
|
case NonFatal(e) => sys.error(s"Error creating OrderData from json '$jason': $e")
|
||||||
|
}
|
||||||
|
|
||||||
def fromJsonString(jason : String) : OrderData = try {
|
def fromJsonString(jason : String) : OrderData = {
|
||||||
read[OrderData](jason)
|
val parsed = try {
|
||||||
|
read[ujson.Value](jason)
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
||||||
}
|
}
|
||||||
|
fromJson(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
def manyFromJsonString(jason : String) : Seq[OrderData] = try {
|
def manyFromJsonString(jason : String) : Seq[OrderData] = try {
|
||||||
read[List[OrderData]](jason)
|
read[List[OrderData]](jason)
|
||||||
|
@ -23,21 +23,18 @@ import upickle.default.*
|
|||||||
|
|
||||||
case class Pet(
|
case class Pet(
|
||||||
id: Option[Long] = None ,
|
id: Option[Long] = None ,
|
||||||
|
|
||||||
category: Option[Category] = None ,
|
category: Option[Category] = None ,
|
||||||
|
|
||||||
name: String,
|
name: String,
|
||||||
|
|
||||||
photoUrls: Seq[String],
|
photoUrls: Seq[String],
|
||||||
|
|
||||||
tags: Seq[Tag] = Nil ,
|
tags: Seq[Tag] = Nil ,
|
||||||
|
|
||||||
/* pet status in the store */
|
/* pet status in the store */
|
||||||
status: Option[Pet.StatusEnum] = None
|
status: Option[Pet.StatusEnum] = None
|
||||||
|
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def asJson: String = asData.asJson
|
def asJsonString: String = asData.asJsonString
|
||||||
|
def asJson: ujson.Value = asData.asJson
|
||||||
|
|
||||||
def asData : PetData = {
|
def asData : PetData = {
|
||||||
PetData(
|
PetData(
|
||||||
@ -47,16 +44,15 @@ case class Pet(
|
|||||||
photoUrls = photoUrls,
|
photoUrls = photoUrls,
|
||||||
tags = tags.map(_.asData),
|
tags = tags.map(_.asData),
|
||||||
status = status.getOrElse(null)
|
status = status.getOrElse(null)
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Pet {
|
object Pet {
|
||||||
|
given RW[Pet] = summon[RW[ujson.Value]].bimap[Pet](_.asJson, json => read[PetData](json).asModel)
|
||||||
|
|
||||||
given RW[Pet] = PetData.readWriter.bimap[Pet](_.asData, _.asModel)
|
enum Fields(val fieldName : String) extends Field(fieldName) {
|
||||||
|
|
||||||
enum Fields(fieldName : String) extends Field(fieldName) {
|
|
||||||
case id extends Fields("id")
|
case id extends Fields("id")
|
||||||
case category extends Fields("category")
|
case category extends Fields("category")
|
||||||
case name extends Fields("name")
|
case name extends Fields("name")
|
||||||
|
@ -27,55 +27,32 @@ import upickle.default.*
|
|||||||
*/
|
*/
|
||||||
case class PetData(
|
case class PetData(
|
||||||
id: Long = 0 ,
|
id: Long = 0 ,
|
||||||
|
|
||||||
category: CategoryData = null ,
|
category: CategoryData = null ,
|
||||||
|
|
||||||
name: String,
|
name: String,
|
||||||
|
|
||||||
photoUrls: Seq[String],
|
photoUrls: Seq[String],
|
||||||
|
|
||||||
tags: Seq[TagData] = Nil ,
|
tags: Seq[TagData] = Nil ,
|
||||||
|
|
||||||
/* pet status in the store */
|
/* pet status in the store */
|
||||||
status: Pet.StatusEnum = null
|
status: Pet.StatusEnum = null
|
||||||
|
|
||||||
) {
|
|
||||||
|
|
||||||
def asJson: String = write(this)
|
) derives RW {
|
||||||
|
|
||||||
|
def asJsonString: String = asJson.toString()
|
||||||
|
|
||||||
|
def asJson : ujson.Value = {
|
||||||
|
val jason = writeJs(this)
|
||||||
|
jason
|
||||||
|
}
|
||||||
|
|
||||||
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
||||||
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
||||||
// ==================
|
// ================== id validation ==================
|
||||||
// id
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// category
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== category validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -85,55 +62,19 @@ case class PetData(
|
|||||||
if category != null then errors ++= category.validationErrors(path :+ Pet.Fields.category, failFast)
|
if category != null then errors ++= category.validationErrors(path :+ Pet.Fields.category, failFast)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================
|
// ================== name validation ==================
|
||||||
// name
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// photoUrls
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// tags
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== photoUrls validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== tags validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -151,19 +92,7 @@ case class PetData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
// ================== status validation ==================
|
||||||
// status
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -206,19 +135,28 @@ case class PetData(
|
|||||||
status
|
status
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object PetData {
|
object PetData {
|
||||||
|
|
||||||
given readWriter : RW[PetData] = macroRW
|
def fromJson(jason : ujson.Value) : PetData = try {
|
||||||
|
val data = read[PetData](jason)
|
||||||
|
data
|
||||||
|
} catch {
|
||||||
|
case NonFatal(e) => sys.error(s"Error creating PetData from json '$jason': $e")
|
||||||
|
}
|
||||||
|
|
||||||
def fromJsonString(jason : String) : PetData = try {
|
def fromJsonString(jason : String) : PetData = {
|
||||||
read[PetData](jason)
|
val parsed = try {
|
||||||
|
read[ujson.Value](jason)
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
||||||
}
|
}
|
||||||
|
fromJson(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
def manyFromJsonString(jason : String) : Seq[PetData] = try {
|
def manyFromJsonString(jason : String) : Seq[PetData] = try {
|
||||||
read[List[PetData]](jason)
|
read[List[PetData]](jason)
|
||||||
|
@ -21,27 +21,27 @@ import upickle.default.*
|
|||||||
|
|
||||||
case class Tag(
|
case class Tag(
|
||||||
id: Option[Long] = None ,
|
id: Option[Long] = None ,
|
||||||
|
|
||||||
name: Option[String] = None
|
name: Option[String] = None
|
||||||
|
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def asJson: String = asData.asJson
|
def asJsonString: String = asData.asJsonString
|
||||||
|
def asJson: ujson.Value = asData.asJson
|
||||||
|
|
||||||
def asData : TagData = {
|
def asData : TagData = {
|
||||||
TagData(
|
TagData(
|
||||||
id = id.getOrElse(0),
|
id = id.getOrElse(0),
|
||||||
name = name.getOrElse("")
|
name = name.getOrElse("")
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object Tag {
|
object Tag {
|
||||||
|
given RW[Tag] = summon[RW[ujson.Value]].bimap[Tag](_.asJson, json => read[TagData](json).asModel)
|
||||||
|
|
||||||
given RW[Tag] = TagData.readWriter.bimap[Tag](_.asData, _.asModel)
|
enum Fields(val fieldName : String) extends Field(fieldName) {
|
||||||
|
|
||||||
enum Fields(fieldName : String) extends Field(fieldName) {
|
|
||||||
case id extends Fields("id")
|
case id extends Fields("id")
|
||||||
case name extends Fields("name")
|
case name extends Fields("name")
|
||||||
}
|
}
|
||||||
|
@ -25,46 +25,27 @@ import upickle.default.*
|
|||||||
*/
|
*/
|
||||||
case class TagData(
|
case class TagData(
|
||||||
id: Long = 0 ,
|
id: Long = 0 ,
|
||||||
|
|
||||||
name: String = ""
|
name: String = ""
|
||||||
|
|
||||||
) {
|
|
||||||
|
|
||||||
def asJson: String = write(this)
|
) derives RW {
|
||||||
|
|
||||||
|
def asJsonString: String = asJson.toString()
|
||||||
|
|
||||||
|
def asJson : ujson.Value = {
|
||||||
|
val jason = writeJs(this)
|
||||||
|
jason
|
||||||
|
}
|
||||||
|
|
||||||
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
||||||
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
||||||
// ==================
|
// ================== id validation ==================
|
||||||
// id
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// name
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== name validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -91,19 +72,28 @@ case class TagData(
|
|||||||
name
|
name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object TagData {
|
object TagData {
|
||||||
|
|
||||||
given readWriter : RW[TagData] = macroRW
|
def fromJson(jason : ujson.Value) : TagData = try {
|
||||||
|
val data = read[TagData](jason)
|
||||||
|
data
|
||||||
|
} catch {
|
||||||
|
case NonFatal(e) => sys.error(s"Error creating TagData from json '$jason': $e")
|
||||||
|
}
|
||||||
|
|
||||||
def fromJsonString(jason : String) : TagData = try {
|
def fromJsonString(jason : String) : TagData = {
|
||||||
read[TagData](jason)
|
val parsed = try {
|
||||||
|
read[ujson.Value](jason)
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
||||||
}
|
}
|
||||||
|
fromJson(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
def manyFromJsonString(jason : String) : Seq[TagData] = try {
|
def manyFromJsonString(jason : String) : Seq[TagData] = try {
|
||||||
read[List[TagData]](jason)
|
read[List[TagData]](jason)
|
||||||
|
@ -21,25 +21,20 @@ import upickle.default.*
|
|||||||
|
|
||||||
case class User(
|
case class User(
|
||||||
id: Option[Long] = None ,
|
id: Option[Long] = None ,
|
||||||
|
|
||||||
username: Option[String] = None ,
|
username: Option[String] = None ,
|
||||||
|
|
||||||
firstName: Option[String] = None ,
|
firstName: Option[String] = None ,
|
||||||
|
|
||||||
lastName: Option[String] = None ,
|
lastName: Option[String] = None ,
|
||||||
|
|
||||||
email: Option[String] = None ,
|
email: Option[String] = None ,
|
||||||
|
|
||||||
password: Option[String] = None ,
|
password: Option[String] = None ,
|
||||||
|
|
||||||
phone: Option[String] = None ,
|
phone: Option[String] = None ,
|
||||||
|
|
||||||
/* User Status */
|
/* User Status */
|
||||||
userStatus: Option[Int] = None
|
userStatus: Option[Int] = None
|
||||||
|
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def asJson: String = asData.asJson
|
def asJsonString: String = asData.asJsonString
|
||||||
|
def asJson: ujson.Value = asData.asJson
|
||||||
|
|
||||||
def asData : UserData = {
|
def asData : UserData = {
|
||||||
UserData(
|
UserData(
|
||||||
@ -51,16 +46,15 @@ case class User(
|
|||||||
password = password.getOrElse(""),
|
password = password.getOrElse(""),
|
||||||
phone = phone.getOrElse(""),
|
phone = phone.getOrElse(""),
|
||||||
userStatus = userStatus.getOrElse(0)
|
userStatus = userStatus.getOrElse(0)
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object User {
|
object User {
|
||||||
|
given RW[User] = summon[RW[ujson.Value]].bimap[User](_.asJson, json => read[UserData](json).asModel)
|
||||||
|
|
||||||
given RW[User] = UserData.readWriter.bimap[User](_.asData, _.asModel)
|
enum Fields(val fieldName : String) extends Field(fieldName) {
|
||||||
|
|
||||||
enum Fields(fieldName : String) extends Field(fieldName) {
|
|
||||||
case id extends Fields("id")
|
case id extends Fields("id")
|
||||||
case username extends Fields("username")
|
case username extends Fields("username")
|
||||||
case firstName extends Fields("firstName")
|
case firstName extends Fields("firstName")
|
||||||
|
@ -25,167 +25,70 @@ import upickle.default.*
|
|||||||
*/
|
*/
|
||||||
case class UserData(
|
case class UserData(
|
||||||
id: Long = 0 ,
|
id: Long = 0 ,
|
||||||
|
|
||||||
username: String = "" ,
|
username: String = "" ,
|
||||||
|
|
||||||
firstName: String = "" ,
|
firstName: String = "" ,
|
||||||
|
|
||||||
lastName: String = "" ,
|
lastName: String = "" ,
|
||||||
|
|
||||||
email: String = "" ,
|
email: String = "" ,
|
||||||
|
|
||||||
password: String = "" ,
|
password: String = "" ,
|
||||||
|
|
||||||
phone: String = "" ,
|
phone: String = "" ,
|
||||||
|
|
||||||
/* User Status */
|
/* User Status */
|
||||||
userStatus: Int = 0
|
userStatus: Int = 0
|
||||||
|
|
||||||
) {
|
|
||||||
|
|
||||||
def asJson: String = write(this)
|
) derives RW {
|
||||||
|
|
||||||
|
def asJsonString: String = asJson.toString()
|
||||||
|
|
||||||
|
def asJson : ujson.Value = {
|
||||||
|
val jason = writeJs(this)
|
||||||
|
jason
|
||||||
|
}
|
||||||
|
|
||||||
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
|
||||||
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
|
||||||
// ==================
|
// ================== id validation ==================
|
||||||
// id
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== username validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== firstName validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== lastName validation ==================
|
||||||
|
|
||||||
// ==================
|
|
||||||
// username
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== email validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== password validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== phone validation ==================
|
||||||
|
|
||||||
// ==================
|
|
||||||
// firstName
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// lastName
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// email
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// password
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// phone
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// userStatus
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ================== userStatus validation ==================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -236,19 +139,28 @@ case class UserData(
|
|||||||
userStatus
|
userStatus
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object UserData {
|
object UserData {
|
||||||
|
|
||||||
given readWriter : RW[UserData] = macroRW
|
def fromJson(jason : ujson.Value) : UserData = try {
|
||||||
|
val data = read[UserData](jason)
|
||||||
|
data
|
||||||
|
} catch {
|
||||||
|
case NonFatal(e) => sys.error(s"Error creating UserData from json '$jason': $e")
|
||||||
|
}
|
||||||
|
|
||||||
def fromJsonString(jason : String) : UserData = try {
|
def fromJsonString(jason : String) : UserData = {
|
||||||
read[UserData](jason)
|
val parsed = try {
|
||||||
|
read[ujson.Value](jason)
|
||||||
} catch {
|
} catch {
|
||||||
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
|
||||||
}
|
}
|
||||||
|
fromJson(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
def manyFromJsonString(jason : String) : Seq[UserData] = try {
|
def manyFromJsonString(jason : String) : Seq[UserData] = try {
|
||||||
read[List[UserData]](jason)
|
read[List[UserData]](jason)
|
||||||
|
@ -63,3 +63,20 @@ given ReadWriter[OffsetDateTime] = readwriter[String].bimap[OffsetDateTime](
|
|||||||
OffsetDateTime.parse(str, DateTimeFormatter.ISO_INSTANT)
|
OffsetDateTime.parse(str, DateTimeFormatter.ISO_INSTANT)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
extension (json: ujson.Value) {
|
||||||
|
def mergeWith(other: ujson.Value): ujson.Value = (json, other) match {
|
||||||
|
case (ujson.Obj(aMap), ujson.Obj(bMap)) =>
|
||||||
|
val mergedMap: scala.collection.mutable.Map[String, ujson.Value] = aMap ++ bMap.map {
|
||||||
|
case (k, v) => k -> (aMap.get(k) match {
|
||||||
|
case Some(aValue) => aValue.mergeWith(v)
|
||||||
|
case None => v
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ujson.Obj.from(mergedMap)
|
||||||
|
case (ujson.Arr(aArray), ujson.Arr(bArray)) => ujson.Arr(aArray ++ bArray)
|
||||||
|
case (aValue, ujson.Null) => aValue
|
||||||
|
case (_, bValue) => bValue
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user