[Scala] added new scala-cask generator for the cask framework (#18344)

* Ran `./new.sh -n scala-cask -s` to generate a new Scala Cask impl

* removed scala-cask-petstore

* Added Scala-cask param parser for BigDecimals

* added scala cask to samples

* splitting out validation and json

* Added GitHub workflow support

* regenerated cask samples

* cask build fix for local builds

* regenerated samples

* trying to reproduce failed cask build. checking in compiles sources, which have been reformatted

* reverted whaitespace change

* cask fix - adding gitignored files

* scala cask toLowreCase fix

* scala cask toUpperCase fix

* cask regenerated samples

* improved exception handling for scala cask

* File separator fix for windows

* Noob fix for cask

* regenerated api package

* Removed environment variable settings, debug code

* Updated samples

* moved scala-cask output

* Regenerated samples

* scala cask fix

* Updated scala cask settings for more typical package structure
Removed cask client samples

* cask - reran generate samples

* Removed duplicate ScalaCaskServer entry

* minor enhancements

* update samples

* update templates

---------

Co-authored-by: aaron.pritzlaff <aaron@kindservices.co.uk>
This commit is contained in:
William Cheng
2024-04-10 18:49:59 +08:00
committed by GitHub
parent ef36ea410e
commit 2bfc5a3958
81 changed files with 6942 additions and 1 deletions

View File

@@ -0,0 +1,41 @@
name: Scala CI with sbt
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
- name: Cache sbt dependencies
uses: actions/cache@v2
with:
path: |
~/.ivy2/cache
~/.sbt
~/.m2
key: ${{ runner.os }}-sbt-${{ hashFiles('**/*.sbt') }}
restore-keys: |
${{ runner.os }}-sbt-
- name: Build with sbt
run: sbt clean compile
- name: Test with sbt
run: sbt test
- name: Publish to GitHub Packages
run: sbt publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,25 @@
# scala specific
*.class
*.log
# sbt specific
.cache
.history
.lib/
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
# Scala-IDE specific
.scala_dependencies
.worksheet
# Mill specific
out
# IntelliJ specific
.idea
*.iml

View File

@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@@ -0,0 +1,39 @@
.github/workflows/bulidAndPublish.yml
.gitignore
.scalafmt.conf
README.md
README.md
build.sbt
build.sc
example/Dockerfile
example/Server.scala
project/build.properties
project/plugins.sbt
src/main/scala/sample/cask/AppRoutes.scala
src/main/scala/sample/cask/BaseApp.scala
src/main/scala/sample/cask/ExampleApp.scala
src/main/scala/sample/cask/api/OpenApiRoutes.scala
src/main/scala/sample/cask/api/PetRoutes.scala
src/main/scala/sample/cask/api/PetRoutes.scala
src/main/scala/sample/cask/api/PetService.scala
src/main/scala/sample/cask/api/StoreRoutes.scala
src/main/scala/sample/cask/api/StoreRoutes.scala
src/main/scala/sample/cask/api/StoreService.scala
src/main/scala/sample/cask/api/UserRoutes.scala
src/main/scala/sample/cask/api/UserRoutes.scala
src/main/scala/sample/cask/api/UserService.scala
src/main/scala/sample/cask/api/package.scala
src/main/scala/sample/cask/model/ApiResponse.scala
src/main/scala/sample/cask/model/ApiResponseData.scala
src/main/scala/sample/cask/model/Category.scala
src/main/scala/sample/cask/model/CategoryData.scala
src/main/scala/sample/cask/model/Order.scala
src/main/scala/sample/cask/model/OrderData.scala
src/main/scala/sample/cask/model/Pet.scala
src/main/scala/sample/cask/model/PetData.scala
src/main/scala/sample/cask/model/Tag.scala
src/main/scala/sample/cask/model/TagData.scala
src/main/scala/sample/cask/model/User.scala
src/main/scala/sample/cask/model/UserData.scala
src/main/scala/sample/cask/model/package.scala
src/main/scala/sample/cask/package.scala

View File

@@ -0,0 +1 @@
7.5.0-SNAPSHOT

View File

@@ -0,0 +1,4 @@
version = 3.6.1
align.preset = more // For pretty alignment.
maxColumn = 100
runner.dialect = scala3

View File

@@ -0,0 +1,96 @@
# REST Service
This project contains the data models and REST services, generated from the [openapi-generator](https://github.com/OpenAPITools/openapi-generator) project.
The server implementation is based on Li's excellent [cask](https://com-lihaoyi.github.io/cask/) library.
# How to use this code
This code was designed so that it can be packaged up and semantically-versioned alongside your open-api schema.
That approach supports having separate "contract" repositories for microservice projects, where the pipeline for the
contract repo might produce versioned jar artefacts which can then easily be brought in / referenced by a separate service project which simply
implements the business logic.
You can read more about this approach [here](https://github.com/kindservices/contract-first-rest)
# How to implement your business logic
There are a few options for using this code/applying your business logic for the services.
## Option 1 (preferred): Package and publish this boilerplate
Typically, OpenApi templates are written to generate code which co-exists alongside the handwritten business logic.
While that works, it's also not ideal:
* You have to ensure the generated code isn't checked in
* Team members, build pipelines, etc all have to regenerate and recompile the same boilerplate code over and over
* People can encounter IDE issues with generated code
Instead, you have the option of simply packaging/publishing this generated code, and then allowing service implementations
to simply bring in the published code as a dependency.
The steps to do that are:
### Build/Publish
This project is built using [sbt](https://www.scala-sbt.org/download/), so you can run `sbt publish` (or `sbt publishLocal`)
Or, for a zero-install docker build:
```bash
docker run -it --rm -v $(pwd):/app -w /app sbtscala/scala-sbt:eclipse-temurin-17.0.4_1.7.1_3.2.0 sbt publishLocal
```
### Create a new separate implementation project
Once published, you can create your server implementation in a new, clean, separate project based on [the example](./example)
This means all the boilerplate endpoint and model code is brought in as "just another jar", and you're free to
create a greenfield project in whatever language (scala, java, kotlin) and build system of your choosing.
We show a simple, minimalistic example of a starting point in [the example project](./example)
## Option 2: Extend this generated example
You can configure this project (for instance, setting up your own .gitignore rules and scripts) to leave the generated code as-is
and provide your implementation alongside the generated code.
The place to start is by providing your own implementation of the Services defined in the `api` package -
perhaps by creating your 'MyService.scala' code in a new `impl` package.
You then have several options for how to wire those in:
1) Create a new BaseApp instance to create your own Main entry point
Follow the pattern in App.scala, but by passing your own implementations to BaseApp,
ensuring you call `start` to start the server
```bash
@main def run() = BaseApp(/* your services here/*).start()
```
2) Extend either BaseApp class or mix in the AppRoutes trait
You can create your own main entry point with further control of the main cask app by extending
the BaseApp or otherwise creating your own CaskApp which mixes in the AppRoutes
```bash
object MyApp extends BaseApp(/* your services here/*) {
// any overrides, new routes, etc here
start()
}
```
# Customising the generated code
A typical config.yml used to alter the generated code may look like this:
```
groupId: "ex.amp.le"
artifactId: "pets-test"
apiPackage: "ex.ample.api"
modelPackage: "ex.ample.model"
```
Which you would then pass to the generator like this:
```
docker run --rm \
-v ${PWD}:/local openapitools/openapi-generator-cli generate \
-i https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.yaml \
-g scala-cask \
-c /local/config.yml \
-o /local/path/to/output_dir
```

View File

@@ -0,0 +1,29 @@
name := "scala-cask-petstore"
organization:="cask.groupId"
version := "0.0.1-SNAPSHOT"
scalaVersion := "3.3.1"
scalafmtOnCompile := true
libraryDependencies ++= Seq(
"com.lihaoyi" %% "cask" % "0.9.2" ,
"com.lihaoyi" %% "upickle" % "3.2.0",
"org.scalatest" %% "scalatest" % "3.2.18" % Test
)
publishMavenStyle := true
val githubUser = "GIT_USER_ID"
val githubRepo = "GIT_REPO_ID"
publishTo := Some("GitHub Package Registry" at s"https://maven.pkg.github.com/$githubUser/$githubRepo")
sys.env.get("GITHUB_TOKEN") match {
case Some(token) if !token.isEmpty =>
credentials += Credentials(
"GitHub Package Registry",
"maven.pkg.github.com",
githubUser,
token
)
case _ =>
println("\n\t\tGITHUB_TOKEN not set - assuming a local build\n\n")
credentials ++= Nil
}

View File

@@ -0,0 +1,43 @@
import mill._, scalalib._, scalafmt._, publish._
// Mill build file (see https://mill-build.com/mill/Intro_to_Mill.html).
// run with:
//
// mill _.compile
// mill _.reformat
// mill _.publishLocal
// mill _.test.test
object scala-cask-petstore extends SbtModule with ScalafmtModule with PublishModule {
def scalaVersion = "3.3.1"
def pomSettings = PomSettings(
description = "scala-cask-petstore",
organization = "cask.groupId",
url = "https://github.com/<username>/scala-cask-petstore",
licenses = Seq(License.MIT),
versionControl = VersionControl.github("<username>", "scala-cask-petstore"),
developers = Seq(
// Developer("<username>", "<your name>", "https://github.com/<you>")
)
)
def publishVersion: mill.T[String] = T("0.0.1-SNAPSHOT")
def ivyDeps = Agg(
ivy"com.lihaoyi::cask:0.9.2" ,
ivy"com.lihaoyi::upickle:3.2.0"
)
override def sources = T.sources(millSourcePath / os.up / "src" / "main" / "scala")
override def resources = T.sources(millSourcePath / os.up / "src" / "main" / "resources")
object test extends SbtModuleTests {
def ivyDeps = Agg(
ivy"org.scalactic::scalactic:3.2.18",
ivy"org.scalatest::scalatest:3.2.18"
)
def testFramework = "org.scalatest.tools.Framework"
override def sources = T.sources(millSourcePath / os.up / "src" / "test" / "scala")
}
}

View File

@@ -0,0 +1,13 @@
FROM virtuslab/scala-cli:latest as build
WORKDIR /app
COPY ./Server.scala /app/
# note: this assumes a published server stub jar.
# If you've published this locally, you would need to copy those into this image,
# perhaps by using coursier fetch
RUN scala-cli --power package /app/Server.scala --assembly -o app.jar
# The main image
FROM openjdk:23-slim
WORKDIR /app
COPY --from=build /app/app.jar /app/
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

View File

@@ -0,0 +1,61 @@
//> using scala "3.3.1"
//> using lib "cask.groupId::scala-cask-petstore:0.0.1-SNAPSHOT"
//> using repositories https://maven.pkg.github.com/GIT_USER_ID/GIT_REPO_ID
/**
* This single file can contain the business logic for a REST service.
* ====================================
* == zero-install build with docker ==
* ====================================
*
*
* ```
* docker build . -t scala-cask-petstore:latest
* ```
* ======================
* == Building Locally ==
* ======================
* This project can be built using [[scala-clit][https://scala-cli.virtuslab.org]]
*
* To simply run the project
* ```
* scala-cli Server.scala
* ```
*
* To create a runnable jar, run:
* ```
* scala-cli --power package Server.scala -o app-assembly --assembly
* ```
*
* To produce a docker image (no need for the Dockerfile), run:
* ```
* scala-cli --power package --docker Server.scala --docker-image-repository app-docker
* ```
*
* To generate an IDE project:
* ```
* scala-cli setup-ide . --scala 3.3
* ```
*/
package app
import cask.groupId.server.BaseApp
import sample.cask.api.*
import sample.cask.model.*
import java.io.File
// TODO - write your business logic for your services here (the defaults all return 'not implemented'):
val myPetService : PetService = PetService() // <-- replace this with your implementation
val myStoreService : StoreService = StoreService() // <-- replace this with your implementation
val myUserService : UserService = UserService() // <-- replace this with your implementation
/** This is your main entry point for your REST service
* It extends BaseApp which defines the business logic for your services
*/
object Server extends BaseApp(appPetService = myPetService,
appStoreService = myStoreService,
appUserService = myUserService):
start()

View File

@@ -0,0 +1 @@
sbt.version=1.9.9

View File

@@ -0,0 +1,3 @@
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.9.2"
//> using lib "com.lihaoyi::scalatags:0.8.2"
/**
* 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 file was generated from app.mustache
package cask.groupId.server
import _root_.sample.cask.model.*
import _root_.sample.cask.api.*
/**
* This trait encapsulates the business logic (services) and the
* http routes which handle the http requests sent to those services.
*
* There are default 'not implemented' implementations for the service.
*
* 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 routeForPet : PetRoutes = PetRoutes(appPetService)
def appStoreService : StoreService = StoreService()
def routeForStore : StoreRoutes = StoreRoutes(appStoreService)
def appUserService : UserService = UserService()
def routeForUser : UserRoutes = UserRoutes(appUserService)
def appRoutes = Seq(
routeForPet ,
routeForStore ,
routeForUser
)
}

View File

@@ -0,0 +1,59 @@
//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.9.2"
//> using lib "com.lihaoyi::scalatags:0.8.2"
/**
* 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 file was generated from app.mustache
package cask.groupId.server
import _root_.sample.cask.model.*
import _root_.sample.cask.api.*
/**
* This class was created with the intention of being extended by some runnable object,
* passing in the custom business logic services
*/
class BaseApp(
override val appPetService : PetService = PetService(),
override val appStoreService : StoreService = StoreService(),
override val appUserService : UserService = UserService(),
override val port : Int = sys.env.get("PORT").map(_.toInt).getOrElse(8080)) extends cask.MainRoutes with AppRoutes {
/** routes for the UI
* Subclasses can override to turn this off
*/
def openApiRoute: Option[cask.Routes] = Option(OpenApiRoutes(port))
override def allRoutes = appRoutes ++ openApiRoute
override def host: String = "0.0.0.0"
def start() = locally {
initialize()
println(box(s""" 🚀 browse to localhost:$port 🚀
| host : $host
| port : $port
| verbose : $verbose
| debugMode : $debugMode
|""".stripMargin))
// if java.awt.Desktop.isDesktopSupported then {
// java.awt.Desktop.getDesktop.browse(new java.net.URI(s"http://localhost:${port}"))
// }
}
}

View File

@@ -0,0 +1,31 @@
//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.9.2"
//> using lib "com.lihaoyi::scalatags:0.8.2"
/**
* 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 file was generated from app.mustache
package cask.groupId.server
import _root_.sample.cask.model.*
import _root_.sample.cask.api.*
/**
* 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
*/
object ExampleApp extends BaseApp() {
start()
}

View File

@@ -0,0 +1,128 @@
/**
* 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
*/
// generated from openapiRoute.mustache
package sample.cask.api
import cask.model.Response
import java.nio.file.{Files, Path, Paths}
/**
* This code will try and download the swagger UI static files on startup
*
* That behaviour can be altered by:
* - setting the environment variable SWAGGER_ON to false
* - setting the environment variable SWAGGER_UI_URL to either the URL of a swagger UI zip or setting it to the empty string
*
*/
object OpenApiRoutes {
def swaggerUIUrl: Option[String] = {
// flag to turn SWAGGER off
def useSwaggerUI = sys.env.get("SWAGGER_ON").map(_.toBoolean).getOrElse(true)
val defaultUrl = "https://github.com/swagger-api/swagger-ui/archive/refs/tags/v5.11.9.zip"
Option(sys.env.getOrElse("SWAGGER_UI_URL", defaultUrl))
.map(_.trim)
.filterNot(_.isEmpty)
.filter(_ => useSwaggerUI)
}
def apply(localPort: Int) = new OpenApiRoutes(localPort, swaggerUIUrl)
}
class OpenApiRoutes(localPort: Int, swaggerUrl: Option[String]) extends cask.Routes {
def openApiDir = "ui"
@cask.get("/")
def index() = cask.Redirect("/ui/index.html")
@cask.staticFiles("/ui")
def staticUI() = openApiDir
@cask.staticResources("/openapi.json")
def staticOpenApi() = "openapi.json"
/** This code will try and download the swagger UI artefacts to a local directory to serve up
*/
object extract {
def openApiDirPath: Path = Paths.get(openApiDir)
def hasSwagger = Files.exists(openApiDirPath) && Files.isDirectory(openApiDirPath)
import java.io.{BufferedInputStream, FileOutputStream, InputStream}
import java.net.URL
import java.util.zip.{ZipEntry, ZipInputStream}
import scala.util.Using
def apply(url: String) = {
if !hasSwagger then downloadAndExtractZip(url, openApiDir)
}
def downloadAndExtractZip(url: String, outputDir: String): Unit = {
val urlConn = new URL(url).openConnection()
urlConn.setRequestProperty("User-Agent", "Mozilla/5.0")
Using(urlConn.getInputStream) { inputStream =>
val zipIn = new ZipInputStream(new BufferedInputStream(inputStream))
LazyList.continually(zipIn.getNextEntry).takeWhile(_ != null).foreach { entry =>
def isDist = entry.getName.contains("/dist/")
def isNotMap = !entry.getName.endsWith(".map")
if (!entry.isDirectory && isDist && isNotMap) {
val fileName = entry.getName.split("/").last
extractFile(entry.getName, zipIn, s"$outputDir/$fileName")
}
zipIn.closeEntry()
}
}
}
def extractFile(name: String, zipIn: ZipInputStream, filePath: String): Unit = {
val fullPath = Paths.get(filePath).toAbsolutePath
if !Files.exists(fullPath.getParent) then {
Files.createDirectories(fullPath.getParent)
}
// config hack - we replace the default url from this swagger conf to use our localhost
//
if name.endsWith("swagger-initializer.js") then {
val textLines = scala.io.Source.fromInputStream(zipIn).getLines().map {
case line if line.contains("url:") =>
s""" url: "http://localhost:$localPort/openapi.json","""
case line => line
}
// keeping this compatible for java 8, where this is from >= java 11:
// Files.writeString(fullPath, textLines.mkString("\n"))
scala.util.Using(new java.io.PrintWriter(fullPath.toFile))(_.write(textLines.mkString("\n")))
} else {
Using(new FileOutputStream(filePath)) { outputStream =>
val buffer = new Array[Byte](1024)
LazyList
.continually(zipIn.read(buffer))
.takeWhile(_ != -1)
.foreach(outputStream.write(buffer, 0, _))
}
}
}
}
// extract the swagger UI resources to our local directory
swaggerUrl.foreach(url => extract(url))
initialize()
}

View File

@@ -0,0 +1,212 @@
//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.8.3"
//> using lib "com.lihaoyi::scalatags:0.12.0"
/**
* 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 is generated from apiRoutes.mustache
package sample.cask.api
import sample.cask.model.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
import sample.cask.model.ApiResponse
import java.io.File
import sample.cask.model.Pet
class PetRoutes(service : PetService) extends cask.Routes {
// route group for routeWorkAroundForPOSTPet
@cask.post("/pet", true)
def routeWorkAroundForPOSTPet(request: cask.Request) = {
request.remainingPathSegments match {
case Seq() => addPet(request)
case Seq(petId) => updatePetWithForm(petId.toLong,request)
case Seq(petId,"uploadImage") => uploadFile(petId.toLong,request)
case _ => cask.Response("Not Found", statusCode = 404)
}
}
// route group for routeWorkAroundForGETPet
@cask.get("/pet", true)
def routeWorkAroundForGETPet(request: cask.Request,status : Seq[String] = Nil,tags : Seq[String] = Nil) = {
request.remainingPathSegments match {
case Seq("findByStatus") => findPetsByStatus(request,status)
case Seq("findByTags") => findPetsByTags(request,tags)
case Seq(petId) => getPetById(petId.toLong,request)
case _ => cask.Response("Not Found", statusCode = 404)
}
}
/** Add a new pet to the store
*
*/
// conflicts with [/pet/{petId}, /pet/{petId}/uploadImage, /pet] after/pet, ignoring @cask.post("/pet")
def addPet(request: cask.Request) = {
// auth method petstore_auth : oauth2, keyParamName:
def failFast = request.queryParams.keySet.contains("failFast")
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 */
pet <- Parsed.fromTry(petData.validated(failFast))
result <- Parsed.eval(service.addPet(pet))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Deletes a pet
*
*/
@cask.delete("/pet/:petId")
def deletePet(petId : Long, request: cask.Request) = {
// auth method petstore_auth : oauth2, keyParamName:
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
petId <- Parsed(petId)
apiKey <- request.headerSingleValueOptional("apiKey")
result <- Parsed.eval(service.deletePet(petId, apiKey))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Finds Pets by status
*
*/
// conflicts with [/pet/{petId}, /pet/findByStatus, /pet/findByTags] after/pet, ignoring @cask.get("/pet/findByStatus")
def findPetsByStatus(request: cask.Request, status : Seq[String]) = {
// auth method petstore_auth : oauth2, keyParamName:
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.findPetsByStatus(status))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Finds Pets by tags
*
*/
// conflicts with [/pet/{petId}, /pet/findByStatus, /pet/findByTags] after/pet, ignoring @cask.get("/pet/findByTags")
def findPetsByTags(request: cask.Request, tags : Seq[String]) = {
// auth method petstore_auth : oauth2, keyParamName:
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.findPetsByTags(tags))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Find pet by ID
*
*/
// conflicts with [/pet/{petId}, /pet/findByStatus, /pet/findByTags] after/pet, ignoring @cask.get("/pet/:petId")
def getPetById(petId : Long, request: cask.Request) = {
// auth method api_key : apiKey, keyParamName: api_key
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
petId <- Parsed(petId)
result <- Parsed.eval(service.getPetById(petId))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Update an existing pet
*
*/
@cask.put("/pet")
def updatePet(request: cask.Request) = {
// auth method petstore_auth : oauth2, keyParamName:
def failFast = request.queryParams.keySet.contains("failFast")
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 */
pet <- Parsed.fromTry(petData.validated(failFast))
result <- Parsed.eval(service.updatePet(pet))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Updates a pet in the store with form data
*
*/
// conflicts with [/pet/{petId}, /pet/{petId}/uploadImage, /pet] after/pet, ignoring @cask.post("/pet/:petId")
def updatePetWithForm(petId : Long, request: cask.Request) = {
// auth method petstore_auth : oauth2, keyParamName:
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
petId <- Parsed(petId)
name <- request.formSingleValueOptional("name")
status <- request.formSingleValueOptional("status")
result <- Parsed.eval(service.updatePetWithForm(petId, name, status))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** uploads an image
*
*/
// conflicts with [/pet/{petId}, /pet/{petId}/uploadImage, /pet] after/pet, ignoring @cask.post("/pet/:petId/uploadImage")
def uploadFile(petId : Long, request: cask.Request) = {
// auth method petstore_auth : oauth2, keyParamName:
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
petId <- Parsed(petId)
additionalMetadata <- request.formSingleValueOptional("additionalMetadata")
file <- request.formValueAsFileOptional("file")
result <- Parsed.eval(service.uploadFile(petId, additionalMetadata, file))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
initialize()
}

View File

@@ -0,0 +1,84 @@
//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.8.3"
//> using lib "com.lihaoyi::scalatags:0.12.0"
/**
* 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
*/
// generated from apiService.mustache
package sample.cask.api
import _root_.sample.cask.model.ApiResponse
import _root_.java.io.File
import _root_.sample.cask.model.Pet
import _root_.sample.cask.model.*
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 Pet business-logic
*/
trait PetService {
/** Add a new pet to the store
*
* @return Pet
*/
def addPet(pet : Pet) : Pet
/** Deletes a pet
*
* @return
*/
def deletePet(petId : Long, apiKey : Option[String]) : Unit
/** Finds Pets by status
*
* @return List[Pet]
*/
def findPetsByStatus(status : Seq[String]) : List[Pet]
/** Finds Pets by tags
*
* @return List[Pet]
*/
def findPetsByTags(tags : Seq[String]) : List[Pet]
/** Find pet by ID
*
* @return Pet
*/
def getPetById(petId : Long) : Pet
/** Update an existing pet
*
* @return Pet
*/
def updatePet(pet : Pet) : Pet
/** Updates a pet in the store with form data
*
* @return
*/
def updatePetWithForm(petId : Long, name : Option[String], status : Option[String]) : Unit
/** uploads an image
*
* @return ApiResponse
*/
def uploadFile(petId : Long, additionalMetadata : Option[String], file : Option[File]) : ApiResponse
}

View File

@@ -0,0 +1,106 @@
//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.8.3"
//> using lib "com.lihaoyi::scalatags:0.12.0"
/**
* 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 is generated from apiRoutes.mustache
package sample.cask.api
import sample.cask.model.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
import sample.cask.model.Order
class StoreRoutes(service : StoreService) extends cask.Routes {
/** Delete purchase order by ID
*
*/
@cask.delete("/store/order/:orderId")
def deleteOrder(orderId : String, request: cask.Request) = {
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
orderId <- Parsed(orderId)
result <- Parsed.eval(service.deleteOrder(orderId))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Returns pet inventories by status
*
*/
@cask.get("/store/inventory")
def getInventory(request: cask.Request) = {
// auth method api_key : apiKey, keyParamName: api_key
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.getInventory())
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Find purchase order by ID
*
*/
@cask.get("/store/order/:orderId")
def getOrderById(orderId : Long, request: cask.Request) = {
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
orderId <- Parsed(orderId)
result <- Parsed.eval(service.getOrderById(orderId))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Place an order for a pet
*
*/
@cask.post("/store/order")
def placeOrder(request: cask.Request) = {
def failFast = request.queryParams.keySet.contains("failFast")
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 */
order <- Parsed.fromTry(orderData.validated(failFast))
result <- Parsed.eval(service.placeOrder(order))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
initialize()
}

View File

@@ -0,0 +1,58 @@
//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.8.3"
//> using lib "com.lihaoyi::scalatags:0.12.0"
/**
* 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
*/
// generated from apiService.mustache
package sample.cask.api
import _root_.sample.cask.model.Order
import _root_.sample.cask.model.*
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 Store business-logic
*/
trait StoreService {
/** Delete purchase order by ID
*
* @return
*/
def deleteOrder(orderId : String) : Unit
/** Returns pet inventories by status
*
* @return Map[String, Int]
*/
def getInventory() : Map[String, Int]
/** Find purchase order by ID
*
* @return Order
*/
def getOrderById(orderId : Long) : Order
/** Place an order for a pet
*
* @return Order
*/
def placeOrder(order : Order) : Order
}

View File

@@ -0,0 +1,195 @@
//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.8.3"
//> using lib "com.lihaoyi::scalatags:0.12.0"
/**
* 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 is generated from apiRoutes.mustache
package sample.cask.api
import sample.cask.model.*
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
import java.time.OffsetDateTime
import sample.cask.model.User
class UserRoutes(service : UserService) extends cask.Routes {
// route group for routeWorkAroundForGETUser
@cask.get("/user", true)
def routeWorkAroundForGETUser(request: cask.Request,username : Option[String] = None,password : Option[String] = None) = {
request.remainingPathSegments match {
case Seq("login") => loginUser(request,username.getOrElse(""), password.getOrElse(""))
case Seq("logout") => logoutUser(request)
case Seq(username) => getUserByName(username,request)
case _ => cask.Response("Not Found", statusCode = 404)
}
}
/** Create user
*
*/
@cask.post("/user")
def createUser(request: cask.Request) = {
// auth method api_key : apiKey, keyParamName: api_key
def failFast = request.queryParams.keySet.contains("failFast")
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 */
user <- Parsed.fromTry(userData.validated(failFast))
result <- Parsed.eval(service.createUser(user))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Creates list of users with given input array
*
*/
@cask.post("/user/createWithArray")
def createUsersWithArrayInput(request: cask.Request) = {
// auth method api_key : apiKey, keyParamName: api_key
def failFast = request.queryParams.keySet.contains("failFast")
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))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Creates list of users with given input array
*
*/
@cask.post("/user/createWithList")
def createUsersWithListInput(request: cask.Request) = {
// auth method api_key : apiKey, keyParamName: api_key
def failFast = request.queryParams.keySet.contains("failFast")
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))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Delete user
*
*/
@cask.delete("/user/:username")
def deleteUser(username : String, request: cask.Request) = {
// auth method api_key : apiKey, keyParamName: api_key
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
username <- Parsed(username)
result <- Parsed.eval(service.deleteUser(username))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Get user by user name
*
*/
// conflicts with [/user/{username}, /user/login, /user/logout] after/user, ignoring @cask.get("/user/:username")
def getUserByName(username : String, request: cask.Request) = {
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
username <- Parsed(username)
result <- Parsed.eval(service.getUserByName(username))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Logs user into the system
*
*/
// conflicts with [/user/{username}, /user/login, /user/logout] after/user, ignoring @cask.get("/user/login")
def loginUser(request: cask.Request, username : String, password : String) = {
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.loginUser(username, password))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Logs out current logged in user session
*
*/
// conflicts with [/user/{username}, /user/login, /user/logout] after/user, ignoring @cask.get("/user/logout")
def logoutUser(request: cask.Request) = {
// auth method api_key : apiKey, keyParamName: api_key
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
result <- Parsed.eval(service.logoutUser())
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
/** Updated user
*
*/
@cask.put("/user/:username")
def updateUser(username : String, request: cask.Request) = {
// auth method api_key : apiKey, keyParamName: api_key
def failFast = request.queryParams.keySet.contains("failFast")
val result = for {
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 */
user <- Parsed.fromTry(userData.validated(failFast))
result <- Parsed.eval(service.updateUser(username, user))
} yield result
result match {
case Left(error) => cask.Response(error, 500)
case Right(_) => cask.Response("", 200)
}
}
initialize()
}

View File

@@ -0,0 +1,83 @@
//> using scala "3.3.1"
//> using lib "com.lihaoyi::cask:0.8.3"
//> using lib "com.lihaoyi::scalatags:0.12.0"
/**
* 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
*/
// generated from apiService.mustache
package sample.cask.api
import _root_.java.time.OffsetDateTime
import _root_.sample.cask.model.User
import _root_.sample.cask.model.*
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 User business-logic
*/
trait UserService {
/** Create user
*
* @return
*/
def createUser(user : User) : Unit
/** Creates list of users with given input array
*
* @return
*/
def createUsersWithArrayInput(user : Seq[User]) : Unit
/** Creates list of users with given input array
*
* @return
*/
def createUsersWithListInput(user : Seq[User]) : Unit
/** Delete user
*
* @return
*/
def deleteUser(username : String) : Unit
/** Get user by user name
*
* @return User
*/
def getUserByName(username : String) : User
/** Logs user into the system
*
* @return String
*/
def loginUser(username : String, password : String) : String
/** Logs out current logged in user session
*
* @return
*/
def logoutUser() : Unit
/** Updated user
*
* @return
*/
def updateUser(username : String, user : User) : Unit
}

View File

@@ -0,0 +1,167 @@
/**
* 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
*/
package sample.cask.api
import cask.FormEntry
import io.undertow.server.handlers.form.{FormData, FormParserFactory}
import java.io.File
import scala.jdk.CollectionConverters.*
import java.time.LocalDate
import java.util.UUID
import scala.reflect.ClassTag
import scala.util.*
// needed for BigDecimal params
given cask.endpoints.QueryParamReader.SimpleParam[BigDecimal](BigDecimal.apply)
// a parsed value from an HTTP request
opaque type Parsed[A] = Either[String, A]
object Parsed {
def apply[A](value: A): Parsed[A] = Right(value)
def eval[A](value: => A): Parsed[A] = Try(value) match {
case Failure(exp) => Left(s"Error: ${exp.getMessage}")
case Success(ok) => Right(ok)
}
def fromTry[A](value : Try[A]) = value match {
case Failure(err) => Left(err.getMessage)
case Success(ok) => Right(ok)
}
def fail[A](msg: String): Parsed[A] = Left(msg)
def optionalValue(map: Map[String, collection.Seq[String]], key: String): Parsed[Option[String]] = {
map.get(key) match {
case Some(Seq(only: String)) => Parsed(Option(only))
case Some(Seq()) => Parsed(None)
case Some(many) => Parsed.fail(s"${many.size} values set for '$key'")
case None => Parsed(None)
}
}
def singleValue(map: Map[String, collection.Seq[String]], key : String): Parsed[String] = {
map.get(key) match {
case Some(Seq(only : String)) => Parsed(only)
case Some(Seq()) => Parsed("")
case Some(many) => Parsed.fail(s"${many.size} values set for '$key'")
case None => Parsed.fail(s"required '$key' was not set")
}
}
def manyValues(map: Map[String, collection.Seq[String]], key : String, required: Boolean): Parsed[List[String]] = {
map.get(key) match {
case Some(many) => Parsed(many.toList)
case None if required => Parsed.fail(s"required '$key' was not set")
case None => Parsed(Nil)
}
}
}
extension[A] (parsed: Parsed[A]) {
def toEither: Either[String, A] = parsed
def asLong(using ev : A =:= String): Parsed[Long] = as[Long](_.toLongOption)
def asBoolean(using ev : A =:= String): Parsed[Boolean] = as[Boolean](_.toBooleanOption)
def asInt(using ev : A =:= String): Parsed[Int] = as[Int](_.toIntOption)
def asByte(using ev : A =:= String): Parsed[Byte] = as[Byte](_.toByteOption)
def asUuid(using ev : A =:= String): Parsed[UUID] = as[UUID](x => Try(UUID.fromString(x)).toOption)
def asFloat(using ev : A =:= String): Parsed[Float] = as[Float](_.toFloatOption)
def asDouble(using ev : A =:= String): Parsed[Double] = as[Double](_.toDoubleOption)
def asDate(using ev: A =:= String): Parsed[LocalDate] = as[LocalDate](x => Try(LocalDate.parse(x)).toOption)
private def as[B : ClassTag](f : String => Option[B])(using ev : A =:= String): Parsed[B] = parsed.flatMap { str =>
f(ev(str)) match {
case None => Parsed.fail(s"'$str' cannot be parsed as a ${implicitly[ClassTag[B]].runtimeClass}")
case Some(x) => Parsed(x)
}
}
def mapError(f : String => String) : Parsed[A] = parsed match {
case Left(msg) => Left(f(msg))
case right => right
}
def map[B](f: A => B): Parsed[B] = parsed match {
case Right(value) => Right(f(value))
case Left(err) => Left(err)
}
def flatMap[B](f : A => Parsed[B]): Parsed[B] = parsed match {
case Right(value) => f(value)
case Left(err) => Left(err)
}
}
extension (request: cask.Request) {
def formSingleValueRequired(paramName: String): Parsed[String] = {
val data = formDataForKey(paramName).map(_.getValue).toSeq
Parsed.singleValue(Map(paramName -> data), paramName)
}
def formSingleValueOptional(paramName: String): Parsed[Option[String]] = {
val data = formDataForKey(paramName).map(_.getValue).toSeq
Parsed.optionalValue(Map(paramName -> data), paramName)
}
def formValueAsFileOptional(paramName: String): Parsed[Option[File]] = {
val data = formDataForKey(paramName)
data.map(_.getFileItem.getFile.toFile).toSeq match {
case Seq() => Parsed(None)
case Seq(file) => Parsed(Option(file))
case many => Parsed.fail(s"${many.size} file values set for '$paramName'")
}
}
def formValueAsFileRequired(paramName: String): Parsed[File] = {
val data = formDataForKey(paramName)
data.map(_.getFileItem.getFile.toFile).toSeq match {
case Seq() => Parsed.fail(s"No file form data was submitted for '$paramName'. The submitted form keys were: ${formDataKeys.mkString(",")}")
case Seq(file) => Parsed(file)
case many => Parsed.fail(s"${many.size} file values set for '$paramName'")
}
}
def formManyValues(paramName: String, required: Boolean): Parsed[List[String]] = {
val data = formDataForKey(paramName).map(_.getValue).toSeq
Parsed.manyValues(Map(paramName -> data), paramName, required)
}
def formData: FormData = FormParserFactory.builder().build().createParser(request.exchange).parseBlocking()
def formDataKeys: Iterator[String] = formData.iterator().asScala
def formDataForKey(paramName: String): Iterable[FormData.FormValue] = formData.get(paramName).asScala
def headerSingleValueOptional(paramName: String): Parsed[Option[String]] = Parsed.optionalValue(request.headers, paramName)
def headerSingleValueRequired(paramName: String): Parsed[String] = Parsed.singleValue(request.headers, paramName)
def headerManyValues(paramName: String, required: Boolean): Parsed[List[String]] = Parsed.manyValues(request.headers, paramName, required)
def bodyAsString = new String(request.readAllBytes(), "UTF-8")
def pathValue(index: Int, paramName: String, required : Boolean): Parsed[String] = {
request
.remainingPathSegments
.lift(index) match {
case Some(value) => Right(value)
case None if required => Left(s"'$paramName'' is a required path parameter at path position $index")
case None => Right("")
}
}
}

View File

@@ -0,0 +1,55 @@
/**
* 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 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
) {
def asJson: String = asData.asJson
def asData : ApiResponseData = {
ApiResponseData(
code = code.getOrElse(0),
`type` = `type`.getOrElse(""),
message = message.getOrElse("")
)
}
}
object ApiResponse{
given RW[ApiResponse] = ApiResponseData.readWriter.bimap[ApiResponse](_.asData, _.asModel)
enum Fields(fieldName : String) extends Field(fieldName) {
case code extends Fields("code")
case `type` extends Fields("`type`")
case message extends Fields("message")
}
}

View File

@@ -0,0 +1,171 @@
/**
* 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 modelData.mustache
package sample.cask.model
import scala.util.control.NonFatal
import scala.util.*
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** 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(
code: Int = 0 ,
`type`: String = "" ,
message: String = ""
) {
def asJson: String = write(this)
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
// ==================
// code
// ==================
// `type`
// ==================
// message
errors.toSeq
}
def validated(failFast : Boolean = false) : scala.util.Try[ApiResponse] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
case first +: theRest => Failure(ValidationErrors(first, theRest))
}
}
/** use 'validated' to check validation */
def asModel : ApiResponse = {
ApiResponse(
code = Option(
code
)
,
`type` = Option(
`type`
)
,
message = Option(
message
)
)
}
}
object ApiResponseData {
given readWriter : RW[ApiResponseData] = macroRW
def fromJsonString(jason : String) : ApiResponseData = try {
read[ApiResponseData](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
}
def manyFromJsonString(jason : String) : Seq[ApiResponseData] = try {
read[List[ApiResponseData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as list: $e")
}
def manyFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Seq[ApiResponse]] = {
Try(manyFromJsonString(jason)).flatMap { list =>
list.zipWithIndex.foldLeft(Try(Vector[ApiResponse]())) {
case (Success(list), (next, i)) =>
next.validated(failFast) match {
case Success(ok) => Success(list :+ ok)
case Failure(err) => Failure(new Exception(s"Validation error on element $i: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
def mapFromJsonString(jason : String) : Map[String, ApiResponseData] = try {
read[Map[String, ApiResponseData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as map: $e")
}
def mapFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Map[String, ApiResponse]] = {
Try(mapFromJsonString(jason)).flatMap { map =>
map.foldLeft(Try(Map[String, ApiResponse]())) {
case (Success(map), (key, next)) =>
next.validated(failFast) match {
case Success(ok) => Success(map.updated(key, ok))
case Failure(err) => Failure(new Exception(s"Validation error on element $key: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
}

View File

@@ -0,0 +1,51 @@
/**
* 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 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
) {
def asJson: String = asData.asJson
def asData : CategoryData = {
CategoryData(
id = id.getOrElse(0),
name = name.getOrElse("")
)
}
}
object Category{
given RW[Category] = CategoryData.readWriter.bimap[Category](_.asData, _.asModel)
enum Fields(fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case name extends Fields("name")
}
}

View File

@@ -0,0 +1,153 @@
/**
* 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 modelData.mustache
package sample.cask.model
import scala.util.control.NonFatal
import scala.util.*
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** 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(
id: Long = 0 ,
name: String = ""
) {
def asJson: String = write(this)
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
// ==================
// id
// ==================
// name
// validate against pattern '^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$'
if (errors.isEmpty || !failFast) {
val regex = """^[a-zA-Z0-9]+[a-zA-Z0-9\\.\\-_]*[a-zA-Z0-9]+$"""
if name == null || !regex.r.matches(name) then
errors += ValidationError(path :+ Category.Fields.name, s"value '$name' doesn't match pattern $regex")
}
errors.toSeq
}
def validated(failFast : Boolean = false) : scala.util.Try[Category] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
case first +: theRest => Failure(ValidationErrors(first, theRest))
}
}
/** use 'validated' to check validation */
def asModel : Category = {
Category(
id = Option(
id
)
,
name = Option(
name
)
)
}
}
object CategoryData {
given readWriter : RW[CategoryData] = macroRW
def fromJsonString(jason : String) : CategoryData = try {
read[CategoryData](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
}
def manyFromJsonString(jason : String) : Seq[CategoryData] = try {
read[List[CategoryData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as list: $e")
}
def manyFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Seq[Category]] = {
Try(manyFromJsonString(jason)).flatMap { list =>
list.zipWithIndex.foldLeft(Try(Vector[Category]())) {
case (Success(list), (next, i)) =>
next.validated(failFast) match {
case Success(ok) => Success(list :+ ok)
case Failure(err) => Failure(new Exception(s"Validation error on element $i: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
def mapFromJsonString(jason : String) : Map[String, CategoryData] = try {
read[Map[String, CategoryData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as map: $e")
}
def mapFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Map[String, Category]] = {
Try(mapFromJsonString(jason)).flatMap { map =>
map.foldLeft(Try(Map[String, Category]())) {
case (Success(map), (key, next)) =>
next.validated(failFast) match {
case Success(ok) => Success(map.updated(key, ok))
case Failure(err) => Failure(new Exception(s"Validation error on element $key: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
}

View File

@@ -0,0 +1,76 @@
/**
* 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 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
) {
def asJson: String = 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)
)
}
}
object Order{
given RW[Order] = OrderData.readWriter.bimap[Order](_.asData, _.asModel)
enum Fields(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
enum StatusEnum derives ReadWriter {
case placed
case approved
case delivered
}
}

View File

@@ -0,0 +1,245 @@
/**
* 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 modelData.mustache
package sample.cask.model
import java.time.OffsetDateTime
import scala.util.control.NonFatal
import scala.util.*
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** 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(
id: Long = 0 ,
petId: Long = 0 ,
quantity: Int = 0 ,
shipDate: OffsetDateTime = null ,
/* Order Status */
status: Order.StatusEnum = null ,
complete: Boolean = false
) {
def asJson: String = write(this)
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
// ==================
// id
// ==================
// petId
// ==================
// quantity
// ==================
// shipDate
// ==================
// status
// ==================
// complete
errors.toSeq
}
def validated(failFast : Boolean = false) : scala.util.Try[Order] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
case first +: theRest => Failure(ValidationErrors(first, theRest))
}
}
/** 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
)
)
}
}
object OrderData {
given readWriter : RW[OrderData] = macroRW
def fromJsonString(jason : String) : OrderData = try {
read[OrderData](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
}
def manyFromJsonString(jason : String) : Seq[OrderData] = try {
read[List[OrderData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as list: $e")
}
def manyFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Seq[Order]] = {
Try(manyFromJsonString(jason)).flatMap { list =>
list.zipWithIndex.foldLeft(Try(Vector[Order]())) {
case (Success(list), (next, i)) =>
next.validated(failFast) match {
case Success(ok) => Success(list :+ ok)
case Failure(err) => Failure(new Exception(s"Validation error on element $i: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
def mapFromJsonString(jason : String) : Map[String, OrderData] = try {
read[Map[String, OrderData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as map: $e")
}
def mapFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Map[String, Order]] = {
Try(mapFromJsonString(jason)).flatMap { map =>
map.foldLeft(Try(Map[String, Order]())) {
case (Success(map), (key, next)) =>
next.validated(failFast) match {
case Success(ok) => Success(map.updated(key, ok))
case Failure(err) => Failure(new Exception(s"Validation error on element $key: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
}

View File

@@ -0,0 +1,77 @@
/**
* 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 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
) {
def asJson: String = 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)
)
}
}
object Pet{
given RW[Pet] = PetData.readWriter.bimap[Pet](_.asData, _.asModel)
enum Fields(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
enum StatusEnum derives ReadWriter {
case available
case pending
case sold
}
}

View File

@@ -0,0 +1,262 @@
/**
* 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 modelData.mustache
package sample.cask.model
import sample.cask.model.Category
import sample.cask.model.Tag
import scala.util.control.NonFatal
import scala.util.*
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** 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(
id: Long = 0 ,
category: CategoryData = null ,
name: String,
photoUrls: Seq[String],
tags: Seq[TagData] = Nil ,
/* pet status in the store */
status: Pet.StatusEnum = null
) {
def asJson: String = write(this)
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
// ==================
// id
// ==================
// category
// validating category
if (errors.isEmpty || !failFast) {
if category != null then errors ++= category.validationErrors(path :+ Pet.Fields.category, failFast)
}
// ==================
// name
// ==================
// photoUrls
// ==================
// tags
if (errors.isEmpty || !failFast) {
if (tags != null) {
tags.zipWithIndex.foreach {
case (value, i) if errors.isEmpty || !failFast =>
errors ++= value.validationErrors(
path :+ Pet.Fields.tags :+ Field(i.toString),
failFast)
case (value, i) =>
}
}
}
// ==================
// status
errors.toSeq
}
def validated(failFast : Boolean = false) : scala.util.Try[Pet] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
case first +: theRest => Failure(ValidationErrors(first, theRest))
}
}
/** 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
)
)
}
}
object PetData {
given readWriter : RW[PetData] = macroRW
def fromJsonString(jason : String) : PetData = try {
read[PetData](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
}
def manyFromJsonString(jason : String) : Seq[PetData] = try {
read[List[PetData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as list: $e")
}
def manyFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Seq[Pet]] = {
Try(manyFromJsonString(jason)).flatMap { list =>
list.zipWithIndex.foldLeft(Try(Vector[Pet]())) {
case (Success(list), (next, i)) =>
next.validated(failFast) match {
case Success(ok) => Success(list :+ ok)
case Failure(err) => Failure(new Exception(s"Validation error on element $i: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
def mapFromJsonString(jason : String) : Map[String, PetData] = try {
read[Map[String, PetData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as map: $e")
}
def mapFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Map[String, Pet]] = {
Try(mapFromJsonString(jason)).flatMap { map =>
map.foldLeft(Try(Map[String, Pet]())) {
case (Success(map), (key, next)) =>
next.validated(failFast) match {
case Success(ok) => Success(map.updated(key, ok))
case Failure(err) => Failure(new Exception(s"Validation error on element $key: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
}

View File

@@ -0,0 +1,51 @@
/**
* 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 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
) {
def asJson: String = asData.asJson
def asData : TagData = {
TagData(
id = id.getOrElse(0),
name = name.getOrElse("")
)
}
}
object Tag{
given RW[Tag] = TagData.readWriter.bimap[Tag](_.asData, _.asModel)
enum Fields(fieldName : String) extends Field(fieldName) {
case id extends Fields("id")
case name extends Fields("name")
}
}

View File

@@ -0,0 +1,147 @@
/**
* 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 modelData.mustache
package sample.cask.model
import scala.util.control.NonFatal
import scala.util.*
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** 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(
id: Long = 0 ,
name: String = ""
) {
def asJson: String = write(this)
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
// ==================
// id
// ==================
// name
errors.toSeq
}
def validated(failFast : Boolean = false) : scala.util.Try[Tag] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
case first +: theRest => Failure(ValidationErrors(first, theRest))
}
}
/** use 'validated' to check validation */
def asModel : Tag = {
Tag(
id = Option(
id
)
,
name = Option(
name
)
)
}
}
object TagData {
given readWriter : RW[TagData] = macroRW
def fromJsonString(jason : String) : TagData = try {
read[TagData](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
}
def manyFromJsonString(jason : String) : Seq[TagData] = try {
read[List[TagData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as list: $e")
}
def manyFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Seq[Tag]] = {
Try(manyFromJsonString(jason)).flatMap { list =>
list.zipWithIndex.foldLeft(Try(Vector[Tag]())) {
case (Success(list), (next, i)) =>
next.validated(failFast) match {
case Success(ok) => Success(list :+ ok)
case Failure(err) => Failure(new Exception(s"Validation error on element $i: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
def mapFromJsonString(jason : String) : Map[String, TagData] = try {
read[Map[String, TagData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as map: $e")
}
def mapFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Map[String, Tag]] = {
Try(mapFromJsonString(jason)).flatMap { map =>
map.foldLeft(Try(Map[String, Tag]())) {
case (Success(map), (key, next)) =>
next.validated(failFast) match {
case Success(ok) => Success(map.updated(key, ok))
case Failure(err) => Failure(new Exception(s"Validation error on element $key: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
}

View File

@@ -0,0 +1,76 @@
/**
* 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 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
) {
def asJson: String = 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)
)
}
}
object User{
given RW[User] = UserData.readWriter.bimap[User](_.asData, _.asModel)
enum Fields(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

@@ -0,0 +1,292 @@
/**
* 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 modelData.mustache
package sample.cask.model
import scala.util.control.NonFatal
import scala.util.*
// see https://com-lihaoyi.github.io/upickle/
import upickle.default.{ReadWriter => RW, macroRW}
import upickle.default.*
/** 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(
id: Long = 0 ,
username: String = "" ,
firstName: String = "" ,
lastName: String = "" ,
email: String = "" ,
password: String = "" ,
phone: String = "" ,
/* User Status */
userStatus: Int = 0
) {
def asJson: String = write(this)
def validationErrors(path : Seq[Field], failFast : Boolean) : Seq[ValidationError] = {
val errors = scala.collection.mutable.ListBuffer[ValidationError]()
// ==================
// id
// ==================
// username
// ==================
// firstName
// ==================
// lastName
// ==================
// email
// ==================
// password
// ==================
// phone
// ==================
// userStatus
errors.toSeq
}
def validated(failFast : Boolean = false) : scala.util.Try[User] = {
validationErrors(Vector(), failFast) match {
case Seq() => Success(asModel)
case first +: theRest => Failure(ValidationErrors(first, theRest))
}
}
/** 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
)
)
}
}
object UserData {
given readWriter : RW[UserData] = macroRW
def fromJsonString(jason : String) : UserData = try {
read[UserData](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason': $e")
}
def manyFromJsonString(jason : String) : Seq[UserData] = try {
read[List[UserData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as list: $e")
}
def manyFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Seq[User]] = {
Try(manyFromJsonString(jason)).flatMap { list =>
list.zipWithIndex.foldLeft(Try(Vector[User]())) {
case (Success(list), (next, i)) =>
next.validated(failFast) match {
case Success(ok) => Success(list :+ ok)
case Failure(err) => Failure(new Exception(s"Validation error on element $i: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
def mapFromJsonString(jason : String) : Map[String, UserData] = try {
read[Map[String, UserData]](jason)
} catch {
case NonFatal(e) => sys.error(s"Error parsing json '$jason' as map: $e")
}
def mapFromJsonStringValidated(jason : String, failFast : Boolean = false) : Try[Map[String, User]] = {
Try(mapFromJsonString(jason)).flatMap { map =>
map.foldLeft(Try(Map[String, User]())) {
case (Success(map), (key, next)) =>
next.validated(failFast) match {
case Success(ok) => Success(map.updated(key, ok))
case Failure(err) => Failure(new Exception(s"Validation error on element $key: ${err.getMessage}", err))
}
case (fail, _) => fail
}
}
}
}

View File

@@ -0,0 +1,65 @@
/**
* 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
*/
package sample.cask.model
// model package
import upickle.default._
import java.time.*
import java.time.format.DateTimeFormatter
/**
* This base class lets us refer to fields in exceptions
*/
class Field(val name : String)
final case class ValidationErrors(
first: ValidationError,
remaining: Seq[ValidationError],
message: String
) extends Exception(message)
object ValidationErrors {
def apply(first: ValidationError, remaining: Seq[ValidationError]) = {
val noun = if remaining.isEmpty then "error" else "errors"
new ValidationErrors(
first,
remaining,
remaining.mkString(s"${remaining.size + 1} $noun found: ${first}", "\n\t", "")
)
}
}
final case class ValidationError(path : Seq[Field], message : String) extends Exception(message) {
override def toString = s"ValidationError for ${path.mkString(".")}: $message"
}
given ReadWriter[ZonedDateTime] = readwriter[String].bimap[ZonedDateTime](
zonedDateTime => DateTimeFormatter.ISO_INSTANT.format(zonedDateTime),
str => ZonedDateTime.parse(str, DateTimeFormatter.ISO_INSTANT))
given ReadWriter[LocalDateTime] = readwriter[String].bimap[LocalDateTime](
zonedDateTime => DateTimeFormatter.ISO_INSTANT.format(zonedDateTime),
str => LocalDateTime.parse(str, DateTimeFormatter.ISO_INSTANT))
given ReadWriter[LocalDate] = readwriter[String].bimap[LocalDate](
zonedDateTime => DateTimeFormatter.ISO_INSTANT.format(zonedDateTime),
str => LocalDate.parse(str, DateTimeFormatter.ISO_INSTANT))
given ReadWriter[OffsetDateTime] = readwriter[String].bimap[OffsetDateTime](
zonedDateTime => DateTimeFormatter.ISO_INSTANT.format(zonedDateTime),
str => scala.util.Try(OffsetDateTime.parse(str, DateTimeFormatter.ISO_DATE_TIME)).getOrElse(
OffsetDateTime.parse(str, DateTimeFormatter.ISO_INSTANT)
)
)

View File

@@ -0,0 +1,24 @@
/**
* 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
*/
package cask.groupId.server
def box(str: String): String = {
val lines = str.linesIterator.toList
val maxLen = (0 +: lines.map(_.length)).max
val boxed = lines.map { line =>
s" | ${line.padTo(maxLen, ' ')} |"
}
val bar = " +-" + ("-" * maxLen) + "-+"
(bar +: boxed :+ bar).mkString("\n")
}

View File

@@ -0,0 +1,33 @@
/**
* 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 the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
*/
// this model was generated using modelTest.mustache
package sample.cask.model
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
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")
}
}
}

View File

@@ -0,0 +1,33 @@
/**
* 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 the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
*/
// this model was generated using modelTest.mustache
package sample.cask.model
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
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")
}
}
}

View File

@@ -0,0 +1,34 @@
/**
* 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 the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
*/
// this model was generated using modelTest.mustache
package sample.cask.model
import java.time.OffsetDateTime
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
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")
}
}
}

View File

@@ -0,0 +1,35 @@
/**
* 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 the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
*/
// 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
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")
}
}
}

View File

@@ -0,0 +1,33 @@
/**
* 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 the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
*/
// this model was generated using modelTest.mustache
package sample.cask.model
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
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")
}
}
}

View File

@@ -0,0 +1,33 @@
/**
* 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 the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
*/
// this model was generated using modelTest.mustache
package sample.cask.model
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
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")
}
}
}