From f22d0cf54832006f9c2b7e3e9da5c11734036279 Mon Sep 17 00:00:00 2001 From: Jim Schubert Date: Sat, 12 May 2018 15:40:24 -0400 Subject: [PATCH] [finch] Allow finch server to compile for CI checks Previous error handling implementation had types returning Either[CommonError, UserType], but implemented with the scala shortcut ??? which throws an exception instead. This causes compilation to fail with a message that the expected CommonError is of type Any. This is often fixable with generic upper bounds constraints, but this is overkill for a placeholder implementation. Returning a temporary 'TODO' type solves the compile error, and should allow CI to check for valid compilation on changes. Included in this is also a fix to support optional query parameter types. The spec used to generate the finch server has optional query parameters, but the version of finch in the template doesn't support options on query parameters. Finch does, however, aggregate everything (headers, query string, path parameters, etc) under "param" with "paramOption" for those which are optional types. --- .../codegen/languages/FinchServerCodegen.java | 33 ++++++------- .../resources/finch/DataAccessor.mustache | 10 ++-- .../src/main/resources/finch/api.mustache | 2 +- .../src/main/resources/finch/build.sbt | 2 +- samples/server/petstore/finch/build.sbt | 2 +- .../finch/src/main/scala/DataAccessor.scala | 48 ++++++++++--------- .../scala/org/openapitools/apis/PetApi.scala | 16 +++---- .../org/openapitools/apis/StoreApi.scala | 8 ++-- .../scala/org/openapitools/apis/UserApi.scala | 16 +++---- 9 files changed, 68 insertions(+), 69 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/FinchServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/FinchServerCodegen.java index 97f52988ed46..ffec1ff51682 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/FinchServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/FinchServerCodegen.java @@ -204,20 +204,7 @@ public String modelFileFolder() { return outputFolder + File.separator + sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar); } - /** - * Convert OpenAPI Model object to Codegen Model object - * - * @param name the name of the model - * @param model OpenAPI Model object - * @param allDefinitions a map of all OpenAPI models from the spec - * @return Codegen Model object - */ - @Override - public CodegenModel fromModel(String name, Schema model, Map allDefinitions) { - CodegenModel codegenModel = super.fromModel(name, model, allDefinitions); - return codegenModel; - } - + @SuppressWarnings("unchecked") @Override public Map postProcessOperations(Map objs) { Map operations = (Map) objs.get("operations"); @@ -244,6 +231,7 @@ public Map postProcessOperations(Map objs) { } + @SuppressWarnings("Duplicates") @Override public String getTypeDeclaration(Schema p) { if (ModelUtils.isArraySchema(p)) { @@ -261,7 +249,7 @@ public String getTypeDeclaration(Schema p) { @Override public String getSchemaType(Schema p) { String schemaType = super.getSchemaType(p); - String type = null; + String type; if (typeMapping.containsKey(schemaType)) { type = typeMapping.get(schemaType); if (languageSpecificPrimitives.contains(type)) { @@ -371,9 +359,9 @@ private void generateScalaPath(CodegenOperation op) { String scalaPath = ""; Integer pathParamIndex = 0; - for (int i = 0; i < items.length; ++i) { + for (String item : items) { - if (items[i].matches("^\\{(.*)\\}$")) { // wrap in {} + if (item.matches("^\\{(.*)}$")) { // wrap in {} // find the datatype of the parameter final CodegenParameter cp = op.pathParams.get(pathParamIndex); @@ -382,7 +370,7 @@ private void generateScalaPath(CodegenOperation op) { pathParamIndex++; } else { - scalaPath = colConcat(scalaPath, "\"" + items[i] + "\""); + scalaPath = colConcat(scalaPath, "\"" + item + "\""); } } @@ -431,7 +419,14 @@ private void generateInputParameters(CodegenOperation op) { 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()); + if (!p.required) { + // Generator's current version of Finch doesn't support something like stringOption, but finch aggregates all + // parameter types under "param", so optional params can be grabbed by "paramOption". + p.vendorExtensions.put("x-codegen-normalized-path-type", toPathParameter(p, "param", true)); + } else { + // If parameter is primitive and required, we can rely on data types like "string" or "long" + 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() diff --git a/modules/openapi-generator/src/main/resources/finch/DataAccessor.mustache b/modules/openapi-generator/src/main/resources/finch/DataAccessor.mustache index ca4e3a8f851d..774ccaa025e8 100644 --- a/modules/openapi-generator/src/main/resources/finch/DataAccessor.mustache +++ b/modules/openapi-generator/src/main/resources/finch/DataAccessor.mustache @@ -4,13 +4,15 @@ package {{packageName}} import java.io._ import java.util.UUID import java.time._ - +import com.twitter.finagle.http.exp.Multipart.{FileUpload, InMemoryFileUpload, OnDiskFileUpload} import {{modelPackage}}._ trait DataAccessor { - // TODO: apiInfo -> apis -> operations = ??? - // NOTE: ??? throws a not implemented exception + // TODO: apiInfo -> apis -> operations = TODO error + private object TODO extends CommonError("Not implemented") { + def message = "Not implemented" + } {{#apiInfo}} {{#apis}} @@ -20,7 +22,7 @@ trait DataAccessor { * {{{description}}} * @return A {{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}} */ - def {{baseName}}_{{operationId}}({{{vendorExtensions.x-codegen-typedParams}}}): Either[CommonError,{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}] = ??? + def {{baseName}}_{{operationId}}({{{vendorExtensions.x-codegen-typedParams}}}): Either[CommonError,{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Unit{{/returnType}}] = Left(TODO) {{/operation}} {{/operations}} diff --git a/modules/openapi-generator/src/main/resources/finch/api.mustache b/modules/openapi-generator/src/main/resources/finch/api.mustache index 8365f59b5bdc..9f2437f348a6 100644 --- a/modules/openapi-generator/src/main/resources/finch/api.mustache +++ b/modules/openapi-generator/src/main/resources/finch/api.mustache @@ -58,7 +58,7 @@ object {{classname}} { * @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-paths}}}) { {{#hasParams}}({{{vendorExtensions.x-codegen-typedParams}}}) => {{/hasParams}} + {{httpMethod}}({{{vendorExtensions.x-codegen-paths}}}) { {{#vendorExtensions.x-codegen-typedParams}}({{{vendorExtensions.x-codegen-typedParams}}}) =>{{/vendorExtensions.x-codegen-typedParams}} da.{{baseName}}_{{operationId}}({{{vendorExtensions.x-codegen-params}}}) match { case Left(error) => checkError(error) case Right(data) => Ok(data) diff --git a/modules/openapi-generator/src/main/resources/finch/build.sbt b/modules/openapi-generator/src/main/resources/finch/build.sbt index b4d013e36cfb..282a54185b6c 100644 --- a/modules/openapi-generator/src/main/resources/finch/build.sbt +++ b/modules/openapi-generator/src/main/resources/finch/build.sbt @@ -6,7 +6,7 @@ name := "finch-sample" version := "0.1.0-SNAPSHOT" -scalaVersion := "2.11.12" +scalaVersion := "2.12.3" resolvers += Resolver.sonatypeRepo("snapshots") diff --git a/samples/server/petstore/finch/build.sbt b/samples/server/petstore/finch/build.sbt index b4d013e36cfb..282a54185b6c 100644 --- a/samples/server/petstore/finch/build.sbt +++ b/samples/server/petstore/finch/build.sbt @@ -6,7 +6,7 @@ name := "finch-sample" version := "0.1.0-SNAPSHOT" -scalaVersion := "2.11.12" +scalaVersion := "2.12.3" resolvers += Resolver.sonatypeRepo("snapshots") diff --git a/samples/server/petstore/finch/src/main/scala/DataAccessor.scala b/samples/server/petstore/finch/src/main/scala/DataAccessor.scala index 8c669f91a668..20c85259099b 100644 --- a/samples/server/petstore/finch/src/main/scala/DataAccessor.scala +++ b/samples/server/petstore/finch/src/main/scala/DataAccessor.scala @@ -4,132 +4,134 @@ package org.openapitools import java.io._ import java.util.UUID import java.time._ - +import com.twitter.finagle.http.exp.Multipart.{FileUpload, InMemoryFileUpload, OnDiskFileUpload} import org.openapitools.models._ trait DataAccessor { - // TODO: apiInfo -> apis -> operations = ??? - // NOTE: ??? throws a not implemented exception + // TODO: apiInfo -> apis -> operations = TODO error + private object TODO extends CommonError("Not implemented") { + def message = "Not implemented" + } /** * * @return A Unit */ - def Pet_addPet(pet: Pet): Either[CommonError,Unit] = ??? + def Pet_addPet(pet: Pet): Either[CommonError,Unit] = Left(TODO) /** * * @return A Unit */ - def Pet_deletePet(petId: Long, apiKey: Option[String]): Either[CommonError,Unit] = ??? + def Pet_deletePet(petId: Long, apiKey: Option[String]): Either[CommonError,Unit] = Left(TODO) /** * * @return A Seq[Pet] */ - def Pet_findPetsByStatus(status: Seq[String]): Either[CommonError,Seq[Pet]] = ??? + def Pet_findPetsByStatus(status: Seq[String]): Either[CommonError,Seq[Pet]] = Left(TODO) /** * * @return A Seq[Pet] */ - def Pet_findPetsByTags(tags: Seq[String]): Either[CommonError,Seq[Pet]] = ??? + def Pet_findPetsByTags(tags: Seq[String]): Either[CommonError,Seq[Pet]] = Left(TODO) /** * * @return A Pet */ - def Pet_getPetById(petId: Long, authParamapi_key: String): Either[CommonError,Pet] = ??? + def Pet_getPetById(petId: Long, authParamapi_key: String): Either[CommonError,Pet] = Left(TODO) /** * * @return A Unit */ - def Pet_updatePet(pet: Pet): Either[CommonError,Unit] = ??? + def Pet_updatePet(pet: Pet): Either[CommonError,Unit] = Left(TODO) /** * * @return A Unit */ - def Pet_updatePetWithForm(petId: Long, name: Option[String], status: Option[String]): Either[CommonError,Unit] = ??? + def Pet_updatePetWithForm(petId: Long, name: Option[String], status: Option[String]): Either[CommonError,Unit] = Left(TODO) /** * * @return A ApiResponse */ - def Pet_uploadFile(petId: Long, additionalMetadata: Option[String], file: FileUpload): Either[CommonError,ApiResponse] = ??? + def Pet_uploadFile(petId: Long, additionalMetadata: Option[String], file: FileUpload): Either[CommonError,ApiResponse] = Left(TODO) /** * * @return A Unit */ - def Store_deleteOrder(orderId: String): Either[CommonError,Unit] = ??? + def Store_deleteOrder(orderId: String): Either[CommonError,Unit] = Left(TODO) /** * * @return A Map[String, Int] */ - def Store_getInventory(authParamapi_key: String): Either[CommonError,Map[String, Int]] = ??? + def Store_getInventory(authParamapi_key: String): Either[CommonError,Map[String, Int]] = Left(TODO) /** * * @return A Order */ - def Store_getOrderById(orderId: Long): Either[CommonError,Order] = ??? + def Store_getOrderById(orderId: Long): Either[CommonError,Order] = Left(TODO) /** * * @return A Order */ - def Store_placeOrder(order: Order): Either[CommonError,Order] = ??? + def Store_placeOrder(order: Order): Either[CommonError,Order] = Left(TODO) /** * * @return A Unit */ - def User_createUser(user: User): Either[CommonError,Unit] = ??? + def User_createUser(user: User): Either[CommonError,Unit] = Left(TODO) /** * * @return A Unit */ - def User_createUsersWithArrayInput(user: Seq[User]): Either[CommonError,Unit] = ??? + def User_createUsersWithArrayInput(user: Seq[User]): Either[CommonError,Unit] = Left(TODO) /** * * @return A Unit */ - def User_createUsersWithListInput(user: Seq[User]): Either[CommonError,Unit] = ??? + def User_createUsersWithListInput(user: Seq[User]): Either[CommonError,Unit] = Left(TODO) /** * * @return A Unit */ - def User_deleteUser(username: String): Either[CommonError,Unit] = ??? + def User_deleteUser(username: String): Either[CommonError,Unit] = Left(TODO) /** * * @return A User */ - def User_getUserByName(username: String): Either[CommonError,User] = ??? + def User_getUserByName(username: String): Either[CommonError,User] = Left(TODO) /** * * @return A String */ - def User_loginUser(username: String, password: String): Either[CommonError,String] = ??? + def User_loginUser(username: String, password: String): Either[CommonError,String] = Left(TODO) /** * * @return A Unit */ - def User_logoutUser(): Either[CommonError,Unit] = ??? + def User_logoutUser(): Either[CommonError,Unit] = Left(TODO) /** * * @return A Unit */ - def User_updateUser(username: String, user: User): Either[CommonError,Unit] = ??? + def User_updateUser(username: String, user: User): Either[CommonError,Unit] = Left(TODO) } \ No newline at end of file diff --git a/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/PetApi.scala b/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/PetApi.scala index 8dffbb6f6005..236496ce1105 100644 --- a/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/PetApi.scala +++ b/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/PetApi.scala @@ -60,7 +60,7 @@ object PetApi { * @return An endpoint representing a Unit */ private def addPet(da: DataAccessor): Endpoint[Unit] = - post("pet" :: jsonBody[Pet]) { (pet: Pet) => + post("pet" :: jsonBody[Pet]) { (pet: Pet) => da.Pet_addPet(pet) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -74,7 +74,7 @@ object PetApi { * @return An endpoint representing a Unit */ private def deletePet(da: DataAccessor): Endpoint[Unit] = - delete("pet" :: long :: headerOption("api_key")) { (petId: Long, apiKey: Option[String]) => + 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) @@ -88,7 +88,7 @@ object PetApi { * @return An endpoint representing a Seq[Pet] */ private def findPetsByStatus(da: DataAccessor): Endpoint[Seq[Pet]] = - get("pet" :: "findByStatus" :: params("status")) { (status: Seq[String]) => + get("pet" :: "findByStatus" :: params("status")) { (status: Seq[String]) => da.Pet_findPetsByStatus(status) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -102,7 +102,7 @@ object PetApi { * @return An endpoint representing a Seq[Pet] */ private def findPetsByTags(da: DataAccessor): Endpoint[Seq[Pet]] = - get("pet" :: "findByTags" :: params("tags")) { (tags: Seq[String]) => + get("pet" :: "findByTags" :: params("tags")) { (tags: Seq[String]) => da.Pet_findPetsByTags(tags) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -116,7 +116,7 @@ object PetApi { * @return An endpoint representing a Pet */ private def getPetById(da: DataAccessor): Endpoint[Pet] = - get("pet" :: long :: header("api_key")) { (petId: Long, authParamapi_key: String) => + 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) @@ -130,7 +130,7 @@ object PetApi { * @return An endpoint representing a Unit */ private def updatePet(da: DataAccessor): Endpoint[Unit] = - put("pet" :: jsonBody[Pet]) { (pet: Pet) => + put("pet" :: jsonBody[Pet]) { (pet: Pet) => da.Pet_updatePet(pet) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -144,7 +144,7 @@ object PetApi { * @return An endpoint representing a Unit */ private def updatePetWithForm(da: DataAccessor): Endpoint[Unit] = - post("pet" :: long :: string :: string) { (petId: Long, name: Option[String], status: Option[String]) => + post("pet" :: long :: paramOption("name") :: paramOption("status")) { (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) @@ -158,7 +158,7 @@ object PetApi { * @return An endpoint representing a ApiResponse */ private def uploadFile(da: DataAccessor): Endpoint[ApiResponse] = - post("pet" :: long :: "uploadImage" :: string :: fileUpload("file")) { (petId: Long, additionalMetadata: Option[String], file: FileUpload) => + post("pet" :: long :: "uploadImage" :: paramOption("additionalMetadata") :: 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) diff --git a/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/StoreApi.scala b/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/StoreApi.scala index bc4f0610394b..7fff45a72260 100644 --- a/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/StoreApi.scala +++ b/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/StoreApi.scala @@ -54,7 +54,7 @@ object StoreApi { * @return An endpoint representing a Unit */ private def deleteOrder(da: DataAccessor): Endpoint[Unit] = - delete("store" :: "order" :: string) { (orderId: String) => + delete("store" :: "order" :: string) { (orderId: String) => da.Store_deleteOrder(orderId) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -68,7 +68,7 @@ object StoreApi { * @return An endpoint representing a Map[String, Int] */ private def getInventory(da: DataAccessor): Endpoint[Map[String, Int]] = - get("store" :: "inventory" :: header("api_key")) { + get("store" :: "inventory" :: header("api_key")) { (authParamapi_key: String) => da.Store_getInventory(authParamapi_key) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -82,7 +82,7 @@ object StoreApi { * @return An endpoint representing a Order */ private def getOrderById(da: DataAccessor): Endpoint[Order] = - get("store" :: "order" :: long) { (orderId: Long) => + get("store" :: "order" :: long) { (orderId: Long) => da.Store_getOrderById(orderId) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -96,7 +96,7 @@ object StoreApi { * @return An endpoint representing a Order */ private def placeOrder(da: DataAccessor): Endpoint[Order] = - post("store" :: "order" :: jsonBody[Order]) { (order: Order) => + post("store" :: "order" :: jsonBody[Order]) { (order: Order) => da.Store_placeOrder(order) match { case Left(error) => checkError(error) case Right(data) => Ok(data) diff --git a/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/UserApi.scala b/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/UserApi.scala index 5b1137d62b5a..6401471ab102 100644 --- a/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/UserApi.scala +++ b/samples/server/petstore/finch/src/main/scala/org/openapitools/apis/UserApi.scala @@ -59,7 +59,7 @@ object UserApi { * @return An endpoint representing a Unit */ private def createUser(da: DataAccessor): Endpoint[Unit] = - post("user" :: jsonBody[User]) { (user: User) => + post("user" :: jsonBody[User]) { (user: User) => da.User_createUser(user) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -73,7 +73,7 @@ object UserApi { * @return An endpoint representing a Unit */ private def createUsersWithArrayInput(da: DataAccessor): Endpoint[Unit] = - post("user" :: "createWithArray" :: jsonBody[Seq[User]]) { (user: Seq[User]) => + post("user" :: "createWithArray" :: jsonBody[Seq[User]]) { (user: Seq[User]) => da.User_createUsersWithArrayInput(user) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -87,7 +87,7 @@ object UserApi { * @return An endpoint representing a Unit */ private def createUsersWithListInput(da: DataAccessor): Endpoint[Unit] = - post("user" :: "createWithList" :: jsonBody[Seq[User]]) { (user: Seq[User]) => + post("user" :: "createWithList" :: jsonBody[Seq[User]]) { (user: Seq[User]) => da.User_createUsersWithListInput(user) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -101,7 +101,7 @@ object UserApi { * @return An endpoint representing a Unit */ private def deleteUser(da: DataAccessor): Endpoint[Unit] = - delete("user" :: string) { (username: String) => + delete("user" :: string) { (username: String) => da.User_deleteUser(username) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -115,7 +115,7 @@ object UserApi { * @return An endpoint representing a User */ private def getUserByName(da: DataAccessor): Endpoint[User] = - get("user" :: string) { (username: String) => + get("user" :: string) { (username: String) => da.User_getUserByName(username) match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -129,7 +129,7 @@ object UserApi { * @return An endpoint representing a String */ private def loginUser(da: DataAccessor): Endpoint[String] = - get("user" :: "login" :: param("username") :: param("password")) { (username: String, password: String) => + 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) @@ -143,7 +143,7 @@ object UserApi { * @return An endpoint representing a Unit */ private def logoutUser(da: DataAccessor): Endpoint[Unit] = - get("user" :: "logout") { + get("user" :: "logout") { () => da.User_logoutUser() match { case Left(error) => checkError(error) case Right(data) => Ok(data) @@ -157,7 +157,7 @@ object UserApi { * @return An endpoint representing a Unit */ private def updateUser(da: DataAccessor): Endpoint[Unit] = - put("user" :: string :: jsonBody[User]) { (username: String, user: User) => + put("user" :: string :: jsonBody[User]) { (username: String, user: User) => da.User_updateUser(username, user) match { case Left(error) => checkError(error) case Right(data) => Ok(data)