[Scala][Finch] Adding security definitions to Finch (#6891)

* #6818: support for security definitions and some refactoring

* adding support for Either

* adding dates with TimeZone (date-time) and Local (date)

* cleanup

* generating code
This commit is contained in:
Erik Janssen 2017-11-27 09:38:12 +01:00 committed by William Cheng
parent 5d59dd10ec
commit 1b90a05754
20 changed files with 560 additions and 292 deletions

View File

@ -1,6 +1,5 @@
package io.swagger.codegen.languages;
import com.google.common.base.Strings;
import io.swagger.codegen.*;
import io.swagger.models.Model;
import io.swagger.models.properties.ArrayProperty;
@ -81,7 +80,7 @@ public class FinchServerCodegen extends DefaultCodegen implements CodegenConfig
typeMapping.put("long", "Long");
typeMapping.put("double", "Double");
typeMapping.put("number", "BigDecimal");
typeMapping.put("date-time", "LocalDateTime");
typeMapping.put("date-time", "ZonedDateTime");
typeMapping.put("date", "LocalDateTime");
typeMapping.put("file", "File");
typeMapping.put("array", "Seq");
@ -90,7 +89,7 @@ public class FinchServerCodegen extends DefaultCodegen implements CodegenConfig
typeMapping.put("object", "Object");
typeMapping.put("binary", "Array[Byte]");
typeMapping.put("Date", "LocalDateTime");
typeMapping.put("DateTime", "LocalDateTime");
typeMapping.put("DateTime", "ZonedDateTime");
additionalProperties.put("modelPackage", modelPackage());
additionalProperties.put("apiPackage", apiPackage());
@ -154,6 +153,7 @@ public class FinchServerCodegen extends DefaultCodegen implements CodegenConfig
importMapping.put("LocalDateTime", "java.time.LocalDateTime");
importMapping.put("LocalDate", "java.time.LocalDate");
importMapping.put("LocalTime", "java.time.LocalTime");
importMapping.put("ZonedDateTime", "java.time.ZonedDateTime");
cliOptions.clear();
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Finch package name (e.g. io.swagger).")
@ -206,81 +206,31 @@ public class FinchServerCodegen extends DefaultCodegen implements CodegenConfig
return codegenModel;
}
@Override
public Map<String, Object> postProcessOperations(Map<String, Object> objs) {
Map<String, Object> operations = (Map<String, Object>) objs.get("operations");
List<CodegenOperation> operationList = (List<CodegenOperation>) operations.get("operation");
for (CodegenOperation op : operationList) {
op.httpMethod = op.httpMethod.toLowerCase();
String path = new String(op.path);
// remove first /
if (path.startsWith("/")) {
path = path.substring(1);
}
// remove last /
if (path.endsWith("/")) {
path = path.substring(0, path.length()-1);
}
// Converts GET /foo/bar => get("foo" :: "bar")
generateScalaPath(op);
String[] items = path.split("/", -1);
String scalaPath = "";
int pathParamIndex = 0;
// Generates e.g. uuid :: header("boo") :: params("baa") under key "x-codegen-pathParams"
// Generates e.g. (id: UUID, headerBoo: String, paramBaa: String) under key "x-codegen-typedInputParams"
// Generates e.g. (id, headerBoo, paramBaa) under key "x-codegen-inputParams"
generateInputParameters(op);
for (int i = 0; i < items.length; ++i) {
if (items[i].matches("^\\{(.*)\\}$")) { // wrap in {}
// find the datatype of the parameter
final CodegenParameter cp = op.pathParams.get(pathParamIndex);
//Generate Auth parameters using security: definition
//Results in header("apiKey") or param("apiKey")
authParameters(op);
// TODO: Handle non-primitives
scalaPath = scalaPath + cp.dataType.toLowerCase();
pathParamIndex++;
} else {
scalaPath = scalaPath + "\"" + items[i] + "\"";
}
if (i != items.length -1) {
scalaPath = scalaPath + " :: ";
}
}
for (CodegenParameter p : op.allParams) {
// TODO: This hacky, should be converted to mappings if possible to keep it clean.
// This could also be done using template imports
if(p.isPathParam && p.isPrimitiveType) {
p.vendorExtensions.put("x-codegen-normalized-path-type", p.dataType.toLowerCase());
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType);
} else if(p.isHeaderParam) {
if(p.required) {
p.vendorExtensions.put("x-codegen-normalized-path-type", "header(\"" + p.baseName + "\")");
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType);
} else {
p.vendorExtensions.put("x-codegen-normalized-path-type", "headerOption(\"" + p.baseName + "\")");
p.vendorExtensions.put("x-codegen-normalized-input-type", "Option["+ p.dataType + "]");
}
} else if(p.isQueryParam) {
if(p.isContainer || p.isListContainer) {
p.vendorExtensions.put("x-codegen-normalized-path-type", "params(\"" + p.baseName + "\")");
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType.replaceAll("^[^\\[]+", "Seq"));
} else {
p.vendorExtensions.put("x-codegen-normalized-path-type", "param(\"" + p.baseName + "\")");
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType);
}
} else if(p.isBodyParam) {
p.vendorExtensions.put("x-codegen-normalized-path-type", "jsonBody["+ p.dataType + "]");
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType);
} else if(p.isFile) {
p.vendorExtensions.put("x-codegen-normalized-path-type", "fileUpload(\""+ p.baseName + "\")");
p.vendorExtensions.put("x-codegen-normalized-input-type", "FileUpload");
} else {
p.vendorExtensions.put("x-codegen-normalized-path-type", p.dataType);
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType);
}
}
op.vendorExtensions.put("x-codegen-path", scalaPath);
//Concatenates all parameters
concatParameters(op);
}
return objs;
}
@ -330,4 +280,172 @@ public class FinchServerCodegen extends DefaultCodegen implements CodegenConfig
this.packageName = packageName;
}
/**
*
* @param prim
* @param isRequired
* @param canBeOptional
* @return
*/
private String toPrimitive(String prim, Boolean isRequired, Boolean canBeOptional) {
String converter = ".map(_.to" + prim + ")";
return (canBeOptional ? (isRequired ? converter : ".map(_" + converter +")") : "");
}
//All path parameters are String initially, for primitives these need to be converted
private String toPathParameter(CodegenParameter p, String paramType, Boolean canBeOptional ) {
Boolean isNotAString = !p.dataType.equals("String");
return paramType + (canBeOptional && !p.required ? "Option" : "") + "(\""+ p.baseName + "\")" + (isNotAString ? toPrimitive(p.dataType,p.required,canBeOptional) : "") ;
}
private String toInputParameter(CodegenParameter p){
return (p.required ? "" : "Option[")+p.dataType+(p.required ? "" : "]");
}
private String concat(String original, String addition, String op) {
return original + (original.isEmpty() ? "" : (addition.isEmpty() ? "" : op)) + addition;
}
// a, b
private String csvConcat(String original, String addition) {
return concat(original, addition,", ");
}
// a :: b
private String colConcat(String original, String addition) {
return concat(original, addition," :: ");
}
private void authParameters(CodegenOperation op) {
String authParams = "";
String authInputParams = "";
String typedAuthInputParams = "";
//Append apikey security to path params and create input parameters for functions
if(op.authMethods != null){
for(CodegenSecurity s : op.authMethods) {
if(s.isApiKey && s.isKeyInHeader){
authParams = colConcat(authParams, "header(\""+ s.keyParamName + "\")");
} else if(s.isApiKey && s.isKeyInQuery){
authParams = colConcat(authParams, "param(\""+ s.keyParamName + "\")");
}
if(s.isApiKey) {
typedAuthInputParams = csvConcat(typedAuthInputParams, "authParam"+ s.name + ": String");
authInputParams = csvConcat(authInputParams,"authParam"+ s.name);
}
}
}
op.vendorExtensions.put("x-codegen-authParams", authParams);
op.vendorExtensions.put("x-codegen-authInputParams", authInputParams);
op.vendorExtensions.put("x-codegen-typedAuthInputParams", typedAuthInputParams);
}
private void generateScalaPath(CodegenOperation op) {
op.httpMethod = op.httpMethod.toLowerCase();
String path = new String(op.path);
// remove first /
if (path.startsWith("/")) {
path = path.substring(1);
}
// remove last /
if (path.endsWith("/")) {
path = path.substring(0, path.length()-1);
}
String[] items = path.split("/", -1);
String scalaPath = "";
Integer pathParamIndex = 0;
for (int i = 0; i < items.length; ++i) {
if (items[i].matches("^\\{(.*)\\}$")) { // wrap in {}
// find the datatype of the parameter
final CodegenParameter cp = op.pathParams.get(pathParamIndex);
// TODO: Handle non-primitives
scalaPath = colConcat(scalaPath, cp.dataType.toLowerCase());
pathParamIndex++;
} else {
scalaPath = colConcat(scalaPath, "\"" + items[i] + "\"");
}
}
op.vendorExtensions.put("x-codegen-path", scalaPath);
}
private void concatParameters(CodegenOperation op) {
String path = colConcat(colConcat(op.vendorExtensions.get("x-codegen-path").toString(),op.vendorExtensions.get("x-codegen-pathParams").toString()), op.vendorExtensions.get("x-codegen-authParams").toString());
String parameters = csvConcat(op.vendorExtensions.get("x-codegen-inputParams").toString(), op.vendorExtensions.get("x-codegen-authInputParams").toString());
String typedParameters = csvConcat(op.vendorExtensions.get("x-codegen-typedInputParams").toString(), op.vendorExtensions.get("x-codegen-typedAuthInputParams").toString());
// The input parameters for functions
op.vendorExtensions.put("x-codegen-paths",path);
op.vendorExtensions.put("x-codegen-params", parameters);
op.vendorExtensions.put("x-codegen-typedParams", typedParameters);
}
private void generateInputParameters(CodegenOperation op) {
String inputParams = "";
String typedInputParams = "";
String pathParams = "";
for (CodegenParameter p : op.allParams) {
// TODO: This hacky, should be converted to mappings if possible to keep it clean.
// This could also be done using template imports
if(p.isBodyParam) {
p.vendorExtensions.put("x-codegen-normalized-path-type", "jsonBody["+ p.dataType + "]");
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType);
} else if(p.isContainer || p.isListContainer) {
p.vendorExtensions.put("x-codegen-normalized-path-type", toPathParameter(p,"params", false));
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType.replaceAll("^[^\\[]+", "Seq"));
} else if(p.isQueryParam) {
p.vendorExtensions.put("x-codegen-normalized-path-type", toPathParameter(p, "param",true));
p.vendorExtensions.put("x-codegen-normalized-input-type", toInputParameter(p));
} else if(p.isHeaderParam) {
p.vendorExtensions.put("x-codegen-normalized-path-type", toPathParameter(p,"header", true));
p.vendorExtensions.put("x-codegen-normalized-input-type", toInputParameter(p));
} else if(p.isFile) {
p.vendorExtensions.put("x-codegen-normalized-path-type", "fileUpload(\""+ p.paramName + "\")");
p.vendorExtensions.put("x-codegen-normalized-input-type", "FileUpload");
} else if(p.isPrimitiveType && !p.isPathParam) {
p.vendorExtensions.put("x-codegen-normalized-path-type", p.dataType.toLowerCase());
p.vendorExtensions.put("x-codegen-normalized-input-type", toInputParameter(p));
} else {
//Path paremeters are handled in generateScalaPath()
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType);
}
if(p.vendorExtensions.get("x-codegen-normalized-path-type") != null){
pathParams = colConcat(pathParams , p.vendorExtensions.get("x-codegen-normalized-path-type").toString());
}
inputParams = csvConcat(inputParams, p.paramName);
typedInputParams = csvConcat(typedInputParams , p.paramName + ": " + p.vendorExtensions.get("x-codegen-normalized-input-type"));
}
// All body, path, query and header parameters
op.vendorExtensions.put("x-codegen-pathParams", pathParams);
// The input parameters for functions
op.vendorExtensions.put("x-codegen-inputParams", inputParams);
op.vendorExtensions.put("x-codegen-typedInputParams", typedInputParams);
}
}

