Skip to content
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

[KOTLIN] Spring Boot Server Generator #820

Merged
merged 39 commits into from
Aug 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4f98b30
Kotlin Spring initial bootstrap
Aug 7, 2018
b2be108
Basic configuration construction for Kotlin Spring
Aug 7, 2018
3013f09
Wired up with comand line client
dr4ke616 Aug 8, 2018
2644e0d
Initial kotlin spring boot application generated using gradle kotlin-dsl
dr4ke616 Aug 8, 2018
aa001f9
Added basic support for generating models
dr4ke616 Aug 9, 2018
cfca803
Basic controllers generated without endpoints generated
dr4ke616 Aug 9, 2018
bf549e8
Basic spring boot app generated with models and controllers
dr4ke616 Aug 10, 2018
9b27b3c
Added fix for type mapping in AbstractKotlinCodegen. Originally it wa…
dr4ke616 Aug 10, 2018
d04d7db
Fixed return type mapping
dr4ke616 Aug 10, 2018
0dd39a9
Sorted bash springboot petstore generator script
dr4ke616 Aug 10, 2018
ecc14aa
Implemented toVarName in AbstractKotlinCodegen to better handle some …
dr4ke616 Aug 10, 2018
f1179da
Checking for reserved words or numerical starting class names in Abst…
dr4ke616 Aug 10, 2018
6dd4614
Implemented toOperationId in AbstractKotlinCodegen
dr4ke616 Aug 10, 2018
d536bf2
Fixed types that were not correctly being mapped to primitives (byte …
dr4ke616 Aug 13, 2018
557561a
Escaping dollar symbols in function names
dr4ke616 Aug 13, 2018
d692efd
Added support for outter enum classes
dr4ke616 Aug 13, 2018
6e4a4fe
Added basic support for generating services
dr4ke616 Aug 14, 2018
4d63f29
Removed option for generated config package. Added option to enable/d…
dr4ke616 Aug 14, 2018
0eee424
Added configuration option to generate gradle. Generated maven pom.xm…
dr4ke616 Aug 15, 2018
9ca036a
Fixed up bash scripts for generating test sample code
dr4ke616 Aug 15, 2018
a0ea9e1
Added configurable option for Swagger Annotations
dr4ke616 Aug 15, 2018
7053851
Added configurable option for generating service interfaces and servi…
dr4ke616 Aug 15, 2018
2edf44b
Added README generation
dr4ke616 Aug 15, 2018
92326f9
Enable optional bean validation
dr4ke616 Aug 16, 2018
b9f8c5b
Added kotlin spring sample to CircleCI pom.xml
dr4ke616 Aug 16, 2018
3119585
Removed kotlin spring boot from .gitignore
dr4ke616 Aug 20, 2018
7fbf228
Minor fixes from PR comments for user submission (#1)
jimschubert Aug 20, 2018
91b9aaa
List of changes based upon code review:
dr4ke616 Aug 20, 2018
8192ad3
Updated samples
dr4ke616 Aug 20, 2018
d8d3586
Generating ConstraintViolation Exception Handler, as Springboot doesn…
dr4ke616 Aug 20, 2018
d673aba
Small fix for date time mappings (plus sample re-gen)
dr4ke616 Aug 21, 2018
9b7d5c5
Minor fix in README template, where port was using wrong variable
dr4ke616 Aug 21, 2018
61ceff3
Fix missing jackson-dataformat-xml dependency
dr4ke616 Aug 21, 2018
dd22c90
Fix build - needed to re-run kotlin-server-petstore.sh
dr4ke616 Aug 21, 2018
23fb51d
Fixes after merge with master
dr4ke616 Aug 22, 2018
ddec3d2
Revert "Small fix for date time mappings (plus sample re-gen)"
dr4ke616 Aug 22, 2018
3c47c59
Moved type mappings to Kotlin Spring generator
dr4ke616 Aug 22, 2018
521bbbf
Regenerated samples
dr4ke616 Aug 22, 2018
921edb5
Regenerated samples
dr4ke616 Aug 22, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,14 @@ samples/client/petstore/swift/**/SwaggerClientTests/SwaggerClient.xcworkspace/xc
samples/client/petstore/swift/**/SwaggerClientTests/Pods/
#samples/client/petstore/swift/**/SwaggerClientTests/Pods/Pods.xcodeproj/xcuserdata
#samples/client/petstore/swift/**/SwaggerClientTests/Pods/Pods.xcodeproj/xcshareddata/xcschemes
samples/client/petstore/swift/**/SwaggerClientTests/Podfile.lock
samples/client/petstore/swift/**/SwaggerClientTests/Podfile.lock
# Swift3
samples/client/petstore/swift3/**/SwaggerClientTests/SwaggerClient.xcodeproj/xcuserdata
samples/client/petstore/swift3/**/SwaggerClientTests/SwaggerClient.xcworkspace/xcuserdata
#samples/client/petstore/swift3/**/SwaggerClientTests/Pods/
#samples/client/petstore/swift3/**/SwaggerClientTests/Pods/Pods.xcodeproj/xcuserdata
#samples/client/petstore/swift3/**/SwaggerClientTests/Pods/Pods.xcodeproj/xcshareddata/xcschemes
samples/client/petstore/swift3/**/SwaggerClientTests/Podfile.lock
samples/client/petstore/swift3/**/SwaggerClientTests/Podfile.lock

# C#
*.csproj.user
Expand Down
1 change: 1 addition & 0 deletions CI/pom.xml.circleci
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,7 @@
<module>samples/server/petstore/scala-lagom-server</module>
<module>samples/server/petstore/scalatra</module>
<module>samples/server/petstore/finch</module>
<module>samples/server/petstore/kotlin-springboot</module>
</modules>
</profile>
</profiles>
Expand Down
35 changes: 35 additions & 0 deletions bin/kotlin-springboot-petstore-server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/sh

SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"

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/openapi-generator-cli/target/openapi-generator-cli.jar"

if [ ! -f "$executable" ]
then
mvn clean package
fi

export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="$@ generate -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -t modules/openapi-generator/src/main/resources/kotlin-spring -g kotlin-spring -o samples/server/petstore/kotlin-springboot --additional-properties=library=spring-boot,beanValidations=true,swaggerAnnotations=true,serviceImplementation=true"

echo "Cleaning previously generated files if any from samples/server/petstore/kotlin-springboot"
rm -rf samples/server/petstore/kotlin-springboot

echo "Generating Kotling Spring Boot server..."
java $JAVA_OPTS -jar $executable $ags
35 changes: 35 additions & 0 deletions bin/openapi3/kotlin-springboot-petstore-server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/sh

SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"

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/openapi-generator-cli/target/openapi-generator-cli.jar"

if [ ! -f "$executable" ]
then
mvn clean package
fi

export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="$@ generate -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -t modules/openapi-generator/src/main/resources/kotlin-spring -g kotlin-spring -o samples/server/openapi3/petstore/kotlin-springboot --additional-properties=library=spring-boot,beanValidations=true,swaggerAnnotations=true,serviceImplementation=true"

echo "Cleaning previously generated files if any from samples/server/openapi3/petstore/kotlin-springboot"
rm -rf samples/server/openapi3/petstore/kotlin-springboot

echo "Generating Kotling Spring Boot server..."
java $JAVA_OPTS -jar $executable $ags
10 changes: 10 additions & 0 deletions bin/windows/kotlin-springboot-petstore-server.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
set executable=.\modules\openapi-generator-cli\target\openapi-generator-cli.jar

If Not Exist %executable% (
mvn clean package
)

REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g kotlin-spring -o samples\server\petstore\kotlin-springboot --additional-properties=library=spring-boot

java %JAVA_OPTS% -jar %executable% %ags%
10 changes: 10 additions & 0 deletions bin/windows/openapi3/kotlin-springboot-petstore-server.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
set executable=.\modules\openapi-generator-cli\target\openapi-generator-cli.jar

If Not Exist %executable% (
mvn clean package
)

REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M
set ags=generate -i modules\openapi-generator\src\test\resources\3_0\petstore.yaml -g kotlin-spring -o samples\server\openapi3\petstore\kotlin-springboot --additional-properties=library=spring-boot

java %JAVA_OPTS% -jar %executable% %ags%
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.CodegenConstants;
Expand All @@ -28,11 +29,7 @@
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.*;

public abstract class AbstractKotlinCodegen extends DefaultCodegen implements CodegenConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractKotlinCodegen.class);
Expand All @@ -55,6 +52,7 @@ public AbstractKotlinCodegen() {

languageSpecificPrimitives = new HashSet<String>(Arrays.asList(
"kotlin.Byte",
"kotlin.ByteArray",
"kotlin.Short",
"kotlin.Int",
"kotlin.Long",
Expand Down Expand Up @@ -139,6 +137,7 @@ public AbstractKotlinCodegen() {

defaultIncludes = new HashSet<String>(Arrays.asList(
"kotlin.Byte",
"kotlin.ByteArray",
"kotlin.Short",
"kotlin.Int",
"kotlin.Long",
Expand All @@ -159,21 +158,22 @@ public AbstractKotlinCodegen() {
typeMapping.put("float", "kotlin.Float");
typeMapping.put("long", "kotlin.Long");
typeMapping.put("double", "kotlin.Double");
typeMapping.put("ByteArray", "kotlin.ByteArray");
typeMapping.put("number", "java.math.BigDecimal");
typeMapping.put("date-time", "java.time.LocalDateTime");
typeMapping.put("date", "java.time.LocalDateTime");
typeMapping.put("file", "java.io.File");
typeMapping.put("array", "kotlin.Array");
typeMapping.put("list", "kotlin.Array");
typeMapping.put("list", "kotlin.collections.List");
typeMapping.put("map", "kotlin.collections.Map");
typeMapping.put("object", "kotlin.Any");
typeMapping.put("binary", "kotlin.Array<kotlin.Byte>");
typeMapping.put("Date", "java.time.LocalDateTime");
typeMapping.put("DateTime", "java.time.LocalDateTime");

instantiationTypes.put("array", "arrayOf");
instantiationTypes.put("list", "arrayOf");
instantiationTypes.put("map", "mapOf");
instantiationTypes.put("array", "kotlin.arrayOf");
instantiationTypes.put("list", "kotlin.arrayOf");
instantiationTypes.put("map", "kotlin.mapOf");

importMapping = new HashMap<String, String>();
importMapping.put("BigDecimal", "java.math.BigDecimal");
Expand Down Expand Up @@ -473,13 +473,53 @@ public String toModelName(final String name) {
// Camelize name of nested properties
modifiedName = camelize(modifiedName);

if (reservedWords.contains(modifiedName)) {
modifiedName = escapeReservedWord(modifiedName);
// model name cannot use reserved keyword, e.g. return
if (isReservedWord(modifiedName)) {
final String modelName = "Model" + modifiedName;
LOGGER.warn(modifiedName + " (reserved word) cannot be used as model name. Renamed to " + modelName);
return modelName;
}

// model name starts with number
if (modifiedName.matches("^\\d.*")) {
final String modelName = "Model" + modifiedName; // 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 titleCase(modifiedName);
}

/**
* Return the operation ID (method name)
*
* @param operationId operation ID
* @return the sanitized method name
*/
@Override
public String toOperationId(String operationId) {
// throw exception if method name is empty
if (StringUtils.isEmpty(operationId))
throw new RuntimeException("Empty method/operation name (operationId) not allowed");

operationId = camelize(sanitizeName(operationId), true);

// method name cannot use reserved keyword, e.g. return
if (isReservedWord(operationId)) {
String newOperationId = camelize("call_" + operationId, true);
LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + newOperationId);
return newOperationId;
}

// operationId starts with a number
if (operationId.matches("^\\d.*")) {
LOGGER.warn(operationId + " (starting with a number) cannot be used as method sname. Renamed to " + camelize("call_" + operationId), true);
operationId = camelize("call_" + operationId, true);
}

return operationId;
}

@Override
public String toModelFilename(String name) {
// Should be the same as the model name
Expand Down Expand Up @@ -577,4 +617,70 @@ public String toEnumValue(String value, String datatype) {
public boolean isDataTypeString(final String dataType) {
return "String".equals(dataType) || "kotlin.String".equals(dataType);
}

@Override
public String toParamName(String name) {
// to avoid conflicts with 'callback' parameter for async call
if ("callback".equals(name)) {
return "paramCallback";
}

// should be the same as variable name
return toVarName(name);
}

@Override
public String toVarName(String name) {
// sanitize name
name = sanitizeName(name, "\\W-[\\$]");

if (name.toLowerCase(Locale.ROOT).matches("^_*class$")) {
return "propertyClass";
}

if ("_".equals(name)) {
name = "_u";
}

// if it's all uppper case, do nothing
if (name.matches("^[A-Z_]*$")) {
return name;
}

if (startsWithTwoUppercaseLetters(name)) {
name = name.substring(0, 2).toLowerCase(Locale.ROOT) + name.substring(2);
}

// If name contains special chars -> replace them.
if ((name.chars().anyMatch(character -> specialCharReplacements.keySet().contains("" + ((char) character))))) {
List<String> allowedCharacters = new ArrayList<>();
allowedCharacters.add("_");
allowedCharacters.add("$");
name = escapeSpecialCharacters(name, allowedCharacters, "_");
}

// camelize (lower first character) the variable name
// pet_id => petId
name = camelize(name, true);

// for reserved word or word starting with number or containing dollar symbol, escape it
if (isReservedWord(name) || name.matches("(^\\d.*)|(.*[$].*)")) {
name = escapeReservedWord(name);
}

return name;
}

@Override
public String toRegularExpression(String pattern) {
return escapeText(pattern);
}

private boolean startsWithTwoUppercaseLetters(String name) {
boolean startsWithTwoUppercaseLetters = false;
if (name.length() > 1) {
startsWithTwoUppercaseLetters = name.substring(0, 2).equals(name.substring(0, 2).toUpperCase(Locale.ROOT));
}
return startsWithTwoUppercaseLetters;
}
}
Loading