[WIP][Scala] Finch generator (#3905)

* Feature/objc tasks 2.3.0 (#3522)

* change api and ApiClient to return cancellable NSURLSessionTasks instead of NSNumber

* define a configuration protocol for custom configurations, which can be passed to api clients instead of a global configuration, provide a default implementation with a singleton option

* integrate a workaround for a current JSONModel concurrency bug

* update to new ISO8601 pod

* add missing call to super

* integrate new templates into codegen

* updates documentation templates

* updates petstore objc generated code

* fixes objc client tests

* [ObjC] Add version define and share default headers of each client

* add finch generator and resource

* update license, add errros

* Fix problem with multitheard api client

* fix some errors for finch

* [finch] Remove license header

* [finch] Remove finatra stuff, fix a few issues

* WIP: Finch server generator

* [finch] WIP: server generator impl

This puts parameters (input/output) in the right format. Currently, this
is done in the generator class using vendorExtensions, but should be
refactored to imported templates to clean up.

Previous commits of the server generator output to appropriate
models/api directories. I've made no changes to this logic, but code
currently generates to the root scala package directory. This will need
to be fixed.

There's also an issue with circe's and Option[Date] in the Order type.
This issue will need to be resolved. As well, there's some unused
imports to clean up.

Initial implementation lacks support for custom imports, type mappings,
etc.

* [finch] Update api/model package and imports

* [finch] Explicit import/type mappings

* [finch] Regenerate example
This commit is contained in:
wing328
2017-01-29 12:15:39 +08:00
committed by GitHub
parent 6890ef9755
commit ae8a123484
140 changed files with 5262 additions and 2421 deletions

View File

@@ -0,0 +1,320 @@
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;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.Property;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
public class FinchServerCodegen extends DefaultCodegen implements CodegenConfig {
protected String invokerPackage = "io.swagger.petstore.client";
protected String groupId = "io.swagger";
protected String artifactId = "finch-server";
protected String artifactVersion = "1.0.0";
protected String sourceFolder = "src/main/scala";
protected String packageName = "io.swagger.petstore";
public FinchServerCodegen() {
super();
outputFolder = "generated-code/finch";
modelTemplateFiles.put("model.mustache", ".scala");
apiTemplateFiles.put("api.mustache", ".scala");
embeddedTemplateDir = templateDir = "finch";
apiPackage = packageName + ".apis";
modelPackage = packageName + ".models";
setReservedWordsLowerCase(
Arrays.asList(
// Scala
"abstract", "case", "catch", "class", "def",
"do", "else", "extends", "false", "final",
"finally", "for", "forSome", "if", "implicit",
"import", "lazy", "match", "new", "null",
"object", "override", "package", "private", "protected",
"return", "sealed", "super", "this", "throw",
"trait", "try", "true", "type", "val",
"var", "while", "with", "yield",
// Scala-interop languages keywords
"abstract", "continue", "switch", "assert",
"default", "synchronized", "goto",
"break", "double", "implements", "byte",
"public", "throws", "enum", "instanceof", "transient",
"int", "short", "char", "interface", "static",
"void", "finally", "long", "strictfp", "volatile", "const", "float",
"native")
);
defaultIncludes = new HashSet<String>(
Arrays.asList("double",
"Int",
"Long",
"Float",
"Double",
"char",
"float",
"String",
"boolean",
"Boolean",
"Double",
"Integer",
"Long",
"Float",
"List",
"Set",
"Map")
);
typeMapping = new HashMap<String, String>();
typeMapping.put("string", "String");
typeMapping.put("boolean", "Boolean");
typeMapping.put("integer", "Int");
typeMapping.put("float", "Float");
typeMapping.put("long", "Long");
typeMapping.put("double", "Double");
typeMapping.put("number", "BigDecimal");
typeMapping.put("date-time", "LocalDateTime");
typeMapping.put("date", "LocalDateTime");
typeMapping.put("file", "File");
typeMapping.put("array", "Seq");
typeMapping.put("list", "List");
typeMapping.put("map", "Map");
typeMapping.put("object", "Object");
typeMapping.put("binary", "Array[Byte]");
typeMapping.put("Date", "LocalDateTime");
typeMapping.put("DateTime", "LocalDateTime");
additionalProperties.put("modelPackage", modelPackage());
additionalProperties.put("apiPackage", apiPackage());
additionalProperties.put("appName", "Swagger Sample");
additionalProperties.put("appDescription", "A sample swagger server");
additionalProperties.put("infoUrl", "http://swagger.io");
additionalProperties.put("infoEmail", "apiteam@swagger.io");
additionalProperties.put("licenseInfo", "Apache 2.0");
additionalProperties.put("licenseUrl", "http://apache.org/licenses/LICENSE-2.0.html");
additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage);
additionalProperties.put(CodegenConstants.GROUP_ID, groupId);
additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId);
additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion);
if (additionalProperties.containsKey(CodegenConstants.PACKAGE_NAME)) {
setPackageName((String) additionalProperties.get(CodegenConstants.PACKAGE_NAME));
} else {
additionalProperties.put(CodegenConstants.PACKAGE_NAME, packageName);
}
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("build.sbt", "", "build.sbt"));
supportingFiles.add(new SupportingFile("Server.mustache", sourceFolder, "Server.scala"));
supportingFiles.add(new SupportingFile("DataAccessor.mustache", sourceFolder, "DataAccessor.scala"));
supportingFiles.add(new SupportingFile("project/build.properties", "project", "build.properties"));
supportingFiles.add(new SupportingFile("project/plugins.sbt", "project", "plugins.sbt"));
supportingFiles.add(new SupportingFile("sbt", "", "sbt"));
supportingFiles.add(new SupportingFile("endpoint.mustache", sourceFolder, "endpoint.scala"));
supportingFiles.add(new SupportingFile("errors.mustache", sourceFolder, "errors.scala"));
languageSpecificPrimitives = new HashSet<String>(
Arrays.asList(
"String",
"Boolean",
"Double",
"Int",
"Integer",
"Long",
"Float",
"Any",
"AnyVal",
"AnyRef",
"Object")
);
instantiationTypes.put("array", "ArrayList");
instantiationTypes.put("map", "HashMap");
importMapping = new HashMap<String, String>();
importMapping.put("BigDecimal", "java.math.BigDecimal");
importMapping.put("UUID", "java.util.UUID");
importMapping.put("File", "java.io.File");
importMapping.put("Date", "java.util.Date");
importMapping.put("Timestamp", "java.sql.Timestamp");
importMapping.put("Map", "scala.collection.immutable.Map");
importMapping.put("HashMap", "scala.collection.immutable.HashMap");
importMapping.put("Seq", "scala.collection.immutable.Seq");
importMapping.put("ArrayBuffer", "scala.collection.mutable.ArrayBuffer");
importMapping.put("DateTime", "java.time.LocalDateTime");
importMapping.put("LocalDateTime", "java.time.LocalDateTime");
importMapping.put("LocalDate", "java.time.LocalDate");
importMapping.put("LocalTime", "java.time.LocalTime");
cliOptions.clear();
cliOptions.add(new CliOption(CodegenConstants.PACKAGE_NAME, "Finch package name (e.g. io.swagger.petstore).")
.defaultValue(this.packageName));
cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC));
cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC));
}
@Override
public CodegenType getTag() {
return CodegenType.SERVER;
}
@Override
public String getName() {
return "finch";
}
@Override
public String getHelp() {
return "Generates a Scala server application with Finch.";
}
@Override
public String escapeReservedWord(String name) {
return "_" + name;
}
@Override
public String apiFileFolder() {
return outputFolder + File.separator + sourceFolder + File.separator + apiPackage().replace('.', File.separatorChar);
}
@Override
public String modelFileFolder() {
return outputFolder + File.separator + sourceFolder + File.separator + modelPackage().replace('.', File.separatorChar);
}
/**
* Convert Swagger Model object to Codegen Model object
*
* @param name the name of the model
* @param model Swagger Model object
* @param allDefinitions a map of all Swagger models from the spec
* @return Codegen Model object
*/
@Override
public CodegenModel fromModel(String name, Model model, Map<String, Model> allDefinitions) {
CodegenModel codegenModel = super.fromModel(name, model, allDefinitions);
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);
}
String[] items = path.split("/", -1);
String scalaPath = "";
int 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 = 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(Boolean.TRUE.equals(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(Boolean.TRUE.equals(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(Boolean.TRUE.equals(p.isContainer) || Boolean.TRUE.equals(p.isListContainer)) {
p.vendorExtensions.put("x-codegen-normalized-path-type", "params(\""+ p.paramName + "\")");
p.vendorExtensions.put("x-codegen-normalized-input-type", p.dataType.replaceAll("^[^\\[]+", "Seq"));
} else if(Boolean.TRUE.equals(p.isFile)) {
p.vendorExtensions.put("x-codegen-normalized-path-type", "fileUpload(\""+ p.paramName + "\")");
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);
}
return objs;
}
@Override
public String getTypeDeclaration(Property p) {
if (p instanceof ArrayProperty) {
ArrayProperty ap = (ArrayProperty) p;
Property inner = ap.getItems();
return getSwaggerType(p) + "[" + getTypeDeclaration(inner) + "]";
} else if (p instanceof MapProperty) {
MapProperty mp = (MapProperty) p;
Property inner = mp.getAdditionalProperties();
return getSwaggerType(p) + "[String, " + getTypeDeclaration(inner) + "]";
}
return super.getTypeDeclaration(p);
}
@Override
public String getSwaggerType(Property p) {
String swaggerType = super.getSwaggerType(p);
String type = null;
if (typeMapping.containsKey(swaggerType)) {
type = typeMapping.get(swaggerType);
if (languageSpecificPrimitives.contains(type)) {
return toModelName(type);
}
} else {
type = swaggerType;
}
return toModelName(type);
}
@Override
public String escapeQuotationMark(String input) {
// remove " to avoid code injection
return input.replace("\"", "");
}
@Override
public String escapeUnsafeCharacters(String input) {
return input.replace("*/", "*_/").replace("/*", "/_*");
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
}

View File

@@ -255,10 +255,8 @@ public class ObjcClientCodegen extends DefaultCodegen implements CodegenConfig {
supportingFiles.add(new SupportingFile("Object-body.mustache", coreFileFolder(), classPrefix + "Object.m"));
supportingFiles.add(new SupportingFile("QueryParamCollection-header.mustache", coreFileFolder(), classPrefix + "QueryParamCollection.h"));
supportingFiles.add(new SupportingFile("QueryParamCollection-body.mustache", coreFileFolder(), classPrefix + "QueryParamCollection.m"));
supportingFiles.add(new SupportingFile("ApiClient-header.mustache", coreFileFolder(), classPrefix + "ApiClient.h"));
supportingFiles.add(new SupportingFile("ApiClient-header.mustache", coreFileFolder(), classPrefix + "ApiClient.h"));
supportingFiles.add(new SupportingFile("ApiClient-body.mustache", coreFileFolder(), classPrefix + "ApiClient.m"));
supportingFiles.add(new SupportingFile("JSONResponseSerializer-header.mustache", coreFileFolder(), classPrefix + "JSONResponseSerializer.h"));
supportingFiles.add(new SupportingFile("JSONResponseSerializer-body.mustache", coreFileFolder(), classPrefix + "JSONResponseSerializer.m"));
supportingFiles.add(new SupportingFile("JSONRequestSerializer-body.mustache", coreFileFolder(), classPrefix + "JSONRequestSerializer.m"));
supportingFiles.add(new SupportingFile("JSONRequestSerializer-header.mustache", coreFileFolder(), classPrefix + "JSONRequestSerializer.h"));
supportingFiles.add(new SupportingFile("ResponseDeserializer-body.mustache", coreFileFolder(), classPrefix + "ResponseDeserializer.m"));
@@ -269,8 +267,11 @@ public class ObjcClientCodegen extends DefaultCodegen implements CodegenConfig {
supportingFiles.add(new SupportingFile("Logger-header.mustache", coreFileFolder(), classPrefix + "Logger.h"));
supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601-body.mustache", coreFileFolder(), "JSONValueTransformer+ISO8601.m"));
supportingFiles.add(new SupportingFile("JSONValueTransformer+ISO8601-header.mustache", coreFileFolder(), "JSONValueTransformer+ISO8601.h"));
supportingFiles.add(new SupportingFile("Configuration-body.mustache", coreFileFolder(), classPrefix + "Configuration.m"));
supportingFiles.add(new SupportingFile("Configuration-header.mustache", coreFileFolder(), classPrefix + "Configuration.h"));
supportingFiles.add(new SupportingFile("Configuration-protocol.mustache", coreFileFolder(), classPrefix + "Configuration.h"));
supportingFiles.add(new SupportingFile("DefaultConfiguration-body.mustache", coreFileFolder(), classPrefix + "DefaultConfiguration.m"));
supportingFiles.add(new SupportingFile("DefaultConfiguration-header.mustache", coreFileFolder(), classPrefix + "DefaultConfiguration.h"));
supportingFiles.add(new SupportingFile("BasicAuthTokenProvider-header.mustache", coreFileFolder(), classPrefix + "BasicAuthTokenProvider.h"));
supportingFiles.add(new SupportingFile("BasicAuthTokenProvider-body.mustache", coreFileFolder(), classPrefix + "BasicAuthTokenProvider.m"));
supportingFiles.add(new SupportingFile("api-protocol.mustache", coreFileFolder(), classPrefix + "Api.h"));
supportingFiles.add(new SupportingFile("podspec.mustache", "", podName + ".podspec"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));

View File

@@ -65,7 +65,6 @@ public class ScalatraServerCodegen extends AbstractScalaCodegen implements Codeg
// mapped to String as a workaround
typeMapping.put("binary", "String");
additionalProperties.put("appName", "Swagger Sample");
additionalProperties.put("appName", "Swagger Sample");
additionalProperties.put("appDescription", "A sample swagger server");
additionalProperties.put("infoUrl", "http://swagger.io");