-
Notifications
You must be signed in to change notification settings - Fork 6k
[LagomScala] Add Scala API generator for lagomframework #6900 #6901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
b0145d3
Initial Commit
gmkumar2005 c34913e
Removed unneeded class, Updated names as per naming conventions, Remo…
gmkumar2005 deebf31
Moved samples from client folder to server
gmkumar2005 88d8dbc
Fixed naming issue with scripts in the bin
gmkumar2005 c077fce
improved code format for api
gmkumar2005 ce4c674
fixed compile issue and improved formatting
gmkumar2005 eb5f0ec
fixed compile issue and improved formatting
gmkumar2005 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #!/bin/sh | ||
|
|
||
| SCRIPT="$0" | ||
|
|
||
| while [ -h "$SCRIPT" ] ; do | ||
| ls=`ls -ld "$SCRIPT"` | ||
| link=`expr "$ls" : '.*-> \(.*\)$'` | ||
| if expr "$link" : '/.*' > /dev/null; then | ||
| SCRIPT="$link" | ||
| else | ||
| SCRIPT=`dirname "$SCRIPT"`/"$link" | ||
| fi | ||
| done | ||
|
|
||
| if [ ! -d "${APP_DIR}" ]; then | ||
| APP_DIR=`dirname "$SCRIPT"`/.. | ||
| APP_DIR=`cd "${APP_DIR}"; pwd` | ||
| fi | ||
|
|
||
| executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar" | ||
|
|
||
| if [ ! -f "$executable" ] | ||
| then | ||
| mvn clean package | ||
| fi | ||
|
|
||
| # if you've executed sbt assembly previously it will use that instead. | ||
| export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" | ||
| ags="$@ generate -t modules/swagger-codegen/src/main/resources/lagomScalaApi -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l scala-lagomApi -o samples/server/petstore/lagomScalaApi" | ||
|
|
||
| java $JAVA_OPTS -jar $executable $ags |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| set executable=.\modules\swagger-codegen-cli\target\swagger-codegen-cli.jar | ||
|
|
||
| If Not Exist %executable% ( | ||
| mvn clean package | ||
| ) | ||
|
|
||
| REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M | ||
| set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l scala-lagomApi -o samples\server\petstore\scala-lagomApi | ||
|
|
||
| java %JAVA_OPTS% -jar %executable% %ags% |
280 changes: 280 additions & 0 deletions
280
...s/swagger-codegen/src/main/java/io/swagger/codegen/languages/ScalaLagomServerCodegen.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,280 @@ | ||
| package io.swagger.codegen.languages; | ||
|
|
||
| import io.swagger.codegen.*; | ||
| import org.apache.commons.lang3.StringUtils; | ||
|
|
||
| import java.util.*; | ||
|
|
||
| public class ScalaLagomServerCodegen extends AbstractScalaCodegen implements CodegenConfig { | ||
|
|
||
| private String authScheme = ""; | ||
| private boolean authPreemptive=false; | ||
| protected String groupId = "io.swagger"; | ||
| protected String artifactId = "scala-lagomApi"; | ||
| protected String artifactVersion = "1.0.0"; | ||
|
|
||
| public ScalaLagomServerCodegen() { | ||
| super(); | ||
| outputFolder = "generated-code/scala-lagomApi"; | ||
| modelTemplateFiles.put("model.mustache", ".scala"); | ||
| apiTemplateFiles.put("api.mustache", ".scala"); | ||
| embeddedTemplateDir = templateDir = "lagomScalaApi"; | ||
| apiPackage = "io.swagger.client.api"; | ||
| modelPackage = "io.swagger.client.model"; | ||
|
|
||
| setReservedWordsLowerCase( | ||
| Arrays.asList( | ||
| // local variable names used in API methods (endpoints) | ||
| "path", "contentTypes", "contentType", "queryParams", "headerParams", | ||
| "formParams", "postBody", "mp", "basePath", "apiInvoker", | ||
|
|
||
| // scala reserved words | ||
| "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") | ||
| ); | ||
|
|
||
| additionalProperties.put(CodegenConstants.GROUP_ID, groupId); | ||
| additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); | ||
| additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); | ||
| additionalProperties.put("authScheme", authScheme); | ||
| additionalProperties.put("authPreemptive", authPreemptive); | ||
|
|
||
| supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); | ||
| supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); | ||
| supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); | ||
| supportingFiles.add(new SupportingFile("build.properties.mustache", "", "project/build.properties")); | ||
| supportingFiles.add(new SupportingFile("plugins.sbt.mustache", "", "project/plugins.sbt")); | ||
|
|
||
| importMapping.remove("List"); | ||
| importMapping.remove("Set"); | ||
| importMapping.remove("Map"); | ||
|
|
||
| importMapping.put("DateTime", "org.joda.time.DateTime"); | ||
| importMapping.put("ListBuffer", "scala.collection.mutable.ListBuffer"); | ||
|
|
||
| typeMapping = new HashMap<String, String>(); | ||
| typeMapping.put("Integer", "Int"); | ||
| typeMapping.put("enum", "NSString"); | ||
| typeMapping.put("array", "Seq"); | ||
| typeMapping.put("set", "Set"); | ||
| typeMapping.put("boolean", "Boolean"); | ||
| typeMapping.put("string", "String"); | ||
| typeMapping.put("int", "Int"); | ||
| typeMapping.put("long", "Long"); | ||
| typeMapping.put("float", "Float"); | ||
| typeMapping.put("byte", "Byte"); | ||
| typeMapping.put("short", "Short"); | ||
| typeMapping.put("char", "Char"); | ||
| typeMapping.put("long", "Long"); | ||
| typeMapping.put("double", "Double"); | ||
| typeMapping.put("object", "Any"); | ||
| typeMapping.put("file", "File"); | ||
|
|
||
| //TODO binary should be mapped to byte array | ||
| // mapped to String as a workaround | ||
| typeMapping.put("binary", "String"); | ||
| typeMapping.put("ByteArray", "String"); | ||
|
|
||
| instantiationTypes.put("array", "ListBuffer"); | ||
| instantiationTypes.put("map", "HashMap"); | ||
|
|
||
| cliOptions.add(new CliOption(CodegenConstants.MODEL_PROPERTY_NAMING, | ||
| CodegenConstants.MODEL_PROPERTY_NAMING_DESC).defaultValue("camelCase")); | ||
| } | ||
|
|
||
| @Override | ||
| public void processOpts() { | ||
| super.processOpts(); | ||
|
|
||
| if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) { | ||
| setModelPropertyNaming( | ||
| (String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING)); | ||
| } | ||
| } | ||
|
|
||
| public void setModelPropertyNaming(String naming) { | ||
| if ("original".equals(naming) || "camelCase".equals(naming) || | ||
| "PascalCase".equals(naming) || "snake_case".equals(naming)) { | ||
| this.modelPropertyNaming = naming; | ||
| } else { | ||
| throw new IllegalArgumentException("Invalid model property naming '" + | ||
| naming + "'. Must be 'original', 'camelCase', " + | ||
| "'PascalCase' or 'snake_case'"); | ||
| } | ||
| } | ||
|
|
||
| public String getModelPropertyNaming() { | ||
| return this.modelPropertyNaming; | ||
| } | ||
|
|
||
| @Override | ||
| public String toVarName(String name) { | ||
| // sanitize name | ||
| name = sanitizeName( | ||
| name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. | ||
|
|
||
| if ("_".equals(name)) { | ||
| name = "_u"; | ||
| } | ||
|
|
||
| // if it's all uppper case, do nothing | ||
| if (name.matches("^[A-Z_]*$")) { | ||
| return name; | ||
| } | ||
|
|
||
| name = getNameUsingModelPropertyNaming(name); | ||
|
|
||
| // for reserved word or word starting with number, append _ | ||
| if (isReservedWord(name) || name.matches("^\\d.*")) { | ||
| name = escapeReservedWord(name); | ||
| } | ||
|
|
||
| return name; | ||
| } | ||
|
|
||
| @Override | ||
| public String toParamName(String name) { | ||
| // should be the same as variable name | ||
| return toVarName(name); | ||
| } | ||
|
|
||
| private String getNameUsingModelPropertyNaming(String name) { | ||
| switch (CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.valueOf(getModelPropertyNaming())) { | ||
| case original: | ||
| return name; | ||
| case camelCase: | ||
| return camelize(name, true); | ||
| case PascalCase: | ||
| return camelize(name); | ||
| case snake_case: | ||
| return underscore(name); | ||
| default: | ||
| throw new IllegalArgumentException("Invalid model property naming '" + | ||
| name + "'. Must be 'original', 'camelCase', " + | ||
| "'PascalCase' or 'snake_case'"); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| @Override | ||
| public CodegenType getTag() { | ||
| return CodegenType.SERVER; | ||
| } | ||
|
|
||
| @Override | ||
| public String getName() { | ||
| return "scala-lagomApi"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll change this to |
||
| } | ||
|
|
||
| @Override | ||
| public String getHelp() { | ||
| return "Generates a Lagom API in scala"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll add the word "beta" to the description. |
||
| } | ||
|
|
||
| @Override | ||
| public String toOperationId(String operationId) { | ||
| // throw exception if method name is empty | ||
| if (StringUtils.isEmpty(operationId)) { | ||
| throw new RuntimeException("Empty method name (operationId) not allowed"); | ||
| } | ||
|
|
||
| // method name cannot use reserved keyword, e.g. return | ||
| if (isReservedWord(operationId)) { | ||
| throw new RuntimeException(operationId + " (reserved word) cannot be used as method name"); | ||
| } | ||
|
|
||
| return camelize(operationId, true); | ||
| } | ||
|
|
||
| @Override | ||
| public String toModelName(final String name) { | ||
| final String sanitizedName = sanitizeName(modelNamePrefix + name + modelNameSuffix); | ||
|
|
||
| // camelize the model name | ||
| // phone_number => PhoneNumber | ||
| final String camelizedName = camelize(sanitizedName); | ||
|
|
||
| // model name cannot use reserved keyword, e.g. return | ||
| if (isReservedWord(camelizedName)) { | ||
| final String modelName = "Model" + camelizedName; | ||
| LOGGER.warn( | ||
| camelizedName + " (reserved word) cannot be used as model name. Renamed to " + modelName); | ||
| return modelName; | ||
| } | ||
|
|
||
| // model name starts with number | ||
| if (name.matches("^\\d.*")) { | ||
| final String modelName = | ||
| "Model" + camelizedName; // e.g. 200Response => Model200Response (after camelize) | ||
| LOGGER.warn( | ||
| name + " (model name starts with number) cannot be used as model name. Renamed to " | ||
| + modelName); | ||
| return modelName; | ||
| } | ||
|
|
||
| return camelizedName; | ||
| } | ||
|
|
||
| @Override | ||
| public String escapeQuotationMark(String input) { | ||
| // remove " to avoid code injection | ||
| return input.replace("\"", ""); | ||
| } | ||
|
|
||
| @Override | ||
| public Map<String, Object> postProcessModelsEnum(Map<String, Object> objs) { | ||
| objs = super.postProcessModelsEnum(objs); | ||
| List<Object> models = (List<Object>) objs.get("models"); | ||
| for (Object _mo : models) { | ||
| Map<String, Object> mo = (Map<String, Object>) _mo; | ||
| CodegenModel cm = (CodegenModel) mo.get("model"); | ||
|
|
||
| for (CodegenProperty var : cm.vars) { | ||
| if (var.isEnum) { | ||
| List<Object> enumValues = (List<Object>) var.allowableValues.get("values"); | ||
|
|
||
| for (final ListIterator<Object> i = enumValues.listIterator(); i.hasNext(); ) { | ||
| final String element = String.valueOf(i.next()); | ||
| i.set(element.replaceAll("^\"|\"$", "")); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| //Needed import for Gson based libraries | ||
| if (additionalProperties.containsKey("gson")) { | ||
| List<Map<String, String>> imports = (List<Map<String, String>>) objs.get("imports"); | ||
|
|
||
| for (Object _mo : models) { | ||
| Map<String, Object> mo = (Map<String, Object>) _mo; | ||
| CodegenModel cm = (CodegenModel) mo.get("model"); | ||
| // for enum model | ||
| if (Boolean.TRUE.equals(cm.isEnum) && cm.allowableValues != null) { | ||
| cm.imports.add(importMapping.get("SerializedName")); | ||
| Map<String, String> item = new HashMap<String, String>(); | ||
| item.put("import", importMapping.get("SerializedName")); | ||
| imports.add(item); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return objs; | ||
| } | ||
|
|
||
| @Override | ||
| public Map<String, Object> postProcessOperations(Map<String, Object> objs) { | ||
| Map<String, Object> operations = (Map<String, Object>) objs.get("operations"); | ||
| ArrayList<CodegenOperation> oplist = (ArrayList<CodegenOperation>) operations.get("operation"); | ||
|
|
||
| for (CodegenOperation codegenOperation : oplist) { | ||
| String path = codegenOperation.path; | ||
| codegenOperation.path = path.replaceAll("\\{", ":").replaceAll("}", ""); | ||
| } | ||
| return objs; | ||
| } | ||
|
|
||
|
|
||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
modules/swagger-codegen/src/main/resources/lagomScalaApi/README.mustache
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # Swagger generated scala-lagomApi | ||
|
|
||
| ## Overview | ||
| This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the | ||
| [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 lagon-api. | ||
|
|
||
| This example uses the [lagomframework](https://www.lagomframework.com) lagomframework. | ||
|
|
||
| # Features which are not supported | ||
| - Form Parameters | ||
| - Seq in query parameters | ||
| - File Handling |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a copy/paste. Can it be updated/fixed here, or does that affect something elsewhere?
Then, use
modifiedNamewithin the method?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jim, you are right it is a copy paste from ScalaClientCodegen.Java. I guess this is needed for scala variables. Not sure what will break. Hence retained the logic.
With reference to method signatures.
Please have a look at https://github.com/lagom/sbt-lagom-descriptor-generator
The method signature generated by this PR matches with the one generated in above project.
Please let me know if you find any gaps.