View File

@ -2,7 +2,9 @@ package {{packageName}}
// TODO: properly handle custom imports
import java.io._
import java.util.Date
import java.util.UUID
import java.time._
import {{modelPackage}}._
@ -18,7 +20,7 @@ trait DataAccessor {
* {{{description}}}
* @return A {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}
*/
def {{baseName}}_{{operationId}}({{#allParams}}{{paramName}}: {{{dataType}}}{{^-last}}, {{/-last}}{{/allParams}}): {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} = ???
def {{baseName}}_{{operationId}}({{{vendorExtensions.x-codegen-typedParams}}}): Either[CommonError,{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}] = ???
{{/operation}}
{{/operations}}

View File

@ -5,6 +5,11 @@ This server was generated by the [swagger-codegen](https://github.com/swagger-ap
[OpenAPI-Spec](https://github.com/swagger-api/swagger-core/wiki) from a remote server, you can easily generate a server stub. This
is an example of building a swagger-enabled scalatra server.
This example uses the [scalatra](http://scalatra.org/) framework. To see how to make this your own, look here:
This example uses the [finch](http://github.com/finagle/finch/) framework. To see how to make this your own, look here:
[README](https://github.com/swagger-api/swagger-codegen/tree/master/samples/server-generator/scalatra)
[README](https://github.com/swagger-api/swagger-codegen/tree/master/samples/server-generator/finch)
### After generation
Run `scalafix RemoveUnusedImports` to cleanup unused imports.

View File

@ -17,10 +17,8 @@ import com.twitter.util.{Await, Future}
class Server {
// Loads implementation defined in resources/META-INF/services/{{packageName}}.DataAccessor
val db = LoadService[DataAccessor]() match {
case accessor :: _ => accessor
case _ => new DataAccessor { }
}
val impls: Seq[DataAccessor] = LoadService[DataAccessor]()
val db = if (impls.isEmpty) new DataAccessor { } else impls.head
val service = endpoint.makeService(db)
@ -32,7 +30,7 @@ class Server {
}
/**
* Launches the PetstoreAPI service when the system is ready.
* Launches the API service when the system is ready.
*/
object Server extends Server with App {
Await.ready(server)

View File

@ -1,7 +1,6 @@
package {{apiPackage}}
import java.io._
import java.util.Date
import {{packageName}}._
import {{modelPackage}}._
{{#imports}}import {{import}}
@ -17,6 +16,7 @@ import com.twitter.util.Future
import com.twitter.io.Buf
import io.finch._, items._
import java.io.File
import java.time._
object {{classname}} {
/**
@ -26,25 +26,43 @@ object {{classname}} {
def endpoints(da: DataAccessor) =
{{#operations}}
{{#operation}}
{{{operationId}}}(da){{^-last}} :+:{{/-last}}
{{{operationId}}}(da){{^-last}} :+:{{/-last}}
{{/operation}}
{{/operations}}
private def checkError(e: CommonError) = e match {
case InvalidInput(_) => BadRequest(e)
case MissingIdentifier(_) => BadRequest(e)
case RecordNotFound(_) => NotFound(e)
case _ => InternalServerError(e)
}
implicit class StringOps(s: String) {
import java.time.format.DateTimeFormatter
lazy val localformatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
lazy val datetimeformatter: DateTimeFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
def toLocalDateTime: LocalDateTime = LocalDateTime.parse(s,localformatter)
def toZonedDateTime: ZonedDateTime = ZonedDateTime.parse(s, datetimeformatter)
}
{{#operations}}
{{#operation}}
/**
* {{{description}}}
* @return And endpoint representing a {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}
* @return An endpoint representing a {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}
*/
private def {{operationId}}(da: DataAccessor): Endpoint[{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}] =
{{httpMethod}}({{{vendorExtensions.x-codegen-path}}} {{#allParams}}{{^isPathParam}} :: {{& vendorExtensions.x-codegen-normalized-path-type}}{{/isPathParam}}{{/allParams}}) { {{#hasParams}}({{#allParams}}{{paramName}}: {{{vendorExtensions.x-codegen-normalized-input-type}}}{{^-last}}, {{/-last}}{{/allParams}}) => {{/hasParams}}
{{#returnType}}
Ok(da.{{baseName}}_{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}))
{{/returnType}}
{{^returnType}}
da.{{baseName}}_{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}})
NoContent[Unit]
{{/returnType}}
{{httpMethod}}({{{vendorExtensions.x-codegen-paths}}}) { {{#hasParams}}({{{vendorExtensions.x-codegen-typedParams}}}) => {{/hasParams}}
da.{{baseName}}_{{operationId}}({{{vendorExtensions.x-codegen-params}}}) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
@ -53,20 +71,20 @@ object {{classname}} {
{{/operations}}
implicit private def fileUploadToFile(fileUpload: FileUpload) : File = {
fileUpload match {
case upload: InMemoryFileUpload =>
bytesToFile(Buf.ByteArray.Owned.extract(upload.content))
case upload: OnDiskFileUpload =>
upload.content
case _ => null
}
fileUpload match {
case upload: InMemoryFileUpload =>
bytesToFile(Buf.ByteArray.Owned.extract(upload.content))
case upload: OnDiskFileUpload =>
upload.content
case _ => null
}
}
private def bytesToFile(input: Array[Byte]): java.io.File = {
val file = File.createTempFile("tmp{{classname}}", null)
val output = new FileOutputStream(file)
output.write(input)
file
val file = File.createTempFile("tmp{{classname}}", null)
val output = new FileOutputStream(file)
output.write(input)
file
}
// This assists in params(string) application (which must be Seq[A] in parameter list) when the param is used as a List[A] elsewhere.

View File

@ -6,7 +6,7 @@ name := "finch-sample"
version := "0.1.0-SNAPSHOT"
scalaVersion := "2.11.8"
scalaVersion := "2.11.11"
resolvers += Resolver.sonatypeRepo("snapshots")
@ -20,6 +20,11 @@ resolvers += "Sonatype OSS Releases" at "http://oss.sonatype.org/content/reposit
Defaults.itSettings
lazy val circeVersion = "0.8.0"
lazy val finagleVersion = "6.45.0"
lazy val finchVersion = "0.15.1"
lazy val scalaTestVersion = "3.0.0"
scalacOptions ++= Seq(
"-deprecation",
"-encoding", "UTF-8",
@ -33,21 +38,21 @@ scalacOptions ++= Seq(
"-Ywarn-numeric-widen",
"-Xfuture",
"-Xlint",
// "-Ywarn-unused-import",
"-Ywarn-unused-import",
"-language:postfixOps"
)
lazy val `it-config-sbt-project` = project.in(file(".")).configs(IntegrationTest)
libraryDependencies ++= Seq(
"com.github.finagle" %% "finch-core" % "0.12.0",
"com.github.finagle" %% "finch-circe" % "0.12.0",
"io.circe" %% "circe-generic" % "0.7.0",
"io.circe" %% "circe-java8" % "0.7.0",
"com.twitter" %% "util-core" % "6.40.0",
"com.github.finagle" %% "finch-test" % "0.12.0" % "test",
"org.scalacheck" %% "scalacheck" % "1.13.4" % "test",
"org.scalatest" %% "scalatest" % "3.0.0" % "test"
"com.github.finagle" %% "finch-core" % finchVersion,
"com.github.finagle" %% "finch-circe" % finchVersion,
"io.circe" %% "circe-generic" % circeVersion,
"io.circe" %% "circe-java8" % circeVersion,
"com.twitter" %% "util-core" % finagleVersion,
"com.github.finagle" %% "finch-test" % finchVersion % "test",
"org.scalacheck" %% "scalacheck" % "1.13.4" % "test",
"org.scalatest" %% "scalatest" % scalaTestVersion % "test"
)
assemblyMergeStrategy in assembly := {

View File

@ -23,7 +23,7 @@ object endpoint {
Json.obj("error" -> Json.fromString("something_not_parsed"))
case Error.NotValid(_, _) =>
Json.obj("error" -> Json.fromString("something_not_valid"))
case error: PetstoreError =>
case error: CommonError =>
Json.obj("error" -> Json.fromString(error.message))
}
@ -44,7 +44,7 @@ object endpoint {
{{/apis}}
{{/apiInfo}}
).handle({
case e: PetstoreError => NotFound(e)
case e: CommonError => NotFound(e)
}).toService
}

View File

@ -1,9 +1,9 @@
package {{packageName}}
/**
* The parent error from which most PetstoreAPI errors extend. Thrown whenever something in the api goes wrong.
* The parent error from which most API errors extend. Thrown whenever something in the api goes wrong.
*/
abstract class PetstoreError(msg: String) extends Exception(msg) {
abstract class CommonError(msg: String) extends Exception(msg) {
def message: String
}
@ -11,17 +11,17 @@ abstract class PetstoreError(msg: String) extends Exception(msg) {
* Thrown when the object given is invalid
* @param message An error message
*/
case class InvalidInput(message: String) extends PetstoreError(message)
case class InvalidInput(message: String) extends CommonError(message)
/**
* Thrown when the given object is missing a unique ID.
* @param message An error message
*/
case class MissingIdentifier(message: String) extends PetstoreError(message)
case class MissingIdentifier(message: String) extends CommonError(message)
/**
* Thrown when the given record does not exist in the database.
* @param message An error message
*/
case class RecordNotFound(message: String) extends PetstoreError(message)
case class RecordNotFound(message: String) extends CommonError(message)

View File

@ -2,6 +2,6 @@ resolvers += Resolver.typesafeRepo("releases")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3")
// addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.1.4")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.3")
addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.6.0")

View File

@ -5,6 +5,11 @@ This server was generated by the [swagger-codegen](https://github.com/swagger-ap
[OpenAPI-Spec](https://github.com/swagger-api/swagger-core/wiki) from a remote server, you can easily generate a server stub. This
is an example of building a swagger-enabled scalatra server.
This example uses the [scalatra](http://scalatra.org/) framework. To see how to make this your own, look here:
This example uses the [finch](http://github.com/finagle/finch/) framework. To see how to make this your own, look here:
[README](https://github.com/swagger-api/swagger-codegen/tree/master/samples/server-generator/scalatra)
[README](https://github.com/swagger-api/swagger-codegen/tree/master/samples/server-generator/finch)
### After generation
Run `scalafix RemoveUnusedImports` to cleanup unused imports.

View File

@ -6,7 +6,7 @@ name := "finch-sample"
version := "0.1.0-SNAPSHOT"
scalaVersion := "2.11.8"
scalaVersion := "2.11.11"
resolvers += Resolver.sonatypeRepo("snapshots")
@ -20,6 +20,11 @@ resolvers += "Sonatype OSS Releases" at "http://oss.sonatype.org/content/reposit
Defaults.itSettings
lazy val circeVersion = "0.8.0"
lazy val finagleVersion = "6.45.0"
lazy val finchVersion = "0.15.1"
lazy val scalaTestVersion = "3.0.0"
scalacOptions ++= Seq(
"-deprecation",
"-encoding", "UTF-8",
@ -33,21 +38,21 @@ scalacOptions ++= Seq(
"-Ywarn-numeric-widen",
"-Xfuture",
"-Xlint",
// "-Ywarn-unused-import",
"-Ywarn-unused-import",
"-language:postfixOps"
)
lazy val `it-config-sbt-project` = project.in(file(".")).configs(IntegrationTest)
libraryDependencies ++= Seq(
"com.github.finagle" %% "finch-core" % "0.12.0",
"com.github.finagle" %% "finch-circe" % "0.12.0",
"io.circe" %% "circe-generic" % "0.7.0",
"io.circe" %% "circe-java8" % "0.7.0",
"com.twitter" %% "util-core" % "6.40.0",
"com.github.finagle" %% "finch-test" % "0.12.0" % "test",
"org.scalacheck" %% "scalacheck" % "1.13.4" % "test",
"org.scalatest" %% "scalatest" % "3.0.0" % "test"
"com.github.finagle" %% "finch-core" % finchVersion,
"com.github.finagle" %% "finch-circe" % finchVersion,
"io.circe" %% "circe-generic" % circeVersion,
"io.circe" %% "circe-java8" % circeVersion,
"com.twitter" %% "util-core" % finagleVersion,
"com.github.finagle" %% "finch-test" % finchVersion % "test",
"org.scalacheck" %% "scalacheck" % "1.13.4" % "test",
"org.scalatest" %% "scalatest" % scalaTestVersion % "test"
)
assemblyMergeStrategy in assembly := {

View File

@ -2,6 +2,6 @@ resolvers += Resolver.typesafeRepo("releases")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3")
// addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.1.4")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.3")
addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.6.0")

View File

@ -2,7 +2,9 @@ package io.swagger
// TODO: properly handle custom imports
import java.io._
import java.util.Date
import java.util.UUID
import java.time._
import io.swagger.models._
@ -14,120 +16,120 @@ trait DataAccessor {
*
* @return A Unit
*/
def Pet_addPet(body: Pet): Unit = ???
def Pet_addPet(body: Pet): Either[CommonError,Unit] = ???
/**
*
* @return A Unit
*/
def Pet_deletePet(petId: Long, apiKey: String): Unit = ???
def Pet_deletePet(petId: Long, apiKey: Option[String]): Either[CommonError,Unit] = ???
/**
*
* @return A Seq[Pet]
*/
def Pet_findPetsByStatus(status: Seq[String]): Seq[Pet] = ???
def Pet_findPetsByStatus(status: Seq[String]): Either[CommonError,Seq[Pet]] = ???
/**
*
* @return A Seq[Pet]
*/
def Pet_findPetsByTags(tags: Seq[String]): Seq[Pet] = ???
def Pet_findPetsByTags(tags: Seq[String]): Either[CommonError,Seq[Pet]] = ???
/**
*
* @return A Pet
*/
def Pet_getPetById(petId: Long): Pet = ???
def Pet_getPetById(petId: Long, authParamapi_key: String): Either[CommonError,Pet] = ???
/**
*
* @return A Unit
*/
def Pet_updatePet(body: Pet): Unit = ???
def Pet_updatePet(body: Pet): Either[CommonError,Unit] = ???
/**
*
* @return A Unit
*/
def Pet_updatePetWithForm(petId: Long, name: String, status: String): Unit = ???
def Pet_updatePetWithForm(petId: Long, name: Option[String], status: Option[String]): Either[CommonError,Unit] = ???
/**
*
* @return A ApiResponse
*/
def Pet_uploadFile(petId: Long, additionalMetadata: String, file: File): ApiResponse = ???
def Pet_uploadFile(petId: Long, additionalMetadata: Option[String], file: FileUpload): Either[CommonError,ApiResponse] = ???
/**
*
* @return A Unit
*/
def Store_deleteOrder(orderId: String): Unit = ???
def Store_deleteOrder(orderId: String): Either[CommonError,Unit] = ???
/**
*
* @return A Map[String, Int]
*/
def Store_getInventory(): Map[String, Int] = ???
def Store_getInventory(authParamapi_key: String): Either[CommonError,Map[String, Int]] = ???
/**
*
* @return A Order
*/
def Store_getOrderById(orderId: Long): Order = ???
def Store_getOrderById(orderId: Long): Either[CommonError,Order] = ???
/**
*
* @return A Order
*/
def Store_placeOrder(body: Order): Order = ???
def Store_placeOrder(body: Order): Either[CommonError,Order] = ???
/**
*
* @return A Unit
*/
def User_createUser(body: User): Unit = ???
def User_createUser(body: User): Either[CommonError,Unit] = ???
/**
*
* @return A Unit
*/
def User_createUsersWithArrayInput(body: Seq[User]): Unit = ???
def User_createUsersWithArrayInput(body: Seq[User]): Either[CommonError,Unit] = ???
/**
*
* @return A Unit
*/
def User_createUsersWithListInput(body: Seq[User]): Unit = ???
def User_createUsersWithListInput(body: Seq[User]): Either[CommonError,Unit] = ???
/**
*
* @return A Unit
*/
def User_deleteUser(username: String): Unit = ???
def User_deleteUser(username: String): Either[CommonError,Unit] = ???
/**
*
* @return A User
*/
def User_getUserByName(username: String): User = ???
def User_getUserByName(username: String): Either[CommonError,User] = ???
/**
*
* @return A String
*/
def User_loginUser(username: String, password: String): String = ???
def User_loginUser(username: String, password: String): Either[CommonError,String] = ???
/**
*
* @return A Unit
*/
def User_logoutUser(): Unit = ???
def User_logoutUser(): Either[CommonError,Unit] = ???
/**
*
* @return A Unit
*/
def User_updateUser(username: String, body: User): Unit = ???
def User_updateUser(username: String, body: User): Either[CommonError,Unit] = ???
}

View File

@ -15,10 +15,8 @@ import com.twitter.util.{Await, Future}
class Server {
// Loads implementation defined in resources/META-INF/services/io.swagger.DataAccessor
val db = LoadService[DataAccessor]() match {
case accessor :: _ => accessor
case _ => new DataAccessor { }
}
val impls: Seq[DataAccessor] = LoadService[DataAccessor]()
val db = if (impls.isEmpty) new DataAccessor { } else impls.head
val service = endpoint.makeService(db)
@ -30,7 +28,7 @@ class Server {
}
/**
* Launches the PetstoreAPI service when the system is ready.
* Launches the API service when the system is ready.
*/
object Server extends Server with App {
Await.ready(server)

View File

@ -23,7 +23,7 @@ object endpoint {
Json.obj("error" -> Json.fromString("something_not_parsed"))
case Error.NotValid(_, _) =>
Json.obj("error" -> Json.fromString("something_not_valid"))
case error: PetstoreError =>
case error: CommonError =>
Json.obj("error" -> Json.fromString(error.message))
}
@ -42,7 +42,7 @@ object endpoint {
StoreApi.endpoints(da) :+:
UserApi.endpoints(da)
).handle({
case e: PetstoreError => NotFound(e)
case e: CommonError => NotFound(e)
}).toService
}

View File

@ -1,9 +1,9 @@
package io.swagger
/**
* The parent error from which most PetstoreAPI errors extend. Thrown whenever something in the api goes wrong.
* The parent error from which most API errors extend. Thrown whenever something in the api goes wrong.
*/
abstract class PetstoreError(msg: String) extends Exception(msg) {
abstract class CommonError(msg: String) extends Exception(msg) {
def message: String
}
@ -11,17 +11,17 @@ abstract class PetstoreError(msg: String) extends Exception(msg) {
* Thrown when the object given is invalid
* @param message An error message
*/
case class InvalidInput(message: String) extends PetstoreError(message)
case class InvalidInput(message: String) extends CommonError(message)
/**
* Thrown when the given object is missing a unique ID.
* @param message An error message
*/
case class MissingIdentifier(message: String) extends PetstoreError(message)
case class MissingIdentifier(message: String) extends CommonError(message)
/**
* Thrown when the given record does not exist in the database.
* @param message An error message
*/
case class RecordNotFound(message: String) extends PetstoreError(message)
case class RecordNotFound(message: String) extends CommonError(message)

View File

@ -1,7 +1,6 @@
package io.swagger.apis
import java.io._
import java.util.Date
import io.swagger._
import io.swagger.models._
import io.swagger.models.ApiResponse
@ -18,6 +17,7 @@ import com.twitter.util.Future
import com.twitter.io.Buf
import io.finch._, items._
import java.io.File
import java.time._
object PetApi {
/**
@ -25,123 +25,164 @@ object PetApi {
* @return Bundled compilation of all service endpoints.
*/
def endpoints(da: DataAccessor) =
addPet(da) :+:
deletePet(da) :+:
findPetsByStatus(da) :+:
findPetsByTags(da) :+:
getPetById(da) :+:
updatePet(da) :+:
updatePetWithForm(da) :+:
uploadFile(da)
addPet(da) :+:
deletePet(da) :+:
findPetsByStatus(da) :+:
findPetsByTags(da) :+:
getPetById(da) :+:
updatePet(da) :+:
updatePetWithForm(da) :+:
uploadFile(da)
private def checkError(e: CommonError) = e match {
case InvalidInput(_) => BadRequest(e)
case MissingIdentifier(_) => BadRequest(e)
case RecordNotFound(_) => NotFound(e)
case _ => InternalServerError(e)
}
implicit class StringOps(s: String) {
import java.time.format.DateTimeFormatter
lazy val localformatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
lazy val datetimeformatter: DateTimeFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
def toLocalDateTime: LocalDateTime = LocalDateTime.parse(s,localformatter)
def toZonedDateTime: ZonedDateTime = ZonedDateTime.parse(s, datetimeformatter)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def addPet(da: DataAccessor): Endpoint[Unit] =
post("pet" :: jsonBody[Pet]) { (body: Pet) =>
da.Pet_addPet(body)
NoContent[Unit]
post("pet" :: jsonBody[Pet]) { (body: Pet) =>
da.Pet_addPet(body) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def deletePet(da: DataAccessor): Endpoint[Unit] =
delete("pet" :: long :: headerOption("api_key")) { (petId: Long, apiKey: Option[String]) =>
da.Pet_deletePet(petId, apiKey)
NoContent[Unit]
delete("pet" :: long :: headerOption("api_key")) { (petId: Long, apiKey: Option[String]) =>
da.Pet_deletePet(petId, apiKey) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Seq[Pet]
* @return An endpoint representing a Seq[Pet]
*/
private def findPetsByStatus(da: DataAccessor): Endpoint[Seq[Pet]] =
get("pet" :: "findByStatus" :: params("status")) { (status: Seq[String]) =>
Ok(da.Pet_findPetsByStatus(status))
get("pet" :: "findByStatus" :: params("status")) { (status: Seq[String]) =>
da.Pet_findPetsByStatus(status) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Seq[Pet]
* @return An endpoint representing a Seq[Pet]
*/
private def findPetsByTags(da: DataAccessor): Endpoint[Seq[Pet]] =
get("pet" :: "findByTags" :: params("tags")) { (tags: Seq[String]) =>
Ok(da.Pet_findPetsByTags(tags))
get("pet" :: "findByTags" :: params("tags")) { (tags: Seq[String]) =>
da.Pet_findPetsByTags(tags) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Pet
* @return An endpoint representing a Pet
*/
private def getPetById(da: DataAccessor): Endpoint[Pet] =
get("pet" :: long ) { (petId: Long) =>
Ok(da.Pet_getPetById(petId))
get("pet" :: long :: header("api_key")) { (petId: Long, authParamapi_key: String) =>
da.Pet_getPetById(petId, authParamapi_key) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def updatePet(da: DataAccessor): Endpoint[Unit] =
put("pet" :: jsonBody[Pet]) { (body: Pet) =>
da.Pet_updatePet(body)
NoContent[Unit]
put("pet" :: jsonBody[Pet]) { (body: Pet) =>
da.Pet_updatePet(body) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def updatePetWithForm(da: DataAccessor): Endpoint[Unit] =
post("pet" :: long :: String :: String) { (petId: Long, name: String, status: String) =>
da.Pet_updatePetWithForm(petId, name, status)
NoContent[Unit]
post("pet" :: long :: string :: string) { (petId: Long, name: Option[String], status: Option[String]) =>
da.Pet_updatePetWithForm(petId, name, status) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a ApiResponse
* @return An endpoint representing a ApiResponse
*/
private def uploadFile(da: DataAccessor): Endpoint[ApiResponse] =
post("pet" :: long :: "uploadImage" :: String :: fileUpload("file")) { (petId: Long, additionalMetadata: String, file: FileUpload) =>
Ok(da.Pet_uploadFile(petId, additionalMetadata, file))
post("pet" :: long :: "uploadImage" :: string :: fileUpload("file")) { (petId: Long, additionalMetadata: Option[String], file: FileUpload) =>
da.Pet_uploadFile(petId, additionalMetadata, file) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
implicit private def fileUploadToFile(fileUpload: FileUpload) : File = {
fileUpload match {
case upload: InMemoryFileUpload =>
bytesToFile(Buf.ByteArray.Owned.extract(upload.content))
case upload: OnDiskFileUpload =>
upload.content
case _ => null
}
fileUpload match {
case upload: InMemoryFileUpload =>
bytesToFile(Buf.ByteArray.Owned.extract(upload.content))
case upload: OnDiskFileUpload =>
upload.content
case _ => null
}
}
private def bytesToFile(input: Array[Byte]): java.io.File = {
val file = File.createTempFile("tmpPetApi", null)
val output = new FileOutputStream(file)
output.write(input)
file
val file = File.createTempFile("tmpPetApi", null)
val output = new FileOutputStream(file)
output.write(input)
file
}
// This assists in params(string) application (which must be Seq[A] in parameter list) when the param is used as a List[A] elsewhere.

View File

@ -1,7 +1,6 @@
package io.swagger.apis
import java.io._
import java.util.Date
import io.swagger._
import io.swagger.models._
import io.swagger.models.Order
@ -16,6 +15,7 @@ import com.twitter.util.Future
import com.twitter.io.Buf
import io.finch._, items._
import java.io.File
import java.time._
object StoreApi {
/**
@ -23,72 +23,104 @@ object StoreApi {
* @return Bundled compilation of all service endpoints.
*/
def endpoints(da: DataAccessor) =
deleteOrder(da) :+:
getInventory(da) :+:
getOrderById(da) :+:
placeOrder(da)
deleteOrder(da) :+:
getInventory(da) :+:
getOrderById(da) :+:
placeOrder(da)
private def checkError(e: CommonError) = e match {
case InvalidInput(_) => BadRequest(e)
case MissingIdentifier(_) => BadRequest(e)
case RecordNotFound(_) => NotFound(e)
case _ => InternalServerError(e)
}
implicit class StringOps(s: String) {
import java.time.format.DateTimeFormatter
lazy val localformatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
lazy val datetimeformatter: DateTimeFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
def toLocalDateTime: LocalDateTime = LocalDateTime.parse(s,localformatter)
def toZonedDateTime: ZonedDateTime = ZonedDateTime.parse(s, datetimeformatter)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def deleteOrder(da: DataAccessor): Endpoint[Unit] =
delete("store" :: "order" :: string ) { (orderId: String) =>
da.Store_deleteOrder(orderId)
NoContent[Unit]
delete("store" :: "order" :: string) { (orderId: String) =>
da.Store_deleteOrder(orderId) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Map[String, Int]
* @return An endpoint representing a Map[String, Int]
*/
private def getInventory(da: DataAccessor): Endpoint[Map[String, Int]] =
get("store" :: "inventory" ) {
Ok(da.Store_getInventory())
get("store" :: "inventory" :: header("api_key")) {
da.Store_getInventory(authParamapi_key) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Order
* @return An endpoint representing a Order
*/
private def getOrderById(da: DataAccessor): Endpoint[Order] =
get("store" :: "order" :: long ) { (orderId: Long) =>
Ok(da.Store_getOrderById(orderId))
get("store" :: "order" :: long) { (orderId: Long) =>
da.Store_getOrderById(orderId) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Order
* @return An endpoint representing a Order
*/
private def placeOrder(da: DataAccessor): Endpoint[Order] =
post("store" :: "order" :: jsonBody[Order]) { (body: Order) =>
Ok(da.Store_placeOrder(body))
post("store" :: "order" :: jsonBody[Order]) { (body: Order) =>
da.Store_placeOrder(body) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
implicit private def fileUploadToFile(fileUpload: FileUpload) : File = {
fileUpload match {
case upload: InMemoryFileUpload =>
bytesToFile(Buf.ByteArray.Owned.extract(upload.content))
case upload: OnDiskFileUpload =>
upload.content
case _ => null
}
fileUpload match {
case upload: InMemoryFileUpload =>
bytesToFile(Buf.ByteArray.Owned.extract(upload.content))
case upload: OnDiskFileUpload =>
upload.content
case _ => null
}
}
private def bytesToFile(input: Array[Byte]): java.io.File = {
val file = File.createTempFile("tmpStoreApi", null)
val output = new FileOutputStream(file)
output.write(input)
file
val file = File.createTempFile("tmpStoreApi", null)
val output = new FileOutputStream(file)
output.write(input)
file
}
// This assists in params(string) application (which must be Seq[A] in parameter list) when the param is used as a List[A] elsewhere.

View File

@ -1,7 +1,6 @@
package io.swagger.apis
import java.io._
import java.util.Date
import io.swagger._
import io.swagger.models._
import scala.collection.immutable.Seq
@ -17,6 +16,7 @@ import com.twitter.util.Future
import com.twitter.io.Buf
import io.finch._, items._
import java.io.File
import java.time._
object UserApi {
/**
@ -24,125 +24,164 @@ object UserApi {
* @return Bundled compilation of all service endpoints.
*/
def endpoints(da: DataAccessor) =
createUser(da) :+:
createUsersWithArrayInput(da) :+:
createUsersWithListInput(da) :+:
deleteUser(da) :+:
getUserByName(da) :+:
loginUser(da) :+:
logoutUser(da) :+:
updateUser(da)
createUser(da) :+:
createUsersWithArrayInput(da) :+:
createUsersWithListInput(da) :+:
deleteUser(da) :+:
getUserByName(da) :+:
loginUser(da) :+:
logoutUser(da) :+:
updateUser(da)
private def checkError(e: CommonError) = e match {
case InvalidInput(_) => BadRequest(e)
case MissingIdentifier(_) => BadRequest(e)
case RecordNotFound(_) => NotFound(e)
case _ => InternalServerError(e)
}
implicit class StringOps(s: String) {
import java.time.format.DateTimeFormatter
lazy val localformatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
lazy val datetimeformatter: DateTimeFormatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
def toLocalDateTime: LocalDateTime = LocalDateTime.parse(s,localformatter)
def toZonedDateTime: ZonedDateTime = ZonedDateTime.parse(s, datetimeformatter)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def createUser(da: DataAccessor): Endpoint[Unit] =
post("user" :: jsonBody[User]) { (body: User) =>
da.User_createUser(body)
NoContent[Unit]
post("user" :: jsonBody[User]) { (body: User) =>
da.User_createUser(body) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def createUsersWithArrayInput(da: DataAccessor): Endpoint[Unit] =
post("user" :: "createWithArray" :: jsonBody[Seq[User]]) { (body: Seq[User]) =>
da.User_createUsersWithArrayInput(body)
NoContent[Unit]
post("user" :: "createWithArray" :: jsonBody[Seq[User]]) { (body: Seq[User]) =>
da.User_createUsersWithArrayInput(body) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def createUsersWithListInput(da: DataAccessor): Endpoint[Unit] =
post("user" :: "createWithList" :: jsonBody[Seq[User]]) { (body: Seq[User]) =>
da.User_createUsersWithListInput(body)
NoContent[Unit]
post("user" :: "createWithList" :: jsonBody[Seq[User]]) { (body: Seq[User]) =>
da.User_createUsersWithListInput(body) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def deleteUser(da: DataAccessor): Endpoint[Unit] =
delete("user" :: string ) { (username: String) =>
da.User_deleteUser(username)
NoContent[Unit]
delete("user" :: string) { (username: String) =>
da.User_deleteUser(username) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a User
* @return An endpoint representing a User
*/
private def getUserByName(da: DataAccessor): Endpoint[User] =
get("user" :: string ) { (username: String) =>
Ok(da.User_getUserByName(username))
get("user" :: string) { (username: String) =>
da.User_getUserByName(username) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a String
* @return An endpoint representing a String
*/
private def loginUser(da: DataAccessor): Endpoint[String] =
get("user" :: "login" :: param("username") :: param("password")) { (username: String, password: String) =>
Ok(da.User_loginUser(username, password))
get("user" :: "login" :: param("username") :: param("password")) { (username: String, password: String) =>
da.User_loginUser(username, password) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def logoutUser(da: DataAccessor): Endpoint[Unit] =
get("user" :: "logout" ) {
da.User_logoutUser()
NoContent[Unit]
get("user" :: "logout") {
da.User_logoutUser() match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
/**
*
* @return And endpoint representing a Unit
* @return An endpoint representing a Unit
*/
private def updateUser(da: DataAccessor): Endpoint[Unit] =
put("user" :: string :: jsonBody[User]) { (username: String, body: User) =>
da.User_updateUser(username, body)
NoContent[Unit]
put("user" :: string :: jsonBody[User]) { (username: String, body: User) =>
da.User_updateUser(username, body) match {
case Left(error) => checkError(error)
case Right(data) => Ok(data)
}
} handle {
case e: Exception => BadRequest(e)
}
implicit private def fileUploadToFile(fileUpload: FileUpload) : File = {
fileUpload match {
case upload: InMemoryFileUpload =>
bytesToFile(Buf.ByteArray.Owned.extract(upload.content))
case upload: OnDiskFileUpload =>
upload.content
case _ => null
}
fileUpload match {
case upload: InMemoryFileUpload =>
bytesToFile(Buf.ByteArray.Owned.extract(upload.content))
case upload: OnDiskFileUpload =>
upload.content
case _ => null
}
}
private def bytesToFile(input: Array[Byte]): java.io.File = {
val file = File.createTempFile("tmpUserApi", null)
val output = new FileOutputStream(file)
output.write(input)
file
val file = File.createTempFile("tmpUserApi", null)
val output = new FileOutputStream(file)
output.write(input)
file
}
// This assists in params(string) application (which must be Seq[A] in parameter list) when the param is used as a List[A] elsewhere.

View File

@ -5,7 +5,7 @@ import io.finch.circe._
import io.circe.generic.semiauto._
import io.circe.java8.time._
import io.swagger._
import java.time.LocalDateTime
import java.time.ZonedDateTime
/**
* An order for a pets from the pet store
@ -19,7 +19,7 @@ import java.time.LocalDateTime
case class Order(id: Option[Long],
petId: Option[Long],
quantity: Option[Int],
shipDate: Option[LocalDateTime],
shipDate: Option[ZonedDateTime],
status: Option[String],
complete: Option[Boolean]
)