Scala cask api effects (#19936)

* Scala-cask improvements:

 * fixe for grouped methods which have routes containing dashes.

Previously our OperationGroup work-around would potentially
Create methods like ‘foo-bar’, which isn’t a valid function name

 * Fix to not import some.package.Array[Byte] when binary format is specified

 * Fix for grouped operations which contain duplicate query parameters

 * Fix for binary response fields. This can come up with the following example

        "responses" : {
          "200" : {
            "content" : {
              "application/json" : {
                "schema" : {
                  "format" : "binary",
                  "type" : "string"
                }
              }
            },
            "description" : "data"
          },

 * Fix for enum model classes
Extracted complex logic for ‘asData’ and ‘asModel’ transformations for properties

 * Introduced a generic effect F[_] for services

This was done to support composable services
(Service A calls Service B) by using monadic
Effect types (ones which can flatMap)

 * Fixed unique union types for responses, asModel and asData fixes for non-model types

* scala-cask: regenerated samples

* Fix for reserved-word properties in the API

* Fix for null imports and reserved-word enum types

* Fixes for api methods with backticked params

* Fix for duplicate (by name) grouped params

* small syntax fix

* logging response type

* Regenerated samples

* String.format fix
This commit is contained in:
Aaron Pritzlaff
2024-11-06 08:14:31 +00:00
committed by GitHub
parent cded99c3fc
commit b51b18e3ca
41 changed files with 1725 additions and 821 deletions

View File

@@ -14,10 +14,10 @@
* https://openapi-generator.tech
*/
// this file was generated from app.mustache
package cask.groupId.server
import scala.util.Try
import _root_.sample.cask.model.*
import _root_.sample.cask.api.*
@@ -30,20 +30,23 @@ import _root_.sample.cask.api.*
* If you wanted fine-grained control over the routes and services, you could
* extend the cask.MainRoutes and mix in this trait by using this:
*
* \{\{\{
* ```
* override def allRoutes = appRoutes
* \}\}\}
* ```
*
* More typically, however, you would extend the 'BaseApp' class
*/
trait AppRoutes {
def appPetService : PetService = PetService()
def appPetService : PetService[Try] = PetService()
def routeForPet : PetRoutes = PetRoutes(appPetService)
def appStoreService : StoreService = StoreService()
def appStoreService : StoreService[Try] = StoreService()
def routeForStore : StoreRoutes = StoreRoutes(appStoreService)
def appUserService : UserService = UserService()
def appUserService : UserService[Try] = UserService()
def routeForUser : UserRoutes = UserRoutes(appUserService)
def appRoutes = Seq(
routeForPet ,
routeForStore ,

View File

@@ -18,6 +18,7 @@
// this file was generated from app.mustache
package cask.groupId.server
import scala.util.Try
import _root_.sample.cask.model.*
import _root_.sample.cask.api.*
@@ -26,11 +27,11 @@ import _root_.sample.cask.api.*
* passing in the custom business logic services
*/
class BaseApp(
override val appPetService : PetService = PetService(),
override val appPetService : PetService[Try] = PetService(),
override val appStoreService : StoreService = StoreService(),
override val appStoreService : StoreService[Try] = StoreService(),
override val appUserService : UserService = UserService(),
override val appUserService : UserService[Try] = UserService(),
override val port : Int = sys.env.get("PORT").map(_.toInt).getOrElse(8080)) extends cask.MainRoutes with AppRoutes {
/** routes for the UI

View File

@@ -22,12 +22,13 @@ import sample.cask.model.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
import scala.util.Try
import sample.cask.model.ApiResponse
import java.io.File
import sample.cask.model.Pet
class PetRoutes(service : PetService) extends cask.Routes {
class PetRoutes(service : PetService[Try]) extends cask.Routes {
// route group for routeWorkAroundForPOSTPet
@cask.post("/pet", true)
@@ -63,7 +64,8 @@ class PetRoutes(service : PetService) extends cask.Routes {
petJson <- Parsed.fromTry(request.bodyAsJson)
petData <- Parsed.eval(PetData.fromJson(petJson)) /* not array or map */
pet <- Parsed.fromTry(petData.validated(failFast))
result <- Parsed.eval(service.addPet(pet))
resultTry <- Parsed.eval(service.addPet(pet))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -84,7 +86,8 @@ class PetRoutes(service : PetService) extends cask.Routes {
val result = for {
petId <- Parsed(petId)
apiKey <- request.headerSingleValueOptional("apiKey")
result <- Parsed.eval(service.deletePet(petId, apiKey))
resultTry <- Parsed.eval(service.deletePet(petId, apiKey))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -102,7 +105,8 @@ class PetRoutes(service : PetService) extends cask.Routes {
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.findPetsByStatus(status))
resultTry <- Parsed.eval(service.findPetsByStatus(status))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -121,7 +125,8 @@ class PetRoutes(service : PetService) extends cask.Routes {
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.findPetsByTags(tags))
resultTry <- Parsed.eval(service.findPetsByTags(tags))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -141,7 +146,8 @@ class PetRoutes(service : PetService) extends cask.Routes {
val result = for {
petId <- Parsed(petId)
result <- Parsed.eval(service.getPetById(petId))
resultTry <- Parsed.eval(service.getPetById(petId))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -163,7 +169,8 @@ class PetRoutes(service : PetService) extends cask.Routes {
petJson <- Parsed.fromTry(request.bodyAsJson)
petData <- Parsed.eval(PetData.fromJson(petJson)) /* not array or map */
pet <- Parsed.fromTry(petData.validated(failFast))
result <- Parsed.eval(service.updatePet(pet))
resultTry <- Parsed.eval(service.updatePet(pet))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -185,7 +192,8 @@ class PetRoutes(service : PetService) extends cask.Routes {
petId <- Parsed(petId)
name <- request.formSingleValueOptional("name")
status <- request.formSingleValueOptional("status")
result <- Parsed.eval(service.updatePetWithForm(petId, name, status))
resultTry <- Parsed.eval(service.updatePetWithForm(petId, name, status))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -206,7 +214,8 @@ class PetRoutes(service : PetService) extends cask.Routes {
petId <- Parsed(petId)
additionalMetadata <- request.formSingleValueOptional("additionalMetadata")
file <- request.formValueAsFileOptional("file")
result <- Parsed.eval(service.uploadFile(petId, additionalMetadata, file))
resultTry <- Parsed.eval(service.uploadFile(petId, additionalMetadata, file))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {

View File

@@ -22,10 +22,11 @@ import sample.cask.model.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
import scala.util.Try
import sample.cask.model.Order
class StoreRoutes(service : StoreService) extends cask.Routes {
class StoreRoutes(service : StoreService[Try]) extends cask.Routes {
/** Delete purchase order by ID
@@ -38,7 +39,8 @@ class StoreRoutes(service : StoreService) extends cask.Routes {
val result = for {
orderId <- Parsed(orderId)
result <- Parsed.eval(service.deleteOrder(orderId))
resultTry <- Parsed.eval(service.deleteOrder(orderId))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -56,7 +58,8 @@ class StoreRoutes(service : StoreService) extends cask.Routes {
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.getInventory())
resultTry <- Parsed.eval(service.getInventory())
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -75,7 +78,8 @@ class StoreRoutes(service : StoreService) extends cask.Routes {
val result = for {
orderId <- Parsed(orderId)
result <- Parsed.eval(service.getOrderById(orderId))
resultTry <- Parsed.eval(service.getOrderById(orderId))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -96,7 +100,8 @@ class StoreRoutes(service : StoreService) extends cask.Routes {
orderJson <- Parsed.fromTry(request.bodyAsJson)
orderData <- Parsed.eval(OrderData.fromJson(orderJson)) /* not array or map */
order <- Parsed.fromTry(orderData.validated(failFast))
result <- Parsed.eval(service.placeOrder(order))
resultTry <- Parsed.eval(service.placeOrder(order))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {

View File

@@ -22,11 +22,12 @@ import sample.cask.model.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
import scala.util.Try
import java.time.OffsetDateTime
import sample.cask.model.User
class UserRoutes(service : UserService) extends cask.Routes {
class UserRoutes(service : UserService[Try]) extends cask.Routes {
// route group for routeWorkAroundForGETUser
@cask.get("/user", true)
@@ -52,7 +53,8 @@ class UserRoutes(service : UserService) extends cask.Routes {
userJson <- Parsed.fromTry(request.bodyAsJson)
userData <- Parsed.eval(UserData.fromJson(userJson)) /* not array or map */
user <- Parsed.fromTry(userData.validated(failFast))
result <- Parsed.eval(service.createUser(user))
resultTry <- Parsed.eval(service.createUser(user))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -71,7 +73,8 @@ class UserRoutes(service : UserService) extends cask.Routes {
val result = for {
user <- Parsed.fromTry(UserData.manyFromJsonStringValidated(request.bodyAsString)).mapError(e => s"Error parsing json as an array of User from >${request.bodyAsString}< : ${e}") /* array */
result <- Parsed.eval(service.createUsersWithArrayInput(user))
resultTry <- Parsed.eval(service.createUsersWithArrayInput(user))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -90,7 +93,8 @@ class UserRoutes(service : UserService) extends cask.Routes {
val result = for {
user <- Parsed.fromTry(UserData.manyFromJsonStringValidated(request.bodyAsString)).mapError(e => s"Error parsing json as an array of User from >${request.bodyAsString}< : ${e}") /* array */
result <- Parsed.eval(service.createUsersWithListInput(user))
resultTry <- Parsed.eval(service.createUsersWithListInput(user))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -109,7 +113,8 @@ class UserRoutes(service : UserService) extends cask.Routes {
val result = for {
username <- Parsed(username)
result <- Parsed.eval(service.deleteUser(username))
resultTry <- Parsed.eval(service.deleteUser(username))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -127,7 +132,8 @@ class UserRoutes(service : UserService) extends cask.Routes {
val result = for {
username <- Parsed(username)
result <- Parsed.eval(service.getUserByName(username))
resultTry <- Parsed.eval(service.getUserByName(username))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -145,7 +151,8 @@ class UserRoutes(service : UserService) extends cask.Routes {
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.loginUser(username, password))
resultTry <- Parsed.eval(service.loginUser(username, password))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -164,7 +171,8 @@ class UserRoutes(service : UserService) extends cask.Routes {
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.logoutUser())
resultTry <- Parsed.eval(service.logoutUser())
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {
@@ -186,7 +194,8 @@ class UserRoutes(service : UserService) extends cask.Routes {
userJson <- Parsed.fromTry(request.bodyAsJson)
userData <- Parsed.eval(UserData.fromJson(userJson)) /* not array or map */
user <- Parsed.fromTry(userData.validated(failFast))
result <- Parsed.eval(service.updateUser(username, user))
resultTry <- Parsed.eval(service.updateUser(username, user))
result <- Parsed.fromTry(resultTry)
} yield result
(result : @unchecked) match {

View File

@@ -25,6 +25,16 @@ import scala.reflect.ClassTag
import scala.util.*
import upickle.default.*
extension (f: java.io.File) {
def bytes: Array[Byte] = java.nio.file.Files.readAllBytes(f.toPath)
def toBase64: String = java.util.Base64.getEncoder.encodeToString(bytes)
}
given Writer[java.io.File] = new Writer[java.io.File] {
def write0[V](out: upickle.core.Visitor[?, V], v: java.io.File) = out.visitString(v.toBase64, -1)
}
// needed for BigDecimal params
given cask.endpoints.QueryParamReader.SimpleParam[BigDecimal](BigDecimal.apply)

View File

@@ -1,15 +1,14 @@
/**
* 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
/** 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
// this model was generated using modelTest.mustache
package sample.cask.model
@@ -20,16 +19,16 @@ import scala.util.*
class ApiResponseTest extends AnyWordSpec with Matchers {
"ApiResponse.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(ApiResponseData.fromJsonString("invalid jason"))
err.getMessage should startWith ("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err : ValidationErrors) = ApiResponseData.fromJsonString("""""").validated()
sys.error("TODO")
}
"ApiResponse.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(ApiResponseData.fromJsonString("invalid jason"))
err.getMessage should startWith("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err: ValidationErrors) = ApiResponseData.fromJsonString("""""").validated()
sys.error("TODO")
}
}
}

View File

@@ -1,15 +1,14 @@
/**
* 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
/** 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
// this model was generated using modelTest.mustache
package sample.cask.model
@@ -20,16 +19,16 @@ import scala.util.*
class CategoryTest extends AnyWordSpec with Matchers {
"Category.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(CategoryData.fromJsonString("invalid jason"))
err.getMessage should startWith ("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err : ValidationErrors) = CategoryData.fromJsonString("""""").validated()
sys.error("TODO")
}
"Category.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(CategoryData.fromJsonString("invalid jason"))
err.getMessage should startWith("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err: ValidationErrors) = CategoryData.fromJsonString("""""").validated()
sys.error("TODO")
}
}
}

View File

@@ -1,15 +1,14 @@
/**
* 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
/** 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
// this model was generated using modelTest.mustache
package sample.cask.model
@@ -21,16 +20,16 @@ import scala.util.*
class OrderTest extends AnyWordSpec with Matchers {
"Order.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(OrderData.fromJsonString("invalid jason"))
err.getMessage should startWith ("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err : ValidationErrors) = OrderData.fromJsonString("""""").validated()
sys.error("TODO")
}
"Order.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(OrderData.fromJsonString("invalid jason"))
err.getMessage should startWith("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err: ValidationErrors) = OrderData.fromJsonString("""""").validated()
sys.error("TODO")
}
}
}

View File

@@ -1,20 +1,17 @@
/**
* 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
/** 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
// this model was generated using modelTest.mustache
package sample.cask.model
import sample.cask.model.Category
import sample.cask.model.Tag
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
@@ -22,16 +19,16 @@ import scala.util.*
class PetTest extends AnyWordSpec with Matchers {
"Pet.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(PetData.fromJsonString("invalid jason"))
err.getMessage should startWith ("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err : ValidationErrors) = PetData.fromJsonString("""""").validated()
sys.error("TODO")
}
"Pet.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(PetData.fromJsonString("invalid jason"))
err.getMessage should startWith("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err: ValidationErrors) = PetData.fromJsonString("""""").validated()
sys.error("TODO")
}
}
}

View File

@@ -1,15 +1,14 @@
/**
* 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
/** 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
// this model was generated using modelTest.mustache
package sample.cask.model
@@ -20,16 +19,16 @@ import scala.util.*
class TagTest extends AnyWordSpec with Matchers {
"Tag.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(TagData.fromJsonString("invalid jason"))
err.getMessage should startWith ("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err : ValidationErrors) = TagData.fromJsonString("""""").validated()
sys.error("TODO")
}
"Tag.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(TagData.fromJsonString("invalid jason"))
err.getMessage should startWith("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err: ValidationErrors) = TagData.fromJsonString("""""").validated()
sys.error("TODO")
}
}
}

View File

@@ -1,15 +1,14 @@
/**
* 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
/** 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.
*
* OpenAPI spec version: 1.0.0
*
* Contact: team@openapitools.org
*
* NOTE: This class is auto generated by OpenAPI Generator.
*
* https://openapi-generator.tech
*/
// this model was generated using modelTest.mustache
package sample.cask.model
@@ -20,16 +19,16 @@ import scala.util.*
class UserTest extends AnyWordSpec with Matchers {
"User.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(UserData.fromJsonString("invalid jason"))
err.getMessage should startWith ("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err : ValidationErrors) = UserData.fromJsonString("""""").validated()
sys.error("TODO")
}
"User.fromJson" should {
"""not parse invalid json""" in {
val Failure(err) = Try(UserData.fromJsonString("invalid jason"))
err.getMessage should startWith("Error parsing json 'invalid jason'")
}
"""parse """ ignore {
val Failure(err: ValidationErrors) = UserData.fromJsonString("""""").validated()
sys.error("TODO")
}
}
}

View File

@@ -21,64 +21,260 @@ package sample.cask.api
import _root_.sample.cask.model.ApiResponse
import _root_.java.io.File
import _root_.sample.cask.model.Pet
import scala.util.Failure
import scala.util.Try
import _root_.sample.cask.model.*
/**
* The PetService companion object.
*
* Use the PetService() companion object to create an instance which returns a 'not implemented' error
* for each operation.
*
*/
object PetService {
def apply() : PetService = new PetService {
override def addPet(pet : Pet) : Pet = ???
override def deletePet(petId : Long, apiKey : Option[String]) : Unit = ???
override def findPetsByStatus(status : Seq[String]) : List[Pet] = ???
override def findPetsByTags(tags : Seq[String]) : List[Pet] = ???
override def getPetById(petId : Long) : Pet = ???
override def updatePet(pet : Pet) : Pet = ???
override def updatePetWithForm(petId : Long, name : Option[String], status : Option[String]) : Unit = ???
override def uploadFile(petId : Long, additionalMetadata : Option[String], file : Option[File]) : ApiResponse = ???
/**
* The 'Handler' is an implementation of PetService convenient for delegating or overriding individual functions
*/
case class Handler[F[_]](
addPetHandler : (pet : Pet) => F[Pet],
deletePetHandler : (petId : Long, apiKey : Option[String]) => F[Unit],
findPetsByStatusHandler : (status : Seq[String]) => F[List[Pet]],
findPetsByTagsHandler : (tags : Seq[String]) => F[List[Pet]],
getPetByIdHandler : (petId : Long) => F[Pet],
updatePetHandler : (pet : Pet) => F[Pet],
updatePetWithFormHandler : (petId : Long, name : Option[String], status : Option[String]) => F[Unit],
uploadFileHandler : (petId : Long, additionalMetadata : Option[String], file : Option[File]) => F[ApiResponse]
) extends PetService[F] {
override def addPet(pet : Pet) : F[Pet] = {
addPetHandler(pet)
}
override def deletePet(petId : Long, apiKey : Option[String]) : F[Unit] = {
deletePetHandler(petId, apiKey)
}
override def findPetsByStatus(status : Seq[String]) : F[List[Pet]] = {
findPetsByStatusHandler(status)
}
override def findPetsByTags(tags : Seq[String]) : F[List[Pet]] = {
findPetsByTagsHandler(tags)
}
override def getPetById(petId : Long) : F[Pet] = {
getPetByIdHandler(petId)
}
override def updatePet(pet : Pet) : F[Pet] = {
updatePetHandler(pet)
}
override def updatePetWithForm(petId : Long, name : Option[String], status : Option[String]) : F[Unit] = {
updatePetWithFormHandler(petId, name, status)
}
override def uploadFile(petId : Long, additionalMetadata : Option[String], file : Option[File]) : F[ApiResponse] = {
uploadFileHandler(petId, additionalMetadata, file)
}
}
def apply() : PetService[Try] = PetService.Handler[Try](
(_) => notImplemented("addPet"),
(_, _) => notImplemented("deletePet"),
(_) => notImplemented("findPetsByStatus"),
(_) => notImplemented("findPetsByTags"),
(_) => notImplemented("getPetById"),
(_) => notImplemented("updatePet"),
(_, _, _) => notImplemented("updatePetWithForm"),
(_, _, _) => notImplemented("uploadFile")
)
private def notImplemented(name : String) = Failure(new Exception(s"TODO: $name not implemented"))
}
/**
* The Pet business-logic
*
*
* The 'asHandler' will return an implementation which allows for easily overriding individual operations.
*
* equally there are "on&lt;Function&gt;" helper methods for easily overriding individual functions
*
* @tparam F the effect type (Future, Try, IO, ID, etc) of the operations
*/
trait PetService {
trait PetService[F[_]] {
/** Add a new pet to the store
*
* @return Pet
*/
def addPet(pet : Pet) : Pet
def addPet(pet : Pet) : F[Pet]
/**
* override addPet with the given handler
* @return a new implementation of PetService[F] with addPet overridden using the given handler
*/
final def onAddPet(handler : (pet : Pet) => F[Pet]) : PetService[F] = {
asHandler.copy(addPetHandler = handler)
}
/** Deletes a pet
*
* @return
*/
def deletePet(petId : Long, apiKey : Option[String]) : Unit
def deletePet(petId : Long, apiKey : Option[String]) : F[Unit]
/**
* override deletePet with the given handler
* @return a new implementation of PetService[F] with deletePet overridden using the given handler
*/
final def onDeletePet(handler : (petId : Long, apiKey : Option[String]) => F[Unit]) : PetService[F] = {
asHandler.copy(deletePetHandler = handler)
}
/** Finds Pets by status
*
* @return List[Pet]
*/
def findPetsByStatus(status : Seq[String]) : List[Pet]
def findPetsByStatus(status : Seq[String]) : F[List[Pet]]
/**
* override findPetsByStatus with the given handler
* @return a new implementation of PetService[F] with findPetsByStatus overridden using the given handler
*/
final def onFindPetsByStatus(handler : (status : Seq[String]) => F[List[Pet]]) : PetService[F] = {
asHandler.copy(findPetsByStatusHandler = handler)
}
/** Finds Pets by tags
*
* @return List[Pet]
*/
def findPetsByTags(tags : Seq[String]) : List[Pet]
def findPetsByTags(tags : Seq[String]) : F[List[Pet]]
/**
* override findPetsByTags with the given handler
* @return a new implementation of PetService[F] with findPetsByTags overridden using the given handler
*/
final def onFindPetsByTags(handler : (tags : Seq[String]) => F[List[Pet]]) : PetService[F] = {
asHandler.copy(findPetsByTagsHandler = handler)
}
/** Find pet by ID
*
* @return Pet
*/
def getPetById(petId : Long) : Pet
def getPetById(petId : Long) : F[Pet]
/**
* override getPetById with the given handler
* @return a new implementation of PetService[F] with getPetById overridden using the given handler
*/
final def onGetPetById(handler : (petId : Long) => F[Pet]) : PetService[F] = {
asHandler.copy(getPetByIdHandler = handler)
}
/** Update an existing pet
*
* @return Pet
*/
def updatePet(pet : Pet) : Pet
def updatePet(pet : Pet) : F[Pet]
/**
* override updatePet with the given handler
* @return a new implementation of PetService[F] with updatePet overridden using the given handler
*/
final def onUpdatePet(handler : (pet : Pet) => F[Pet]) : PetService[F] = {
asHandler.copy(updatePetHandler = handler)
}
/** Updates a pet in the store with form data
*
* @return
*/
def updatePetWithForm(petId : Long, name : Option[String], status : Option[String]) : Unit
def updatePetWithForm(petId : Long, name : Option[String], status : Option[String]) : F[Unit]
/**
* override updatePetWithForm with the given handler
* @return a new implementation of PetService[F] with updatePetWithForm overridden using the given handler
*/
final def onUpdatePetWithForm(handler : (petId : Long, name : Option[String], status : Option[String]) => F[Unit]) : PetService[F] = {
asHandler.copy(updatePetWithFormHandler = handler)
}
/** uploads an image
*
* @return ApiResponse
*/
def uploadFile(petId : Long, additionalMetadata : Option[String], file : Option[File]) : ApiResponse
def uploadFile(petId : Long, additionalMetadata : Option[String], file : Option[File]) : F[ApiResponse]
/**
* override uploadFile with the given handler
* @return a new implementation of PetService[F] with uploadFile overridden using the given handler
*/
final def onUploadFile(handler : (petId : Long, additionalMetadata : Option[String], file : Option[File]) => F[ApiResponse]) : PetService[F] = {
asHandler.copy(uploadFileHandler = handler)
}
/**
* @return a Handler implementation of this service
*/
final def asHandler : PetService.Handler[F] = this match {
case h : PetService.Handler[F] => h
case _ =>
PetService.Handler[F](
(pet) => addPet(pet),
(petId, apiKey) => deletePet(petId, apiKey),
(status) => findPetsByStatus(status),
(tags) => findPetsByTags(tags),
(petId) => getPetById(petId),
(pet) => updatePet(pet),
(petId, name, status) => updatePetWithForm(petId, name, status),
(petId, additionalMetadata, file) => uploadFile(petId, additionalMetadata, file)
)
}
/**
* This function will change the effect type of this service.
*
* It's not unlike a typical map operation from A => B, except we're not mapping
* a type from A to B, but rather from F[A] => G[A] using the 'changeEffect' function.
*
* For, this could turn an asynchronous service (one which returns Future[_] types) into
* a synchronous one (one which returns Try[_] types) by awaiting on the Future.
*
* It could change an IO type (like cats effect or ZIO) into an ID[A] which is just:
* ```
* type ID[A] => A
* ```
*
* @tparam G the new "polymorphic" effect type
* @param changeEffect the "natural transformation" which can change one effect type into another
* @return a new PetService service implementation with effect type [G]
*/
final def mapEffect[G[_]](changeEffect : [A] => F[A] => G[A]) : PetService[G] = {
val self = this
new PetService[G] {
override def addPet(pet : Pet) : G[Pet] = changeEffect {
self.addPet(pet)
}
override def deletePet(petId : Long, apiKey : Option[String]) : G[Unit] = changeEffect {
self.deletePet(petId, apiKey)
}
override def findPetsByStatus(status : Seq[String]) : G[List[Pet]] = changeEffect {
self.findPetsByStatus(status)
}
override def findPetsByTags(tags : Seq[String]) : G[List[Pet]] = changeEffect {
self.findPetsByTags(tags)
}
override def getPetById(petId : Long) : G[Pet] = changeEffect {
self.getPetById(petId)
}
override def updatePet(pet : Pet) : G[Pet] = changeEffect {
self.updatePet(pet)
}
override def updatePetWithForm(petId : Long, name : Option[String], status : Option[String]) : G[Unit] = changeEffect {
self.updatePetWithForm(petId, name, status)
}
override def uploadFile(petId : Long, additionalMetadata : Option[String], file : Option[File]) : G[ApiResponse] = changeEffect {
self.uploadFile(petId, additionalMetadata, file)
}
}
}
}

View File

@@ -19,40 +19,168 @@
package sample.cask.api
import _root_.sample.cask.model.Order
import scala.util.Failure
import scala.util.Try
import _root_.sample.cask.model.*
/**
* The StoreService companion object.
*
* Use the StoreService() companion object to create an instance which returns a 'not implemented' error
* for each operation.
*
*/
object StoreService {
def apply() : StoreService = new StoreService {
override def deleteOrder(orderId : String) : Unit = ???
override def getInventory() : Map[String, Int] = ???
override def getOrderById(orderId : Long) : Order = ???
override def placeOrder(order : Order) : Order = ???
/**
* The 'Handler' is an implementation of StoreService convenient for delegating or overriding individual functions
*/
case class Handler[F[_]](
deleteOrderHandler : (orderId : String) => F[Unit],
getInventoryHandler : () => F[Map[String, Int]],
getOrderByIdHandler : (orderId : Long) => F[Order],
placeOrderHandler : (order : Order) => F[Order]
) extends StoreService[F] {
override def deleteOrder(orderId : String) : F[Unit] = {
deleteOrderHandler(orderId)
}
override def getInventory() : F[Map[String, Int]] = {
getInventoryHandler()
}
override def getOrderById(orderId : Long) : F[Order] = {
getOrderByIdHandler(orderId)
}
override def placeOrder(order : Order) : F[Order] = {
placeOrderHandler(order)
}
}
def apply() : StoreService[Try] = StoreService.Handler[Try](
(_) => notImplemented("deleteOrder"),
() => notImplemented("getInventory"),
(_) => notImplemented("getOrderById"),
(_) => notImplemented("placeOrder")
)
private def notImplemented(name : String) = Failure(new Exception(s"TODO: $name not implemented"))
}
/**
* The Store business-logic
*
*
* The 'asHandler' will return an implementation which allows for easily overriding individual operations.
*
* equally there are "on&lt;Function&gt;" helper methods for easily overriding individual functions
*
* @tparam F the effect type (Future, Try, IO, ID, etc) of the operations
*/
trait StoreService {
trait StoreService[F[_]] {
/** Delete purchase order by ID
*
* @return
*/
def deleteOrder(orderId : String) : Unit
def deleteOrder(orderId : String) : F[Unit]
/**
* override deleteOrder with the given handler
* @return a new implementation of StoreService[F] with deleteOrder overridden using the given handler
*/
final def onDeleteOrder(handler : (orderId : String) => F[Unit]) : StoreService[F] = {
asHandler.copy(deleteOrderHandler = handler)
}
/** Returns pet inventories by status
*
* @return Map[String, Int]
*/
def getInventory() : Map[String, Int]
def getInventory() : F[Map[String, Int]]
/**
* override getInventory with the given handler
* @return a new implementation of StoreService[F] with getInventory overridden using the given handler
*/
final def onGetInventory(handler : () => F[Map[String, Int]]) : StoreService[F] = {
asHandler.copy(getInventoryHandler = handler)
}
/** Find purchase order by ID
*
* @return Order
*/
def getOrderById(orderId : Long) : Order
def getOrderById(orderId : Long) : F[Order]
/**
* override getOrderById with the given handler
* @return a new implementation of StoreService[F] with getOrderById overridden using the given handler
*/
final def onGetOrderById(handler : (orderId : Long) => F[Order]) : StoreService[F] = {
asHandler.copy(getOrderByIdHandler = handler)
}
/** Place an order for a pet
*
* @return Order
*/
def placeOrder(order : Order) : Order
def placeOrder(order : Order) : F[Order]
/**
* override placeOrder with the given handler
* @return a new implementation of StoreService[F] with placeOrder overridden using the given handler
*/
final def onPlaceOrder(handler : (order : Order) => F[Order]) : StoreService[F] = {
asHandler.copy(placeOrderHandler = handler)
}
/**
* @return a Handler implementation of this service
*/
final def asHandler : StoreService.Handler[F] = this match {
case h : StoreService.Handler[F] => h
case _ =>
StoreService.Handler[F](
(orderId) => deleteOrder(orderId),
() => getInventory(),
(orderId) => getOrderById(orderId),
(order) => placeOrder(order)
)
}
/**
* This function will change the effect type of this service.
*
* It's not unlike a typical map operation from A => B, except we're not mapping
* a type from A to B, but rather from F[A] => G[A] using the 'changeEffect' function.
*
* For, this could turn an asynchronous service (one which returns Future[_] types) into
* a synchronous one (one which returns Try[_] types) by awaiting on the Future.
*
* It could change an IO type (like cats effect or ZIO) into an ID[A] which is just:
* ```
* type ID[A] => A
* ```
*
* @tparam G the new "polymorphic" effect type
* @param changeEffect the "natural transformation" which can change one effect type into another
* @return a new StoreService service implementation with effect type [G]
*/
final def mapEffect[G[_]](changeEffect : [A] => F[A] => G[A]) : StoreService[G] = {
val self = this
new StoreService[G] {
override def deleteOrder(orderId : String) : G[Unit] = changeEffect {
self.deleteOrder(orderId)
}
override def getInventory() : G[Map[String, Int]] = changeEffect {
self.getInventory()
}
override def getOrderById(orderId : Long) : G[Order] = changeEffect {
self.getOrderById(orderId)
}
override def placeOrder(order : Order) : G[Order] = changeEffect {
self.placeOrder(order)
}
}
}
}

View File

@@ -20,64 +20,260 @@ package sample.cask.api
import _root_.java.time.OffsetDateTime
import _root_.sample.cask.model.User
import scala.util.Failure
import scala.util.Try
import _root_.sample.cask.model.*
/**
* The UserService companion object.
*
* Use the UserService() companion object to create an instance which returns a 'not implemented' error
* for each operation.
*
*/
object UserService {
def apply() : UserService = new UserService {
override def createUser(user : User) : Unit = ???
override def createUsersWithArrayInput(user : Seq[User]) : Unit = ???
override def createUsersWithListInput(user : Seq[User]) : Unit = ???
override def deleteUser(username : String) : Unit = ???
override def getUserByName(username : String) : User = ???
override def loginUser(username : String, password : String) : String = ???
override def logoutUser() : Unit = ???
override def updateUser(username : String, user : User) : Unit = ???
/**
* The 'Handler' is an implementation of UserService convenient for delegating or overriding individual functions
*/
case class Handler[F[_]](
createUserHandler : (user : User) => F[Unit],
createUsersWithArrayInputHandler : (user : Seq[User]) => F[Unit],
createUsersWithListInputHandler : (user : Seq[User]) => F[Unit],
deleteUserHandler : (username : String) => F[Unit],
getUserByNameHandler : (username : String) => F[User],
loginUserHandler : (username : String, password : String) => F[String],
logoutUserHandler : () => F[Unit],
updateUserHandler : (username : String, user : User) => F[Unit]
) extends UserService[F] {
override def createUser(user : User) : F[Unit] = {
createUserHandler(user)
}
override def createUsersWithArrayInput(user : Seq[User]) : F[Unit] = {
createUsersWithArrayInputHandler(user)
}
override def createUsersWithListInput(user : Seq[User]) : F[Unit] = {
createUsersWithListInputHandler(user)
}
override def deleteUser(username : String) : F[Unit] = {
deleteUserHandler(username)
}
override def getUserByName(username : String) : F[User] = {
getUserByNameHandler(username)
}
override def loginUser(username : String, password : String) : F[String] = {
loginUserHandler(username, password)
}
override def logoutUser() : F[Unit] = {
logoutUserHandler()
}
override def updateUser(username : String, user : User) : F[Unit] = {
updateUserHandler(username, user)
}
}
def apply() : UserService[Try] = UserService.Handler[Try](
(_) => notImplemented("createUser"),
(_) => notImplemented("createUsersWithArrayInput"),
(_) => notImplemented("createUsersWithListInput"),
(_) => notImplemented("deleteUser"),
(_) => notImplemented("getUserByName"),
(_, _) => notImplemented("loginUser"),
() => notImplemented("logoutUser"),
(_, _) => notImplemented("updateUser")
)
private def notImplemented(name : String) = Failure(new Exception(s"TODO: $name not implemented"))
}
/**
* The User business-logic
*
*
* The 'asHandler' will return an implementation which allows for easily overriding individual operations.
*
* equally there are "on&lt;Function&gt;" helper methods for easily overriding individual functions
*
* @tparam F the effect type (Future, Try, IO, ID, etc) of the operations
*/
trait UserService {
trait UserService[F[_]] {
/** Create user
*
* @return
*/
def createUser(user : User) : Unit
def createUser(user : User) : F[Unit]
/**
* override createUser with the given handler
* @return a new implementation of UserService[F] with createUser overridden using the given handler
*/
final def onCreateUser(handler : (user : User) => F[Unit]) : UserService[F] = {
asHandler.copy(createUserHandler = handler)
}
/** Creates list of users with given input array
*
* @return
*/
def createUsersWithArrayInput(user : Seq[User]) : Unit
def createUsersWithArrayInput(user : Seq[User]) : F[Unit]
/**
* override createUsersWithArrayInput with the given handler
* @return a new implementation of UserService[F] with createUsersWithArrayInput overridden using the given handler
*/
final def onCreateUsersWithArrayInput(handler : (user : Seq[User]) => F[Unit]) : UserService[F] = {
asHandler.copy(createUsersWithArrayInputHandler = handler)
}
/** Creates list of users with given input array
*
* @return
*/
def createUsersWithListInput(user : Seq[User]) : Unit
def createUsersWithListInput(user : Seq[User]) : F[Unit]
/**
* override createUsersWithListInput with the given handler
* @return a new implementation of UserService[F] with createUsersWithListInput overridden using the given handler
*/
final def onCreateUsersWithListInput(handler : (user : Seq[User]) => F[Unit]) : UserService[F] = {
asHandler.copy(createUsersWithListInputHandler = handler)
}
/** Delete user
*
* @return
*/
def deleteUser(username : String) : Unit
def deleteUser(username : String) : F[Unit]
/**
* override deleteUser with the given handler
* @return a new implementation of UserService[F] with deleteUser overridden using the given handler
*/
final def onDeleteUser(handler : (username : String) => F[Unit]) : UserService[F] = {
asHandler.copy(deleteUserHandler = handler)
}
/** Get user by user name
*
* @return User
*/
def getUserByName(username : String) : User
def getUserByName(username : String) : F[User]
/**
* override getUserByName with the given handler
* @return a new implementation of UserService[F] with getUserByName overridden using the given handler
*/
final def onGetUserByName(handler : (username : String) => F[User]) : UserService[F] = {
asHandler.copy(getUserByNameHandler = handler)
}
/** Logs user into the system
*
* @return String
*/
def loginUser(username : String, password : String) : String
def loginUser(username : String, password : String) : F[String]
/**
* override loginUser with the given handler
* @return a new implementation of UserService[F] with loginUser overridden using the given handler
*/
final def onLoginUser(handler : (username : String, password : String) => F[String]) : UserService[F] = {
asHandler.copy(loginUserHandler = handler)
}
/** Logs out current logged in user session
*
* @return
*/
def logoutUser() : Unit
def logoutUser() : F[Unit]
/**
* override logoutUser with the given handler
* @return a new implementation of UserService[F] with logoutUser overridden using the given handler
*/
final def onLogoutUser(handler : () => F[Unit]) : UserService[F] = {
asHandler.copy(logoutUserHandler = handler)
}
/** Updated user
*
* @return
*/
def updateUser(username : String, user : User) : Unit
def updateUser(username : String, user : User) : F[Unit]
/**
* override updateUser with the given handler
* @return a new implementation of UserService[F] with updateUser overridden using the given handler
*/
final def onUpdateUser(handler : (username : String, user : User) => F[Unit]) : UserService[F] = {
asHandler.copy(updateUserHandler = handler)
}
/**
* @return a Handler implementation of this service
*/
final def asHandler : UserService.Handler[F] = this match {
case h : UserService.Handler[F] => h
case _ =>
UserService.Handler[F](
(user) => createUser(user),
(user) => createUsersWithArrayInput(user),
(user) => createUsersWithListInput(user),
(username) => deleteUser(username),
(username) => getUserByName(username),
(username, password) => loginUser(username, password),
() => logoutUser(),
(username, user) => updateUser(username, user)
)
}
/**
* This function will change the effect type of this service.
*
* It's not unlike a typical map operation from A => B, except we're not mapping
* a type from A to B, but rather from F[A] => G[A] using the 'changeEffect' function.
*
* For, this could turn an asynchronous service (one which returns Future[_] types) into
* a synchronous one (one which returns Try[_] types) by awaiting on the Future.
*
* It could change an IO type (like cats effect or ZIO) into an ID[A] which is just:
* ```
* type ID[A] => A
* ```
*
* @tparam G the new "polymorphic" effect type
* @param changeEffect the "natural transformation" which can change one effect type into another
* @return a new UserService service implementation with effect type [G]
*/
final def mapEffect[G[_]](changeEffect : [A] => F[A] => G[A]) : UserService[G] = {
val self = this
new UserService[G] {
override def createUser(user : User) : G[Unit] = changeEffect {
self.createUser(user)
}
override def createUsersWithArrayInput(user : Seq[User]) : G[Unit] = changeEffect {
self.createUsersWithArrayInput(user)
}
override def createUsersWithListInput(user : Seq[User]) : G[Unit] = changeEffect {
self.createUsersWithListInput(user)
}
override def deleteUser(username : String) : G[Unit] = changeEffect {
self.deleteUser(username)
}
override def getUserByName(username : String) : G[User] = changeEffect {
self.getUserByName(username)
}
override def loginUser(username : String, password : String) : G[String] = changeEffect {
self.loginUser(username, password)
}
override def logoutUser() : G[Unit] = changeEffect {
self.logoutUser()
}
override def updateUser(username : String, user : User) : G[Unit] = changeEffect {
self.updateUser(username, user)
}
}
}
}

View File

@@ -13,41 +13,44 @@
// this model was generated using model.mustache
package sample.cask.model
import scala.util.control.NonFatal
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
case class ApiResponse(
code: Option[Int] = None ,
`type`: Option[String] = None ,
message: Option[String] = None
code: Option[Int] = None ,
`type`: Option[String] = None ,
message: Option[String] = None
) {
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asData : ApiResponseData = {
ApiResponseData(
code = code.getOrElse(0),
`type` = `type`.getOrElse(""),
message = message.getOrElse("")
)
}
def asData : ApiResponseData = {
ApiResponseData(
code = code.getOrElse(0) /* 1 */,
`type` = `type`.getOrElse("") /* 1 */,
message = message.getOrElse("") /* 1 */
)
}
}
object ApiResponse {
given RW[ApiResponse] = summon[RW[ujson.Value]].bimap[ApiResponse](_.asJson, json => read[ApiResponseData](json).asModel)
given RW[ApiResponse] = summon[RW[ujson.Value]].bimap[ApiResponse](_.asJson, json => read[ApiResponseData](json).asModel)
enum Fields(val fieldName : String) extends Field(fieldName) {
case code extends Fields("code")
case `type` extends Fields("`type`")
case message extends Fields("message")
}
enum Fields(val fieldName : String) extends Field(fieldName) {
case code extends Fields("code")
case `type` extends Fields("`type`")
case message extends Fields("message")
}
}

View File

@@ -20,7 +20,8 @@ import scala.util.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** ApiResponseData a data transfer object, primarily for simple json serialisation.
/** ApiResponseData a data transfer object, primarily for simple json serialisation.
* It has no validation - there may be nulls, values out of range, etc
*/
case class ApiResponseData(
@@ -61,6 +62,9 @@ case class ApiResponseData(
errors.toSeq
}
/**
* @return the validated model within a Try (if successful)
*/
def validated(failFast : Boolean = false) : scala.util.Try[ApiResponse] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
@@ -71,18 +75,9 @@ case class ApiResponseData(
/** use 'validated' to check validation */
def asModel : ApiResponse = {
ApiResponse(
code = Option(
code
)
,
`type` = Option(
`type`
)
,
message = Option(
message
)
code = Option(code) /* 1 */,
`type` = Option(`type`) /* 1 */,
message = Option(message) /* 1 */
)
}

View File

@@ -13,38 +13,41 @@
// this model was generated using model.mustache
package sample.cask.model
import scala.util.control.NonFatal
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
case class Category(
id: Option[Long] = None ,
name: Option[String] = None
id: Option[Long] = None ,
name: Option[String] = None
) {
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asData : CategoryData = {
CategoryData(
id = id.getOrElse(0),
name = name.getOrElse("")
)
}
def asData : CategoryData = {
CategoryData(
id = id.getOrElse(0) /* 1 */,
name = name.getOrElse("") /* 1 */
)
}
}
object Category {
given RW[Category] = summon[RW[ujson.Value]].bimap[Category](_.asJson, json => read[CategoryData](json).asModel)
given RW[Category] = summon[RW[ujson.Value]].bimap[Category](_.asJson, json => read[CategoryData](json).asModel)
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case name extends Fields("name")
}
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case name extends Fields("name")
}
}

View File

@@ -20,7 +20,8 @@ import scala.util.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** CategoryData a data transfer object, primarily for simple json serialisation.
/** CategoryData a data transfer object, primarily for simple json serialisation.
* It has no validation - there may be nulls, values out of range, etc
*/
case class CategoryData(
@@ -60,6 +61,9 @@ case class CategoryData(
errors.toSeq
}
/**
* @return the validated model within a Try (if successful)
*/
def validated(failFast : Boolean = false) : scala.util.Try[Category] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
@@ -70,14 +74,8 @@ case class CategoryData(
/** use 'validated' to check validation */
def asModel : Category = {
Category(
id = Option(
id
)
,
name = Option(
name
)
id = Option(id) /* 1 */,
name = Option(name) /* 1 */
)
}

View File

@@ -14,51 +14,54 @@
// this model was generated using model.mustache
package sample.cask.model
import java.time.OffsetDateTime
import scala.util.control.NonFatal
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
case class Order(
id: Option[Long] = None ,
petId: Option[Long] = None ,
quantity: Option[Int] = None ,
shipDate: Option[OffsetDateTime] = None ,
/* Order Status */
status: Option[Order.StatusEnum] = None ,
complete: Option[Boolean] = None
id: Option[Long] = None ,
petId: Option[Long] = None ,
quantity: Option[Int] = None ,
shipDate: Option[OffsetDateTime] = None ,
/* Order Status */
status: Option[Order.StatusEnum] = None ,
complete: Option[Boolean] = None
) {
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asData : OrderData = {
OrderData(
id = id.getOrElse(0),
petId = petId.getOrElse(0),
quantity = quantity.getOrElse(0),
shipDate = shipDate.getOrElse(null),
status = status.getOrElse(null),
complete = complete.getOrElse(false)
)
}
def asData : OrderData = {
OrderData(
id = id.getOrElse(0) /* 1 */,
petId = petId.getOrElse(0) /* 1 */,
quantity = quantity.getOrElse(0) /* 1 */,
shipDate = shipDate.getOrElse(null) /* 1 */,
status = status.getOrElse(null) /* 1 */,
complete = complete.getOrElse(false) /* 1 */
)
}
}
object Order {
given RW[Order] = summon[RW[ujson.Value]].bimap[Order](_.asJson, json => read[OrderData](json).asModel)
given RW[Order] = summon[RW[ujson.Value]].bimap[Order](_.asJson, json => read[OrderData](json).asModel)
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case petId extends Fields("petId")
case quantity extends Fields("quantity")
case shipDate extends Fields("shipDate")
case status extends Fields("status")
case complete extends Fields("complete")
}
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case petId extends Fields("petId")
case quantity extends Fields("quantity")
case shipDate extends Fields("shipDate")
case status extends Fields("status")
case complete extends Fields("complete")
}
// baseName=status
// nameInCamelCase = status

View File

@@ -21,7 +21,8 @@ import scala.util.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** OrderData a data transfer object, primarily for simple json serialisation.
/** OrderData a data transfer object, primarily for simple json serialisation.
* It has no validation - there may be nulls, values out of range, etc
*/
case class OrderData(
@@ -84,6 +85,9 @@ case class OrderData(
errors.toSeq
}
/**
* @return the validated model within a Try (if successful)
*/
def validated(failFast : Boolean = false) : scala.util.Try[Order] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
@@ -94,30 +98,12 @@ case class OrderData(
/** use 'validated' to check validation */
def asModel : Order = {
Order(
id = Option(
id
)
,
petId = Option(
petId
)
,
quantity = Option(
quantity
)
,
shipDate = Option(
shipDate
)
,
status = Option(
status
)
,
complete = Option(
complete
)
id = Option(id) /* 1 */,
petId = Option(petId) /* 1 */,
quantity = Option(quantity) /* 1 */,
shipDate = Option(shipDate) /* 1 */,
status = Option(status) /* 1 */,
complete = Option(complete) /* 1 */
)
}

View File

@@ -13,53 +13,54 @@
// this model was generated using model.mustache
package sample.cask.model
import sample.cask.model.Category
import sample.cask.model.Tag
import scala.util.control.NonFatal
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
case class Pet(
id: Option[Long] = None ,
category: Option[Category] = None ,
name: String,
photoUrls: Seq[String],
tags: Seq[Tag] = Nil ,
/* pet status in the store */
status: Option[Pet.StatusEnum] = None
id: Option[Long] = None ,
category: Option[Category] = None ,
name: String,
photoUrls: Seq[String],
tags: Seq[Tag] = Nil ,
/* pet status in the store */
status: Option[Pet.StatusEnum] = None
) {
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asData : PetData = {
PetData(
id = id.getOrElse(0),
category = category.map(_.asData).getOrElse(null),
name = name,
photoUrls = photoUrls,
tags = tags.map(_.asData),
status = status.getOrElse(null)
)
}
def asData : PetData = {
PetData(
id = id.getOrElse(0) /* 1 */,
category = category.map(_.asData).getOrElse(null) /* 4 */,
name = name /* 2 */,
photoUrls = photoUrls /* 2 */,
tags = tags.map(_.asData) /* 6 */,
status = status.getOrElse(null) /* 1 */
)
}
}
object Pet {
given RW[Pet] = summon[RW[ujson.Value]].bimap[Pet](_.asJson, json => read[PetData](json).asModel)
given RW[Pet] = summon[RW[ujson.Value]].bimap[Pet](_.asJson, json => read[PetData](json).asModel)
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case category extends Fields("category")
case name extends Fields("name")
case photoUrls extends Fields("photoUrls")
case tags extends Fields("tags")
case status extends Fields("status")
}
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case category extends Fields("category")
case name extends Fields("name")
case photoUrls extends Fields("photoUrls")
case tags extends Fields("tags")
case status extends Fields("status")
}
// baseName=status
// nameInCamelCase = status

View File

@@ -13,8 +13,6 @@
// this model was generated using modelData.mustache
package sample.cask.model
import sample.cask.model.Category
import sample.cask.model.Tag
import scala.util.control.NonFatal
import scala.util.*
@@ -22,7 +20,8 @@ import scala.util.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** PetData a data transfer object, primarily for simple json serialisation.
/** PetData a data transfer object, primarily for simple json serialisation.
* It has no validation - there may be nulls, values out of range, etc
*/
case class PetData(
@@ -101,6 +100,9 @@ case class PetData(
errors.toSeq
}
/**
* @return the validated model within a Try (if successful)
*/
def validated(failFast : Boolean = false) : scala.util.Try[Pet] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
@@ -111,30 +113,12 @@ case class PetData(
/** use 'validated' to check validation */
def asModel : Pet = {
Pet(
id = Option(
id
)
,
category = Option(
category
)
.map(_.asModel),
name =
name
,
photoUrls =
photoUrls
,
tags =
tags
.map(_.asModel),
status = Option(
status
)
id = Option(id) /* 1 */,
category = Option(category).map(_.asModel) /* 4 */,
name = name /* 2 */,
photoUrls = photoUrls /* 2 */,
tags = tags.map(_.asModel) /* 5 */,
status = Option(status) /* 1 */
)
}

View File

@@ -13,38 +13,41 @@
// this model was generated using model.mustache
package sample.cask.model
import scala.util.control.NonFatal
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
case class Tag(
id: Option[Long] = None ,
name: Option[String] = None
id: Option[Long] = None ,
name: Option[String] = None
) {
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asData : TagData = {
TagData(
id = id.getOrElse(0),
name = name.getOrElse("")
)
}
def asData : TagData = {
TagData(
id = id.getOrElse(0) /* 1 */,
name = name.getOrElse("") /* 1 */
)
}
}
object Tag {
given RW[Tag] = summon[RW[ujson.Value]].bimap[Tag](_.asJson, json => read[TagData](json).asModel)
given RW[Tag] = summon[RW[ujson.Value]].bimap[Tag](_.asJson, json => read[TagData](json).asModel)
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case name extends Fields("name")
}
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case name extends Fields("name")
}
}

View File

@@ -20,7 +20,8 @@ import scala.util.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** TagData a data transfer object, primarily for simple json serialisation.
/** TagData a data transfer object, primarily for simple json serialisation.
* It has no validation - there may be nulls, values out of range, etc
*/
case class TagData(
@@ -54,6 +55,9 @@ case class TagData(
errors.toSeq
}
/**
* @return the validated model within a Try (if successful)
*/
def validated(failFast : Boolean = false) : scala.util.Try[Tag] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
@@ -64,14 +68,8 @@ case class TagData(
/** use 'validated' to check validation */
def asModel : Tag = {
Tag(
id = Option(
id
)
,
name = Option(
name
)
id = Option(id) /* 1 */,
name = Option(name) /* 1 */
)
}

View File

@@ -13,57 +13,60 @@
// this model was generated using model.mustache
package sample.cask.model
import scala.util.control.NonFatal
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
case class User(
id: Option[Long] = None ,
username: Option[String] = None ,
firstName: Option[String] = None ,
lastName: Option[String] = None ,
email: Option[String] = None ,
password: Option[String] = None ,
phone: Option[String] = None ,
/* User Status */
userStatus: Option[Int] = None
id: Option[Long] = None ,
username: Option[String] = None ,
firstName: Option[String] = None ,
lastName: Option[String] = None ,
email: Option[String] = None ,
password: Option[String] = None ,
phone: Option[String] = None ,
/* User Status */
userStatus: Option[Int] = None
) {
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asJsonString: String = asData.asJsonString
def asJson: ujson.Value = asData.asJson
def asData : UserData = {
UserData(
id = id.getOrElse(0),
username = username.getOrElse(""),
firstName = firstName.getOrElse(""),
lastName = lastName.getOrElse(""),
email = email.getOrElse(""),
password = password.getOrElse(""),
phone = phone.getOrElse(""),
userStatus = userStatus.getOrElse(0)
)
}
def asData : UserData = {
UserData(
id = id.getOrElse(0) /* 1 */,
username = username.getOrElse("") /* 1 */,
firstName = firstName.getOrElse("") /* 1 */,
lastName = lastName.getOrElse("") /* 1 */,
email = email.getOrElse("") /* 1 */,
password = password.getOrElse("") /* 1 */,
phone = phone.getOrElse("") /* 1 */,
userStatus = userStatus.getOrElse(0) /* 1 */
)
}
}
object User {
given RW[User] = summon[RW[ujson.Value]].bimap[User](_.asJson, json => read[UserData](json).asModel)
given RW[User] = summon[RW[ujson.Value]].bimap[User](_.asJson, json => read[UserData](json).asModel)
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case username extends Fields("username")
case firstName extends Fields("firstName")
case lastName extends Fields("lastName")
case email extends Fields("email")
case password extends Fields("password")
case phone extends Fields("phone")
case userStatus extends Fields("userStatus")
}
enum Fields(val fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case username extends Fields("username")
case firstName extends Fields("firstName")
case lastName extends Fields("lastName")
case email extends Fields("email")
case password extends Fields("password")
case phone extends Fields("phone")
case userStatus extends Fields("userStatus")
}
}

View File

@@ -20,7 +20,8 @@ import scala.util.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** UserData a data transfer object, primarily for simple json serialisation.
/** UserData a data transfer object, primarily for simple json serialisation.
* It has no validation - there may be nulls, values out of range, etc
*/
case class UserData(
@@ -97,6 +98,9 @@ case class UserData(
errors.toSeq
}
/**
* @return the validated model within a Try (if successful)
*/
def validated(failFast : Boolean = false) : scala.util.Try[User] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
@@ -107,38 +111,14 @@ case class UserData(
/** use 'validated' to check validation */
def asModel : User = {
User(
id = Option(
id
)
,
username = Option(
username
)
,
firstName = Option(
firstName
)
,
lastName = Option(
lastName
)
,
email = Option(
email
)
,
password = Option(
password
)
,
phone = Option(
phone
)
,
userStatus = Option(
userStatus
)
id = Option(id) /* 1 */,
username = Option(username) /* 1 */,
firstName = Option(firstName) /* 1 */,
lastName = Option(lastName) /* 1 */,
email = Option(email) /* 1 */,
password = Option(password) /* 1 */,
phone = Option(phone) /* 1 */,
userStatus = Option(userStatus) /* 1 */
)
}