From 59dad78413ab49c9768eed08eeb0569dd953ad36 Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Tue, 16 Feb 2021 10:40:49 -0500 Subject: [PATCH 1/7] fixes #528 complete the graphql, hybrid and openapi --- codegen-cli/pom.xml | 9 +- .../main/java/com/networknt/codegen/Cli.java | 68 +- .../codegen}/YAMLFileParameterizer.java | 49 +- .../java/com/networknt/codegen/CliTest.java | 21 + .../codegen}/YAMLFileParameterizerTest.java | 45 +- codegen-cli/src/test/resources/openapi.json | 304 ++++++ codegen-cli/src/test/resources/openapi.yaml | 187 ++++ codegen-core/pom.xml | 8 +- .../java/com/networknt/codegen/Generator.java | 324 +++++- .../networknt/codegen/JsonDefaultTest.java | 17 + .../com/networknt/codegen/JsonIterTest.java | 102 -- .../src/test/resources/config/service.yml | 8 +- .../handler/CodegenMultipleHandler.java | 41 +- .../codegen/handler/CodegenSingleHandler.java | 36 +- .../codegen/graphql/GraphqlGenerator.java | 146 ++- .../templates/graphql/dockerfile.rocker.raw | 6 +- .../graphql/dockerfileslim.rocker.raw | 6 +- .../templates/graphql/handlerYml.rocker.raw | 1 - .../templates/graphql/pom.xml.rocker.raw | 32 +- .../templates/graphql/serviceYml.rocker.raw | 10 +- .../codegen/graphql/GraphqlGeneratorTest.java | 13 +- .../src/test/resources/config.json | 2 + light-hybrid-4j/pom.xml | 8 +- .../codegen/hybrid/HybridGenerator.java | 19 + .../codegen/hybrid/HybridServerGenerator.java | 71 +- .../hybrid/HybridServiceGenerator.java | 93 +- .../templates/hybrid/handler.rocker.raw | 8 +- .../templates/hybrid/handlerTest.rocker.raw | 10 +- .../templates/hybrid/handlerYml.rocker.raw | 11 +- .../hybrid/server/dockerfile.rocker.raw | 6 +- .../hybrid/server/dockerfileslim.rocker.raw | 6 +- .../hybrid/server/pom.xml.rocker.raw | 46 +- .../hybrid/service/pom.xml.rocker.raw | 46 +- .../templates/hybrid/serviceYml.rocker.raw | 20 +- .../hybrid/HybridServerGeneratorTest.java | 11 +- .../hybrid/HybridServiceGeneratorTest.java | 14 +- light-rest-4j/pom.xml | 12 +- .../codegen/rest/OpenApiGenerator.java | 567 +++++----- .../codegen/rest/OpenApiKotlinGenerator.java | 568 +--------- .../codegen/rest/OpenApiLambdaGenerator.java | 983 +----------------- .../codegen/rest/OpenApiSpecGenerator.java | 188 +--- .../templates/lambda/buildGradle.rocker.raw | 6 +- .../templates/lambda/pom.xml.rocker.raw | 10 +- .../lambda/proxy/handlerYml.rocker.raw | 1 - .../lambda/proxy/lambdaInvokerYml.rocker.raw | 1 - .../templates/rest/dockerfile.rocker.raw | 6 +- .../templates/rest/dockerfileslim.rocker.raw | 6 +- .../templates/rest/enumInline.rocker.raw | 7 +- .../templates/rest/handlerTest.rocker.raw | 3 +- .../resources/templates/rest/model.rocker.raw | 4 +- .../templates/rest/openapi/config.rocker.raw | 6 +- .../rest/openapi/handlerTest.rocker.raw | 1 - .../rest/openapi/handlerYml.rocker.raw | 11 +- .../templates/rest/openapi/pom.xml.rocker.raw | 48 +- .../rest/openapi/service.yml.rocker.raw | 20 +- .../resources/templates/rest/pojo.rocker.raw | 8 +- .../templates/rest/project.rocker.raw | 6 +- .../restkotlin/buildGradleKts.rocker.raw | 14 +- .../restkotlin/dockerfile.rocker.raw | 6 +- .../restkotlin/dockerfileslim.rocker.raw | 6 +- .../restkotlin/enumInline.rocker.raw | 5 +- .../restkotlin/gradleProperties.rocker.raw | 14 +- .../restkotlin/handlerTest.rocker.raw | 1 - .../templates/restkotlin/model.rocker.raw | 3 +- .../restkotlin/openapi/handlerYml.rocker.raw | 1 - .../restkotlin/openapi/service.yml.rocker.raw | 8 +- .../templates/restkotlin/pojo.rocker.raw | 3 +- .../restkotlin/settingsGradleKts.rocker.raw | 6 +- .../com/networknt/codegen/ConfigTest.java | 21 +- .../OpenApiArrayReferenceGeneratorTest.java | 10 +- .../codegen/OpenApiGeneratorTest.java | 58 +- .../codegen/OpenApiKotlinGeneratorTest.java | 29 +- .../codegen/OpenApiLambdaGeneratorTest.java | 32 +- .../rest/OpenApiSpecGeneratorTest.java | 8 +- light-rest-4j/src/test/resources/config.json | 1 + light-rest-4j/src/test/resources/config.yaml | 26 + pom.xml | 30 +- 77 files changed, 1812 insertions(+), 2736 deletions(-) rename {light-rest-4j/src/main/java/com/networknt/codegen/rest => codegen-cli/src/main/java/com/networknt/codegen}/YAMLFileParameterizer.java (92%) rename {light-rest-4j/src/test/java/com/networknt/codegen/rest => codegen-cli/src/test/java/com/networknt/codegen}/YAMLFileParameterizerTest.java (74%) create mode 100644 codegen-cli/src/test/resources/openapi.json create mode 100644 codegen-cli/src/test/resources/openapi.yaml create mode 100644 codegen-core/src/test/java/com/networknt/codegen/JsonDefaultTest.java delete mode 100644 codegen-core/src/test/java/com/networknt/codegen/JsonIterTest.java create mode 100644 light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridGenerator.java create mode 100644 light-rest-4j/src/test/resources/config.yaml diff --git a/codegen-cli/pom.xml b/codegen-cli/pom.xml index 4d27fd54a..5310002c8 100644 --- a/codegen-cli/pom.xml +++ b/codegen-cli/pom.xml @@ -50,14 +50,13 @@ com.networknt light-graphql-4j-generator - - com.jsoniter - jsoniter + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml - org.javassist - javassist + com.fasterxml.jackson.core + jackson-databind org.slf4j diff --git a/codegen-cli/src/main/java/com/networknt/codegen/Cli.java b/codegen-cli/src/main/java/com/networknt/codegen/Cli.java index 18d66a46d..c4320ebfa 100644 --- a/codegen-cli/src/main/java/com/networknt/codegen/Cli.java +++ b/codegen-cli/src/main/java/com/networknt/codegen/Cli.java @@ -3,15 +3,20 @@ import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fizzed.rocker.runtime.RockerRuntime; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; -import com.networknt.codegen.rest.YAMLFileParameterizer; +import com.networknt.utility.NioUtils; +import org.apache.commons.lang3.StringUtils; import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Map; import java.util.Set; import static java.io.File.separator; @@ -19,6 +24,12 @@ * Created by steve on 24/04/17. */ public class Cli { + private static final String JSON="json"; + private static final String YAML="yaml"; + private static final String YML="yml"; + private static final String GRAPHQLS = "graphqls"; + + private ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); @Parameter(names={"--framework", "-f"}, required = true, description = "The framework template to be used as scaffolding to the generated code.") @@ -75,37 +86,58 @@ public void run(JCommander jCommander) throws Exception { Set frameworks = registry.getFrameworks(); if(frameworks.contains(framework)) { Generator generator = registry.getGenerator(framework); - Object anyModel = null; + Object modelNode = null; // model can be empty in some cases. if(model != null) { - // check if model is json or not before loading. - if(model.endsWith("json")) { - if(Utils.isUrl(model)) { - anyModel = JsonIterator.deserialize(Utils.urlToByteArray(new URL(model))); + String ext = NioUtils.getFileExtension(model); + if(StringUtils.equalsIgnoreCase(ext, JSON)) { + if (Utils.isUrl(model)) { + modelNode = Generator.jsonMapper.readTree(Utils.urlToByteArray(new URL(model))); } else { - anyModel = JsonIterator.deserialize(Files.readAllBytes(Paths.get(model))); + modelNode = Generator.jsonMapper.readTree(Files.readAllBytes(Paths.get(model))); } - } else { + } else if(StringUtils.equalsIgnoreCase(ext, YML) || StringUtils.equalsIgnoreCase(ext, YAML)) { + if (Utils.isUrl(model)) { + modelNode = Generator.yamlMapper.readTree(Utils.urlToByteArray(new URL(model))); + } else { + modelNode = Generator.yamlMapper.readTree(Files.readAllBytes(Paths.get(model))); + } + } else if(StringUtils.equalsIgnoreCase(ext, GRAPHQLS)) { if(Utils.isUrl(model)) { - anyModel = new String(Utils.urlToByteArray(new URL(model)), StandardCharsets.UTF_8); + modelNode = new String(Utils.urlToByteArray(new URL(model)), StandardCharsets.UTF_8); } else { - anyModel = new String(Files.readAllBytes(Paths.get(model)), StandardCharsets.UTF_8); + modelNode = new String(Files.readAllBytes(Paths.get(model)), StandardCharsets.UTF_8); } + } else { + throw new UnsupportedOperationException("Unknown model file format " + ext); } } - Any anyConfig = null; + JsonNode configNode = null; if(config != null) { - if(Utils.isUrl(config)) { - anyConfig = JsonIterator.deserialize(Utils.urlToByteArray(new URL(config))); + String ext = NioUtils.getFileExtension(config); + if (StringUtils.equalsIgnoreCase(ext, JSON)) { + if (Utils.isUrl(config)) { + configNode = Generator.jsonMapper.readTree(Utils.urlToByteArray(new URL(config))); + } else { + configNode = Generator.jsonMapper.readTree(Files.readAllBytes(Paths.get(config))); + } + } else if (StringUtils.equalsIgnoreCase(ext, YML)||StringUtils.equalsIgnoreCase(ext, YAML)) { + if (Utils.isUrl(config)) { + configNode = Generator.yamlMapper.readTree(Utils.urlToByteArray(new URL(config))); + } else { + configNode = Generator.yamlMapper.readTree(Files.readAllBytes(Paths.get(config))); + } } else { - anyConfig = JsonIterator.deserialize(Files.readAllBytes(Paths.get(config))); + throw new UnsupportedOperationException("Unknow file format " + ext); } + } if(parameterizationDir != null) { - YAMLFileParameterizer.rewriteAll(parameterizationDir, output + separator + YAMLFileParameterizer.DEFAULT_DEST_DIR, anyConfig.asMap().get(YAMLFileParameterizer.GENERATE_ENV_VARS).asMap()); + Map map = Generator.jsonMapper.convertValue(configNode.get(YAMLFileParameterizer.GENERATE_ENV_VARS), new TypeReference>(){}); + YAMLFileParameterizer.rewriteAll(parameterizationDir, output + separator + YAMLFileParameterizer.DEFAULT_DEST_DIR, map); } - generator.generate(output, anyModel, anyConfig); + generator.generate(output, modelNode, configNode); System.out.println("A project has been generated successfully in " + output + " folder. Have fun!!!"); } else { System.out.printf("Invalid framework: %s\navaliable frameworks:\n", framework); diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/YAMLFileParameterizer.java b/codegen-cli/src/main/java/com/networknt/codegen/YAMLFileParameterizer.java similarity index 92% rename from light-rest-4j/src/main/java/com/networknt/codegen/rest/YAMLFileParameterizer.java rename to codegen-cli/src/main/java/com/networknt/codegen/YAMLFileParameterizer.java index cbb7f4cf6..8ef29ef51 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/YAMLFileParameterizer.java +++ b/codegen-cli/src/main/java/com/networknt/codegen/YAMLFileParameterizer.java @@ -1,18 +1,10 @@ -package com.networknt.codegen.rest; +package com.networknt.codegen; import java.io.*; import java.net.URL; import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.stream.Collectors; @@ -32,7 +24,6 @@ import org.yaml.snakeyaml.reader.StreamReader; import org.yaml.snakeyaml.resolver.Resolver; -import com.jsoniter.any.Any; import static java.nio.charset.StandardCharsets.UTF_8; @@ -60,16 +51,16 @@ public class YAMLFileParameterizer { * @param dir - file dir * @param generateEnvVars - config */ - public static void rewriteAll(String dir, Map generateEnvVars) { + public static void rewriteAll(String dir, Map generateEnvVars) { if (logger.isDebugEnabled()) { logger.debug("rewriting files in {}", dir); } - generateEnvVars.put(KEY_IN_PLACE, Any.wrap(true)); + generateEnvVars.put(KEY_IN_PLACE, true); rewriteFiles(new File(dir), new File(dir), generateEnvVars); } - public static void rewriteAll(String srcLocation, String destDir, Map generateEnvVars) { + public static void rewriteAll(String srcLocation, String destDir, Map generateEnvVars) { if (logger.isDebugEnabled()) { logger.debug("rewriting files in {}", srcLocation); @@ -82,7 +73,7 @@ public static void rewriteAll(String srcLocation, String destDir, Map generateEnvVars) { + public static void rewriteFiles(File sourceDir, File destDir, Map generateEnvVars) { if (!sourceDir.exists() || !sourceDir.isDirectory()) { logger.error("{} does not exist or is not a folder.", sourceDir); return; @@ -111,7 +102,7 @@ public static void rewriteFiles(File sourceDir, File destDir, Map g } } - public static void rewriteResources(String resourceLocation, String destDir, Map generateEnvVars) { + public static void rewriteResources(String resourceLocation, String destDir, Map generateEnvVars) { if (StringUtils.isBlank(resourceLocation)) { return; } @@ -180,7 +171,7 @@ public static void copyResources(String resourceLocation, String destDir) { } } - public static void rewriteResource(String filename, String resourceLocation, String destFilename, Map generateEnvVars) { + public static void rewriteResource(String filename, String resourceLocation, String destFilename, Map generateEnvVars) { if (logger.isDebugEnabled()) { logger.debug("rewriting resource {}", resourceLocation); } @@ -197,7 +188,7 @@ public static void rewriteResource(String filename, String resourceLocation, Str } } - public static void rewriteFile(File srcFile, File destFile, Map generateEnvVars) { + public static void rewriteFile(File srcFile, File destFile, Map generateEnvVars) { if (logger.isDebugEnabled()) { logger.debug("rewriting file {}", srcFile.getAbsolutePath()); } @@ -260,9 +251,9 @@ protected static String resolveLocation(String location) { return location; } - protected static boolean getValue(Map generateEnvVars, String key) { + protected static boolean getValue(Map generateEnvVars, String key) { if (generateEnvVars.containsKey(key)) { - return generateEnvVars.get(key).toBoolean(); + return (Boolean)generateEnvVars.get(key); } return false; @@ -391,7 +382,7 @@ protected static List readFile(File file) { return lines; } - protected static void parameterize(String filename, Node document, List srclines, File destFile, Map generateEnvVars) { + protected static void parameterize(String filename, Node document, List srclines, File destFile, Map generateEnvVars) { try (Writer writer = Files.newBufferedWriter(destFile.toPath(), UTF_8)) { if (document instanceof MappingNode) { List destlines = parameterize(filename, srclines, (MappingNode) document, generateEnvVars); @@ -407,7 +398,7 @@ protected static void parameterize(String filename, Node document, List } } - protected static List parameterize(String filename, List srclines, MappingNode node, Map generateEnvVars) { + protected static List parameterize(String filename, List srclines, MappingNode node, Map generateEnvVars) { List destlines = new ArrayList<>(); List tuples = node.getValue(); @@ -490,33 +481,31 @@ protected static String stripExtension (String str) { return str.substring(0, pos); } - protected static Set buildFileExcludeSet(String sourceDir, Map generateEnvVars) { + protected static Set buildFileExcludeSet(String sourceDir, Map generateEnvVars) { if (generateEnvVars.containsKey(KEY_EXCLUDE)) { - return buildFileExcludeSet(sourceDir, generateEnvVars.get(KEY_EXCLUDE).asList()); + return buildFileExcludeSet(sourceDir, (List)generateEnvVars.get(KEY_EXCLUDE)); } return Collections.emptySet(); } - protected static Set buildFileExcludeSet(String sourceDir, Collection excludes) { + protected static Set buildFileExcludeSet(String sourceDir, Collection excludes) { return excludes.stream() - .map(Any::toString) .filter(StringUtils::isNotBlank) .map(s->sourceDir+normalizeFilename(s)) .collect(Collectors.toSet()); } - protected static Set buildResourceExcludeSet(String resourceLocation, Map generateEnvVars) { + protected static Set buildResourceExcludeSet(String resourceLocation, Map generateEnvVars) { if (generateEnvVars.containsKey(KEY_EXCLUDE)) { - return buildResourceExcludeSet(resourceLocation, generateEnvVars.get(KEY_EXCLUDE).asList()); + return buildResourceExcludeSet(resourceLocation, (List)generateEnvVars.get(KEY_EXCLUDE)); } return Collections.emptySet(); } - protected static Set buildResourceExcludeSet(String resourceLocation, Collection excludes) { + protected static Set buildResourceExcludeSet(String resourceLocation, Collection excludes) { return excludes.stream() - .map(Any::toString) .filter(StringUtils::isNotBlank) .map(s->toNonNullString(getResourceURL(resourceLocation+s))) .collect(Collectors.toSet()); diff --git a/codegen-cli/src/test/java/com/networknt/codegen/CliTest.java b/codegen-cli/src/test/java/com/networknt/codegen/CliTest.java index aad3a0134..90049dc7b 100644 --- a/codegen-cli/src/test/java/com/networknt/codegen/CliTest.java +++ b/codegen-cli/src/test/java/com/networknt/codegen/CliTest.java @@ -1,7 +1,28 @@ package com.networknt.codegen; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.networknt.config.Config; +import org.junit.Test; + /** * Created by steve on 24/04/17. */ public class CliTest { + private ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()); + private final ObjectReader reader = objectMapper.reader(); + + @Test + public void testJsonModel() throws Exception { + JsonNode modelNode = reader.readTree(CliTest.class.getClassLoader().getResourceAsStream("openapi.json")); + System.out.println(modelNode); + } + + @Test + public void testYamlModel() throws Exception { + JsonNode modelNode = reader.readTree(CliTest.class.getClassLoader().getResourceAsStream("openapi.yaml")); + System.out.println(modelNode); + } } diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/rest/YAMLFileParameterizerTest.java b/codegen-cli/src/test/java/com/networknt/codegen/YAMLFileParameterizerTest.java similarity index 74% rename from light-rest-4j/src/test/java/com/networknt/codegen/rest/YAMLFileParameterizerTest.java rename to codegen-cli/src/test/java/com/networknt/codegen/YAMLFileParameterizerTest.java index 0d0d83e06..3c68d12ee 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/rest/YAMLFileParameterizerTest.java +++ b/codegen-cli/src/test/java/com/networknt/codegen/YAMLFileParameterizerTest.java @@ -1,7 +1,8 @@ -package com.networknt.codegen.rest; +package com.networknt.codegen; -import static java.io.File.separator; -import static org.junit.Assert.assertTrue; +import com.fasterxml.jackson.databind.JsonNode; +import org.junit.Ignore; +import org.junit.Test; import java.io.File; import java.io.FilenameFilter; @@ -11,12 +12,8 @@ import java.util.Map; import java.util.Set; -import org.junit.Ignore; -import org.junit.Test; - -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; -import com.networknt.codegen.OpenApiGeneratorTest; +import static java.io.File.separator; +import static org.junit.Assert.assertTrue; @Ignore public class YAMLFileParameterizerTest { @@ -38,16 +35,16 @@ public void testNormalizeFilename() { @Test public void testFileExcludeSet() { - List excludes = Arrays.asList(Any.wrap("/a/b/c\\d.txt"), Any.wrap("2.yml"), Any.wrap("\\a\\b\\c\\d.txt"), Any.wrap("")); + List excludes = Arrays.asList("/a/b/c\\d.txt", "2.yml", "\\a\\b\\c\\d.txt", ""); - Set excludeSet = YAMLFileParameterizer.buildFileExcludeSet(".",excludes); + Set excludeSet = YAMLFileParameterizer.buildFileExcludeSet(".", excludes); assertTrue(2==excludeSet.size()); } @Test public void testResourceExcludeSet() { - List excludes = Arrays.asList(Any.wrap("audit.yml"), Any.wrap("body.yml")); + List excludes = Arrays.asList("audit.yml", "body.yml"); Set excludeSet = YAMLFileParameterizer.buildResourceExcludeSet("handlerconfig/", excludes); @@ -58,29 +55,29 @@ public void testResourceExcludeSet() { public void testParameterizing() throws IOException { String destDirName = "/tmp/yml_param_test"; File destDir = new File(destDirName); - + if (destDir.exists() && destDir.isDirectory()) { destDir.delete(); } - + YAMLFileParameterizer.copyResources(YAMLFileParameterizer.DEFAULT_RESOURCE_LOCATION, destDirName+separator+YAMLFileParameterizer.DEFAULT_DEST_DIR); - - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - - Map genConfig = anyConfig.get(YAMLFileParameterizer.GENERATE_ENV_VARS).asMap(); - - if (anyConfig.keys().contains(YAMLFileParameterizer.GENERATE_ENV_VARS)) { + + JsonNode anyConfig = Generator.jsonMapper.readTree(YAMLFileParameterizer.class.getResourceAsStream(configName)); + + Map genConfig = (Map)anyConfig.get(YAMLFileParameterizer.GENERATE_ENV_VARS); + + if (anyConfig.findValue(YAMLFileParameterizer.GENERATE_ENV_VARS) != null) { YAMLFileParameterizer.rewriteAll(destDirName+separator+YAMLFileParameterizer.DEFAULT_DEST_DIR, genConfig); } - + FilenameFilter filter = (dir, name)->name.toLowerCase().endsWith(".yml"); File outputDestDir = new File(destDirName+separator+YAMLFileParameterizer.DEFAULT_DEST_DIR); int destCount = outputDestDir.list(filter).length; - + assertTrue(destCount>0); - + //genConfig.remove(YAMLFileParameterizer.KEY_IN_PLACE); - + //YAMLFileParameterizer.rewriteFile(new File("src/main/resources/handlerconfig/config.yml"), new File("/tmp/config.yml"), genConfig); } } diff --git a/codegen-cli/src/test/resources/openapi.json b/codegen-cli/src/test/resources/openapi.json new file mode 100644 index 000000000..3a278d8d8 --- /dev/null +++ b/codegen-cli/src/test/resources/openapi.json @@ -0,0 +1,304 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "license": { + "name": "MIT" + } + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1" + } + ], + "paths": { + "/pets": { + "get": { + "summary": "List all pets", + "operationId": "listPets", + "tags": [ + "pets" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "security": [ + { + "petstore_auth": [ + "read:pets" + ] + } + ], + "responses": { + "200": { + "description": "An paged array of pets", + "headers": { + "x-next": { + "description": "A link to the next page of responses", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + }, + "example": [ + { + "id": 1, + "name": "catten", + "tag": "cat" + }, + { + "id": 2, + "name": "doggy", + "tag": "dog" + } + ] + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "post": { + "summary": "Create a pet", + "operationId": "createPets", + "requestBody": { + "description": "Pet to add to the store", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "tags": [ + "pets" + ], + "security": [ + { + "petstore_auth": [ + "read:pets", + "write:pets" + ] + } + ], + "responses": { + "201": { + "description": "Null response" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pets/{petId}": { + "get": { + "summary": "Info for a specific pet", + "operationId": "showPetById", + "tags": [ + "pets" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "required": true, + "description": "The id of the pet to retrieve", + "schema": { + "type": "string" + } + } + ], + "security": [ + { + "petstore_auth": [ + "read:pets" + ] + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + }, + "example": { + "id": 1, + "name": "Jessica Right", + "tag": "pet" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "delete": { + "summary": "Delete a specific pet", + "operationId": "deletePetById", + "tags": [ + "pets" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "required": true, + "description": "The id of the pet to delete", + "schema": { + "type": "string" + } + }, + { + "name": "key", + "in": "header", + "required": true, + "description": "The key header", + "schema": { + "type": "string" + } + } + ], + "security": [ + { + "petstore_auth": [ + "write:pets" + ] + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + }, + "examples": { + "response": { + "value": { + "id": 1, + "name": "Jessica Right", + "tag": "pet" + } + } + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "petstore_auth": { + "type": "oauth2", + "description": "This API uses OAuth 2 with the client credential grant flow.", + "flows": { + "clientCredentials": { + "tokenUrl": "https://localhost:6882/token", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } + } + } + }, + "schemas": { + "Pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Error": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/codegen-cli/src/test/resources/openapi.yaml b/codegen-cli/src/test/resources/openapi.yaml new file mode 100644 index 000000000..a455347f9 --- /dev/null +++ b/codegen-cli/src/test/resources/openapi.yaml @@ -0,0 +1,187 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Swagger Petstore + license: + name: MIT +servers: + - url: 'http://petstore.swagger.io/v1' +paths: + /pets: + get: + summary: List all pets + operationId: listPets + tags: + - pets + parameters: + - name: limit + in: query + description: How many items to return at one time (max 100) + required: false + schema: + type: integer + format: int32 + security: + - petstore_auth: + - 'read:pets' + responses: + '200': + description: An paged array of pets + headers: + x-next: + description: A link to the next page of responses + schema: + type: string + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + example: + - id: 1 + name: catten + tag: cat + - id: 2 + name: doggy + tag: dog + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + post: + summary: Create a pet + operationId: createPets + requestBody: + description: Pet to add to the store + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + tags: + - pets + security: + - petstore_auth: + - 'read:pets' + - 'write:pets' + responses: + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '/pets/{petId}': + get: + summary: Info for a specific pet + operationId: showPetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to retrieve + schema: + type: string + security: + - petstore_auth: + - 'read:pets' + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + example: + id: 1 + name: Jessica Right + tag: pet + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + delete: + summary: Delete a specific pet + operationId: deletePetById + tags: + - pets + parameters: + - name: petId + in: path + required: true + description: The id of the pet to delete + schema: + type: string + - name: key + in: header + required: true + description: The key header + schema: + type: string + security: + - petstore_auth: + - 'write:pets' + responses: + '200': + description: Expected response to a valid request + content: + application/json: + schema: + $ref: '#/components/schemas/Pet' + examples: + response: + value: + id: 1 + name: Jessica Right + tag: pet + default: + description: unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' +components: + securitySchemes: + petstore_auth: + type: oauth2 + description: This API uses OAuth 2 with the client credential grant flow. + flows: + clientCredentials: + tokenUrl: 'https://localhost:6882/token' + scopes: + 'write:pets': modify pets in your account + 'read:pets': read your pets + schemas: + Pet: + type: object + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string diff --git a/codegen-core/pom.xml b/codegen-core/pom.xml index 97489198c..72c03706b 100644 --- a/codegen-core/pom.xml +++ b/codegen-core/pom.xml @@ -47,12 +47,12 @@ rocker-runtime - com.jsoniter - jsoniter + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml - org.javassist - javassist + com.fasterxml.jackson.core + jackson-databind ch.qos.logback diff --git a/codegen-core/src/main/java/com/networknt/codegen/Generator.java b/codegen-core/src/main/java/com/networknt/codegen/Generator.java index d63bb3dbd..777a1c1a7 100644 --- a/codegen-core/src/main/java/com/networknt/codegen/Generator.java +++ b/codegen-core/src/main/java/com/networknt/codegen/Generator.java @@ -13,9 +13,12 @@ import java.util.HashMap; import java.util.Map; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fizzed.rocker.runtime.ArrayOfByteArraysOutput; import com.fizzed.rocker.runtime.DefaultRockerModel; -import com.jsoniter.any.Any; import org.apache.commons.io.IOUtils; @@ -28,19 +31,22 @@ * @author Steve Hu */ public interface Generator { - + // Jackson ObjectMapper with YAML support. + public static ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); + public static ObjectMapper jsonMapper = new ObjectMapper(); // config schema cache Map schemaMap = new HashMap<>(); /** - * Generate the project based on the input parameters. + * Generate the project based on the input parameters. For most frameworks, the model can be converted to JSON, but + * the GraphQL is using a schema file that can only be passed in as a string. This is why, the model is an Object. * * @param targetPath The output directory of the generated project * @param model The optional model data that trigger the generation, i.e. swagger specification, graphql IDL etc. * @param config A json object that controls how the generator behaves. * @throws IOException throws IOException */ - void generate(String targetPath, Object model, Any config) throws IOException; + void generate(String targetPath, Object model, JsonNode config) throws IOException; /** * Get generator name @@ -171,4 +177,314 @@ static void copyFile(final InputStream is, final java.nio.file.Path folder) thro Files.copy(is, folder, StandardCopyOption.REPLACE_EXISTING); } + default String getRootPackage(JsonNode config, String defaultValue) { + String rootPackage = defaultValue == null ? "com.networknt.app" : defaultValue; + JsonNode jsonNode = config.get("rootPackage"); + if(jsonNode == null) { + ((ObjectNode)config).put("rootPackage", rootPackage); + } else { + rootPackage = jsonNode.textValue(); + } + return rootPackage; + } + + default String getModelPackage(JsonNode config, String defaultValue) { + String rootPackage = getRootPackage(config, null); + String modelPackage = defaultValue == null ? rootPackage + ".model" : defaultValue; + JsonNode jsonNode = config.get("modelPackage"); + if(jsonNode == null) { + ((ObjectNode)config).put("modelPackage", modelPackage); + } else { + modelPackage = jsonNode.textValue(); + } + return modelPackage; + } + + default String getHandlerPackage(JsonNode config, String defaultValue) { + String rootPackage = getRootPackage(config, null); + String handlerPackage = defaultValue == null ? rootPackage + ".handler" : defaultValue; + JsonNode jsonNode = config.get("handlerPackage"); + if(jsonNode == null) { + ((ObjectNode)config).put("handlerPackage", handlerPackage); + } else { + handlerPackage = jsonNode.textValue(); + } + return handlerPackage; + } + + default boolean isOverwriteHandler(JsonNode config, Boolean defaultValue) { + boolean overwriteHandler = defaultValue == null ? true : defaultValue; + JsonNode jsonNode = config.get("overwriteHandler"); + if(jsonNode == null) { + ((ObjectNode)config).put("overwriteHandler", overwriteHandler); + } else { + overwriteHandler = jsonNode.booleanValue(); + } + return overwriteHandler; + } + + default boolean isOverwriteHandlerTest(JsonNode config, Boolean defaultValue) { + boolean overwriteHandlerTest = defaultValue == null ? true : defaultValue; + JsonNode jsonNode = config.get("overwriteHandlerTest"); + if(jsonNode == null) { + ((ObjectNode)config).put("overwriteHandlerTest", overwriteHandlerTest); + } else { + overwriteHandlerTest = jsonNode.booleanValue(); + } + return overwriteHandlerTest; + } + + default boolean isOverwriteModel(JsonNode config, Boolean defaultValue) { + boolean overwriteModel = defaultValue == null ? true : defaultValue; + JsonNode jsonNode = config.get("overwriteModel"); + if(jsonNode == null) { + ((ObjectNode)config).put("overwriteModel", overwriteModel); + } else { + overwriteModel = jsonNode.booleanValue(); + } + return overwriteModel; + } + + default boolean isGenerateModelOnly(JsonNode config, Boolean defaultValue) { + boolean generateModelOnly = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("generateModelOnly"); + if(jsonNode == null) { + ((ObjectNode)config).put("generateModelOnly", generateModelOnly); + } else { + generateModelOnly = jsonNode.booleanValue(); + } + return generateModelOnly; + } + + default boolean isEnableHttp(JsonNode config, Boolean defaultValue) { + boolean enableHttp = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("enableHttp"); + if(jsonNode == null) { + ((ObjectNode)config).put("enableHttp", enableHttp); + } else { + enableHttp = jsonNode.booleanValue(); + } + return enableHttp; + } + + default String getHttpPort(JsonNode config, String defaultValue) { + String httpPort = defaultValue == null ? "8080" : defaultValue; + JsonNode jsonNode = config.get("httpPort"); + if(jsonNode == null) { + ((ObjectNode)config).put("httpPort", httpPort); + } else { + httpPort = jsonNode.asText(); + } + return httpPort; + } + + default boolean isEnableHttps(JsonNode config, Boolean defaultValue) { + boolean enableHttps = defaultValue == null ? true : defaultValue; + JsonNode jsonNode = config.get("enableHttps"); + if(jsonNode == null) { + ((ObjectNode)config).put("enableHttps", enableHttps); + } else { + enableHttps = jsonNode.booleanValue(); + } + return enableHttps; + } + + default boolean isEnableHttp2(JsonNode config, Boolean defaultValue) { + boolean enableHttp2 = defaultValue == null ? true : defaultValue; + JsonNode jsonNode = config.get("enableHttp2"); + if(jsonNode == null) { + ((ObjectNode)config).put("enableHttp2", enableHttp2); + } else { + enableHttp2 = jsonNode.booleanValue(); + } + return enableHttp2; + } + + default String getHttpsPort(JsonNode config, String defaultValue) { + String httpsPort = defaultValue == null ? "8443" : defaultValue; + JsonNode jsonNode = config.get("httpsPort"); + if(jsonNode == null) { + ((ObjectNode)config).put("httpsPort", httpsPort); + } else { + httpsPort = jsonNode.asText(); + } + return httpsPort; + } + + default boolean isEnableRegistry(JsonNode config, Boolean defaultValue) { + boolean enableRegistry = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("enableRegistry"); + if(jsonNode == null) { + ((ObjectNode)config).put("enableRegistry", enableRegistry); + } else { + enableRegistry = jsonNode.booleanValue(); + } + return enableRegistry; + } + + default boolean isEclipseIDE(JsonNode config, Boolean defaultValue) { + boolean eclipseIDE = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("eclipseIDE"); + if(jsonNode == null) { + ((ObjectNode)config).put("eclipseIDE", eclipseIDE); + } else { + eclipseIDE = jsonNode.booleanValue(); + } + return eclipseIDE; + } + + default boolean isSupportClient(JsonNode config, Boolean defaultValue) { + boolean supportClient = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("supportClient"); + if(jsonNode == null) { + ((ObjectNode)config).put("supportClient", supportClient); + } else { + supportClient = jsonNode.booleanValue(); + } + return supportClient; + } + + default boolean isPrometheusMetrics(JsonNode config, Boolean defaultValue) { + boolean prometheusMetrics = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("prometheusMetrics"); + if(jsonNode == null) { + ((ObjectNode)config).put("prometheusMetrics", prometheusMetrics); + } else { + prometheusMetrics = jsonNode.booleanValue(); + } + return prometheusMetrics; + } + + default String getDockerOrganization(JsonNode config, String defaultValue) { + String dockerOrganization = defaultValue == null ? "networknt" : defaultValue; + JsonNode jsonNode = config.get("dockerOrganization"); + if(jsonNode == null) { + ((ObjectNode)config).put("dockerOrganization", dockerOrganization); + } else { + dockerOrganization = jsonNode.asText(); + } + return dockerOrganization; + } + + default String getVersion(JsonNode config, String defaultValue) { + String version = defaultValue == null ? "1.0.0" : defaultValue; + JsonNode jsonNode = config.get("version"); + if(jsonNode == null) { + ((ObjectNode)config).put("version", version); + } else { + version = jsonNode.asText(); + } + return version; + } + + default String getGroupId(JsonNode config, String defaultValue) { + String groupId = defaultValue == null ? "com.networknt" : defaultValue; + JsonNode jsonNode = config.get("groupId"); + if(jsonNode == null) { + ((ObjectNode)config).put("groupId", groupId); + } else { + groupId = jsonNode.asText(); + } + return groupId; + } + + default String getArtifactId(JsonNode config, String defaultValue) { + String artifactId = defaultValue == null ? "app" : defaultValue; + JsonNode jsonNode = config.get("artifactId"); + if(jsonNode == null) { + ((ObjectNode)config).put("artifactId", artifactId); + } else { + artifactId = jsonNode.asText(); + } + return artifactId; + } + + default boolean isSpecChangeCodeReGenOnly(JsonNode config, Boolean defaultValue) { + boolean specChangeCodeReGenOnly = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("specChangeCodeReGenOnly"); + if(jsonNode == null) { + ((ObjectNode)config).put("specChangeCodeReGenOnly", specChangeCodeReGenOnly); + } else { + specChangeCodeReGenOnly = jsonNode.booleanValue(); + } + return specChangeCodeReGenOnly; + } + + default boolean isEnableParamDescription(JsonNode config, Boolean defaultValue) { + boolean enableParamDescription = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("enableParamDescription"); + if(jsonNode == null) { + ((ObjectNode)config).put("enableParamDescription", enableParamDescription); + } else { + enableParamDescription = jsonNode.booleanValue(); + } + return enableParamDescription; + } + + default boolean isSkipPomFile(JsonNode config, Boolean defaultValue) { + boolean skipPomFile = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("skipPomFile"); + if(jsonNode == null) { + ((ObjectNode)config).put("skipPomFile", skipPomFile); + } else { + skipPomFile = jsonNode.booleanValue(); + } + return skipPomFile; + } + + default boolean isKafkaProducer(JsonNode config, Boolean defaultValue) { + boolean kafkaProducer = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("kafkaProducer"); + if(jsonNode == null) { + ((ObjectNode)config).put("kafkaProducer", kafkaProducer); + } else { + kafkaProducer = jsonNode.booleanValue(); + } + return kafkaProducer; + } + + default boolean isKafkaConsumer(JsonNode config, Boolean defaultValue) { + boolean kafkaConsumer = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("kafkaConsumer"); + if(jsonNode == null) { + ((ObjectNode)config).put("kafkaConsumer", kafkaConsumer); + } else { + kafkaConsumer = jsonNode.booleanValue(); + } + return kafkaConsumer; + } + + default boolean isSupportAvro(JsonNode config, Boolean defaultValue) { + boolean supportAvro = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("supportAvro"); + if(jsonNode == null) { + ((ObjectNode)config).put("supportAvro", supportAvro); + } else { + supportAvro = jsonNode.booleanValue(); + } + return supportAvro; + } + + default String getKafkaTopic(JsonNode config, String defaultValue) { + String kafkaTopic = defaultValue == null ? "event" : defaultValue; + JsonNode jsonNode = config.get("kafkaTopic"); + if(jsonNode == null) { + ((ObjectNode)config).put("kafkaTopic", kafkaTopic); + } else { + kafkaTopic = jsonNode.asText(); + } + return kafkaTopic; + } + + default String getDecryptOption(JsonNode config, String defaultValue) { + String decryptOption = defaultValue == null ? "default" : defaultValue; + JsonNode jsonNode = config.get("decryptOption"); + if(jsonNode == null) { + ((ObjectNode)config).put("decryptOption", decryptOption); + } else { + decryptOption = jsonNode.asText(); + } + return decryptOption; + } + } diff --git a/codegen-core/src/test/java/com/networknt/codegen/JsonDefaultTest.java b/codegen-core/src/test/java/com/networknt/codegen/JsonDefaultTest.java new file mode 100644 index 000000000..350d01716 --- /dev/null +++ b/codegen-core/src/test/java/com/networknt/codegen/JsonDefaultTest.java @@ -0,0 +1,17 @@ +package com.networknt.codegen; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.Test; + +public class JsonDefaultTest { + String s = "{\"enabled\":true,\"level\":5,\"name\":\"steve\"}"; + + @Test + public void testBooleanDefault() throws Exception { + JsonNode node = Generator.jsonMapper.readTree(s); + ((ObjectNode)node).put("useData", true); + boolean b = node.get("useData").booleanValue(); + System.out.println(b); + } +} diff --git a/codegen-core/src/test/java/com/networknt/codegen/JsonIterTest.java b/codegen-core/src/test/java/com/networknt/codegen/JsonIterTest.java deleted file mode 100644 index af7fdd88b..000000000 --- a/codegen-core/src/test/java/com/networknt/codegen/JsonIterTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.networknt.codegen; - -import com.jsoniter.any.Any; -import org.junit.Test; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; - -/** - * Created by steve on 09/07/17. - */ -public class JsonIterTest { - private static Map mapOf(Object... args) { - HashMap map = new HashMap(); - for (int i = 0; i < args.length; i += 2) { - map.put((String) args[i], args[i + 1]); - } - return map; - } - - @Test - public void testMapSize() { - Any any = Any.wrap(mapOf("hello", 1, "world", 2)); - assertEquals(2, any.size()); - } - - @Test - public void testListSize() { - Any any = Any.wrap(Arrays.asList(1, 2, 3)); - assertEquals(3, any.size()); - } - - @Test - public void testArraySize() { - Any any = Any.wrap(new int[]{1, 2, 3}); - assertEquals(3, any.size()); - } - - @Test - public void testGetNestedMapLevel1() { - Any any = Any.wrap(mapOf("a", mapOf("b", "c"), "d", mapOf("e", "f"))); - assertEquals("c", any.get("a", "b").toString()); - // if using * it must be single quoted. - assertEquals("{\"a\":\"c\"}", any.get('*', "b").toString()); - } - - @Test - public void testGetNestedMapLevel2() { - Any any = Any.wrap(mapOf("a", mapOf("b", mapOf("c", "d")), "e", mapOf("f", mapOf("g", "h")))); - System.out.println("any = " + any.toString()); - assertEquals("d", any.get("a", "b", "c").toString()); - String s = any.get('*', "b").toString(); - assertEquals("{\"a\":{\"c\":\"d\"}}", s); - s = any.get('*', "b", "c").toString(); - assertEquals("{\"a\":\"d\"}", s); - s = any.get('*', '*', "f").toString(); - System.out.println("s = " + s); - assertEquals("{\"a\":{},\"e\":{}}", s); - - } - - @Test - public void testGetNestedListLevel1() { - Any any = Any.wrap(Arrays.asList(Arrays.asList("hello"), Arrays.asList("world"))); - System.out.println("any = " + any.toString()); - - assertEquals("hello", any.get(0, 0).toString()); - assertEquals("[\"hello\",\"world\"]", any.get('*', 0).toString()); - } - - @Test - public void testGetNestedListLevel2() { - Any any = Any.wrap(Arrays.asList(Arrays.asList(Arrays.asList("hello")), Arrays.asList(Arrays.asList("world")))); - System.out.println("any = " + any.toString()); - assertEquals("hello", any.get(0, 0, 0).toString()); - assertEquals("[\"hello\",\"world\"]", any.get('*', 0, 0).toString()); - } - - @Test - public void testGetListOfMaps() { - Any any = Any.wrap(Arrays.asList(Arrays.asList(Arrays.asList(mapOf("hello", "world"))))); - System.out.println("any = " + any.toString()); - assertEquals("world", any.get(0, 0, 0, "hello").toString()); - String s = any.get(0, 0, 0).toString(); - System.out.println("s = " + s); - assertEquals("{\"hello\":\"world\"}", s); - } - - @Test - public void testGetMapOfLists() { - Any any = Any.wrap(mapOf("a", Arrays.asList(Arrays.asList(Arrays.asList(mapOf("hello", "world")))))); - System.out.println("any = " + any.toString()); - assertEquals("world", any.get("a", 0, 0, 0, "hello").toString()); - String s = any.get("a", 0, 0, 0).toString(); - System.out.println("s = " + s); - assertEquals("{\"hello\":\"world\"}", s); - } - -} diff --git a/codegen-fwk/src/test/resources/config/service.yml b/codegen-fwk/src/test/resources/config/service.yml index 49cf844ed..003a153c7 100644 --- a/codegen-fwk/src/test/resources/config/service.yml +++ b/codegen-fwk/src/test/resources/config/service.yml @@ -2,8 +2,8 @@ singletons: # Generator interface implementations - com.networknt.codegen.Generator: - - com.networknt.codegen.rest.OpenApiGenerator - - com.networknt.codegen.hybrid.HybridServerGenerator - - com.networknt.codegen.hybrid.HybridServiceGenerator + # - com.networknt.codegen.rest.OpenApiGenerator + # - com.networknt.codegen.hybrid.HybridServerGenerator + # - com.networknt.codegen.hybrid.HybridServiceGenerator - com.networknt.codegen.graphql.GraphqlGenerator - - com.networknt.codegen.rest.OpenApiKotlinGenerator + # - com.networknt.codegen.rest.OpenApiKotlinGenerator diff --git a/codegen-web/src/main/java/com/networknt/codegen/handler/CodegenMultipleHandler.java b/codegen-web/src/main/java/com/networknt/codegen/handler/CodegenMultipleHandler.java index efe576577..4060a62fb 100644 --- a/codegen-web/src/main/java/com/networknt/codegen/handler/CodegenMultipleHandler.java +++ b/codegen-web/src/main/java/com/networknt/codegen/handler/CodegenMultipleHandler.java @@ -1,12 +1,13 @@ package com.networknt.codegen.handler; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; +import com.fasterxml.jackson.databind.JsonNode; import com.networknt.codegen.CodegenWebConfig; import com.networknt.codegen.FrameworkRegistry; import com.networknt.codegen.Generator; import com.networknt.codegen.Utils; +import com.networknt.codegen.graphql.GraphqlGenerator; import com.networknt.config.Config; +import com.networknt.config.JsonMapper; import com.networknt.rpc.Handler; import com.networknt.rpc.router.JsonHandler; import com.networknt.rpc.router.ServiceHandler; @@ -71,16 +72,20 @@ public ByteBuffer handle(HttpServerExchange exchange, Object input) { return NioUtils.toByteBuffer(getStatus(exchange, STATUS_INVALID_FRAMEWORK, framework)); } String modelType = (String)generatorMap.get("modelType"); - Object model = null; // the model can be Any or String depending on the framework + Object model = null; // the model can be JsonNode or String depending on the framework if("C".equals(modelType)) { String modelText = (String)generatorMap.get("modelText"); - // json or yaml? - modelText = modelText.trim(); - if(modelText.startsWith("{") || modelText.startsWith("[")) { - // This is a json string. - model = JsonIterator.deserialize(modelText); - } else { + if(GraphqlGenerator.FRAMEWORK.equals(framework)) { model = modelText; + } else { + // json or yaml? + if(modelText.startsWith("{") || modelText.startsWith("[")) { + // This is a json string. + model = Generator.jsonMapper.readTree(modelText); + } else { + // This is a yaml string + model = Generator.yamlMapper.readTree(modelText); + } } } else if("U".equals(modelType)) { String modelUrl = (String)generatorMap.get("modelUrl"); @@ -88,7 +93,9 @@ public ByteBuffer handle(HttpServerExchange exchange, Object input) { if(Utils.isUrl(modelUrl)) { // if it is a json file. if(modelUrl.endsWith(".json")) { - model = JsonIterator.deserialize(Utils.urlToByteArray(new URL(modelUrl))); + model = Generator.jsonMapper.readTree((Utils.urlToByteArray(new URL(modelUrl)))); + } else if (modelUrl.endsWith(".yml") || modelUrl.endsWith(".yaml")) { + model = Generator.yamlMapper.readTree(Utils.urlToByteArray(new URL(modelUrl))); } else { model = new String(Utils.urlToByteArray(new URL(modelUrl)), StandardCharsets.UTF_8); } @@ -99,29 +106,29 @@ public ByteBuffer handle(HttpServerExchange exchange, Object input) { } String configType = (String)generatorMap.get("configType"); - Any config = null; + JsonNode config = null; if("C".equals(configType)) { String configText = (String)generatorMap.get("configText"); configText = configText.trim(); // the config must be json. if(configText.startsWith("{") || configText.startsWith("[")) { - config = JsonIterator.deserialize(configText); + config = Generator.jsonMapper.readTree(configText); } else { - return NioUtils.toByteBuffer(getStatus(exchange, STATUS_INVALID_CONFIG_JSON)); + config = Generator.yamlMapper.readTree(configText); } } else if("U".equals(configType)) { String configUrl = (String)generatorMap.get("configUrl"); configUrl = configUrl.trim(); - // make sure that the file extension is .json if(configUrl.endsWith(".json")) { - config = JsonIterator.deserialize(Utils.urlToByteArray(new URL(configUrl))); + config = Generator.jsonMapper.readTree(Utils.urlToByteArray(new URL(configUrl))); + } else if(configUrl.endsWith(".yml") || configUrl.endsWith(".yaml")) { + config = Generator.yamlMapper.readTree(Utils.urlToByteArray(new URL(configUrl))); } else { return NioUtils.toByteBuffer(getStatus(exchange, STATUS_INVALID_CONFIG_URL_EXTENSION, configUrl)); } } - Generator generator = FrameworkRegistry.getInstance().getGenerator(framework); - generator.generate(projectFolder, model, Any.wrap(config)); + generator.generate(projectFolder, model, config); } } catch (Exception e) { logger.error("Exception:", e); diff --git a/codegen-web/src/main/java/com/networknt/codegen/handler/CodegenSingleHandler.java b/codegen-web/src/main/java/com/networknt/codegen/handler/CodegenSingleHandler.java index ac05e44d4..d4aa1ed50 100644 --- a/codegen-web/src/main/java/com/networknt/codegen/handler/CodegenSingleHandler.java +++ b/codegen-web/src/main/java/com/networknt/codegen/handler/CodegenSingleHandler.java @@ -1,11 +1,11 @@ package com.networknt.codegen.handler; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; +import com.fasterxml.jackson.databind.JsonNode; import com.networknt.codegen.CodegenWebConfig; import com.networknt.codegen.FrameworkRegistry; import com.networknt.codegen.Generator; import com.networknt.codegen.Utils; +import com.networknt.codegen.graphql.GraphqlGenerator; import com.networknt.config.Config; import com.networknt.config.JsonMapper; import com.networknt.rpc.Handler; @@ -75,12 +75,18 @@ public ByteBuffer handle(HttpServerExchange exchange, Object input) { if("C".equals(modelType)) { String modelText = (String)generatorMap.get("modelText"); modelText = modelText.trim(); - // json or yaml? - if(modelText.startsWith("{") || modelText.startsWith("[")) { - // This is a json string. - model = JsonIterator.deserialize(modelText); - } else { + // based on the framework, we decide the format. + if(GraphqlGenerator.FRAMEWORK.equals(framework)) { model = modelText; + } else { + // json or yaml? + if(modelText.startsWith("{") || modelText.startsWith("[")) { + // This is a json string. + model = Generator.jsonMapper.readTree(modelText); + } else { + // This is a yaml string + model = Generator.yamlMapper.readTree(modelText); + } } } else if("U".equals(modelType)) { String modelUrl = (String)generatorMap.get("modelUrl"); @@ -88,7 +94,9 @@ public ByteBuffer handle(HttpServerExchange exchange, Object input) { if(Utils.isUrl(modelUrl)) { // if it is a json file. if(modelUrl.endsWith(".json")) { - model = JsonIterator.deserialize(Utils.urlToByteArray(new URL(modelUrl))); + model = Generator.jsonMapper.readTree(Utils.urlToByteArray(new URL(modelUrl))); + } else if (modelUrl.endsWith(".yml") || modelUrl.endsWith(".yaml")) { + model = Generator.yamlMapper.readTree(Utils.urlToByteArray(new URL(modelUrl))); } else { model = new String(Utils.urlToByteArray(new URL(modelUrl)), StandardCharsets.UTF_8); } @@ -98,29 +106,31 @@ public ByteBuffer handle(HttpServerExchange exchange, Object input) { } String configType = (String)generatorMap.get("configType"); - Any config = null; + JsonNode config = null; if("C".equals(configType)) { String configText = (String)generatorMap.get("configText"); configText = configText.trim(); // the config must be json. if(configText.startsWith("{") || configText.startsWith("[")) { - config = JsonIterator.deserialize(configText); + config = Generator.jsonMapper.readTree(configText); } else { - return NioUtils.toByteBuffer(getStatus(exchange, STATUS_INVALID_CONFIG_JSON)); + config = Generator.yamlMapper.readTree(configText); } } else if("U".equals(configType)) { String configUrl = (String)generatorMap.get("configUrl"); configUrl = configUrl.trim(); // make sure that the file extension is .json if(configUrl.endsWith(".json")) { - config = JsonIterator.deserialize(Utils.urlToByteArray(new URL(configUrl))); + config = Generator.jsonMapper.readTree(Utils.urlToByteArray(new URL(configUrl))); + } else if(configUrl.endsWith(".yml") || configUrl.endsWith(".yaml")) { + config = Generator.yamlMapper.readTree(Utils.urlToByteArray(new URL(configUrl))); } else { return NioUtils.toByteBuffer(getStatus(exchange, STATUS_INVALID_CONFIG_URL_EXTENSION, configUrl)); } } Generator generator = FrameworkRegistry.getInstance().getGenerator(framework); - generator.generate(projectFolder, model, Any.wrap(config)); + generator.generate(projectFolder, model, config); } catch (Exception e) { logger.error("Exception:", e); diff --git a/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java b/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java index a78b32736..19306fb53 100644 --- a/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java +++ b/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java @@ -1,21 +1,14 @@ package com.networknt.codegen.graphql; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jsoniter.any.Any; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.networknt.codegen.Generator; -import com.networknt.utility.NioUtils; -import graphql.schema.GraphQLSchema; -import graphql.schema.idl.RuntimeWiring; -import graphql.schema.idl.SchemaGenerator; -import graphql.schema.idl.SchemaParser; -import graphql.schema.idl.TypeDefinitionRegistry; - -import java.io.File; +import com.networknt.config.ConfigException; + import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.*; -import java.util.Map; import static java.io.File.separator; @@ -23,35 +16,118 @@ * Created by steve on 01/05/17. */ public class GraphqlGenerator implements Generator { - static ObjectMapper mapper = new ObjectMapper(); + public static final String FRAMEWORK="light-graphql-4j"; @Override public String getFramework() { - return "light-graphql-4j"; + return FRAMEWORK; } @Override - public void generate(String targetPath, Object model, Any config) throws IOException { + public void generate(String targetPath, Object schema, JsonNode config) throws IOException { // whoever is calling this needs to make sure that model is converted to Map - String schemaPackage = config.get("schemaPackage").toString(); - String schemaClass = config.get("schemaClass").toString(); - boolean overwriteSchemaClass = config.toBoolean("overwriteSchemaClass"); - boolean enableHttp = config.toBoolean("enableHttp"); - String httpPort = config.toString("httpPort"); - boolean enableHttps = config.toBoolean("enableHttps"); - String httpsPort = config.toString("httpsPort"); - boolean enableHttp2 = config.toBoolean("enableHttp2"); - boolean enableRegistry = config.toBoolean("enableRegistry"); - boolean eclipseIDE = config.toBoolean("eclipseIDE"); - boolean supportClient = config.toBoolean("supportClient"); - boolean prometheusMetrics = config.toBoolean("prometheusMetrics"); - boolean skipHealthCheck = config.toBoolean("skipHealthCheck"); - boolean skipServerInfo = config.toBoolean("skipServerInfo"); - String dockerOrganization = config.toString("dockerOrganization"); - String version = config.toString("version"); - String serviceId = config.get("groupId").toString().trim() + "." + config.get("artifactId").toString().trim() + "-" + config.get("version").toString().trim(); - - if(dockerOrganization == null || dockerOrganization.length() == 0) dockerOrganization = "networknt"; + JsonNode jsonNode = config.get("schemaPackage"); + if(jsonNode == null) throw new ConfigException("schemaPackage is missing in config"); + String schemaPackage = jsonNode.textValue(); + + jsonNode = config.get("schemaClass"); + if(jsonNode == null) throw new ConfigException("schemaClass is missing in config"); + String schemaClass = jsonNode.textValue(); + + jsonNode = config.get("overwriteSchemaClass"); + if(jsonNode == null) throw new ConfigException("overwriteSchemaClass is missing in config"); + boolean overwriteSchemaClass = jsonNode.booleanValue(); + + boolean enableHttp = false; + jsonNode = config.get("enableHttp"); + if(jsonNode == null) { + ((ObjectNode)config).put("enableHttp", false); + } else { + enableHttp = jsonNode.booleanValue(); + } + + String httpPort = "8080"; + jsonNode = config.get("httpPort"); + if(jsonNode == null) { + ((ObjectNode)config).put("httpPort", httpPort); + } else { + httpPort = jsonNode.asText(); + } + + boolean enableHttps = true; + jsonNode = config.get("enableHttps"); + if(jsonNode == null) { + ((ObjectNode)config).put("enableHttps", true); + } else { + enableHttps = jsonNode.booleanValue(); + } + + String httpsPort = "8443"; + jsonNode = config.get("httpsPort"); + if(jsonNode == null) { + ((ObjectNode)config).put("httpsPort", httpsPort); + } else { + httpsPort = jsonNode.asText(); + } + + boolean enableHttp2 = true; + jsonNode = config.get("enableHttp2"); + if(jsonNode == null) { + ((ObjectNode)config).put("enableHttp2", enableHttp2); + } else { + enableHttp2 = jsonNode.booleanValue(); + } + + boolean enableRegistry = false; + jsonNode = config.get("enableRegistry"); + if(jsonNode == null) { + ((ObjectNode)config).put("enableRegistry", enableRegistry); + } else { + enableRegistry = jsonNode.booleanValue(); + } + + boolean eclipseIDE = false; + jsonNode = config.get("eclipseIDE"); + if(jsonNode == null) { + ((ObjectNode)config).put("eclipseIDE", eclipseIDE); + } else { + eclipseIDE = jsonNode.booleanValue(); + } + + jsonNode = config.get("supportClient"); + if(jsonNode == null) throw new ConfigException("supportClient is missing in config"); + boolean supportClient = jsonNode.booleanValue(); + + boolean prometheusMetrics = false; + jsonNode = config.get("prometheusMetrics"); + if(jsonNode != null) prometheusMetrics = jsonNode.booleanValue(); + + boolean skipHealthCheck = false; + jsonNode = config.get("skipHealthCheck"); + if(jsonNode != null) skipHealthCheck = jsonNode.booleanValue(); + + + boolean skipServerInfo = false; + jsonNode = config.get("skipServerInfo"); + if(jsonNode != null) skipServerInfo = jsonNode.booleanValue(); + + String dockerOrganization = "networknt"; + jsonNode = config.get("dockerOrganization"); + if(jsonNode != null) dockerOrganization = jsonNode.textValue(); + + jsonNode = config.get("version"); + if(jsonNode == null) throw new ConfigException("version is missing in config"); + String version = jsonNode.textValue(); + + jsonNode = config.get("groupId"); + if(jsonNode == null) throw new ConfigException("groupId is missing in config"); + String groupId = jsonNode.textValue(); + + jsonNode = config.get("artifactId"); + if(jsonNode == null) throw new ConfigException("artifactId is missing in config"); + String artifactId = jsonNode.textValue(); + + String serviceId = groupId + "." + artifactId + "-" + version; transfer(targetPath, "", "pom.xml", templates.graphql.pom.template(config)); transferMaven(targetPath); @@ -109,10 +185,10 @@ public void generate(String targetPath, Object model, Any config) throws IOExcep // If no schema file is passed in, then it will just hard-coded as a Hello World example so that developer // can expand that to code his/her own schema. if(overwriteSchemaClass) { - if(model == null) { + if(schema == null) { transfer(targetPath, ("src.main.java." + schemaPackage).replace(".", separator), schemaClass + ".java", templates.graphql.schemaClassExample.template(schemaPackage, schemaClass)); } else { - Files.write(FileSystems.getDefault().getPath(targetPath, ("src.main.resources").replace(".", separator), "schema.graphqls"), ((String)model).getBytes(StandardCharsets.UTF_8)); + Files.write(FileSystems.getDefault().getPath(targetPath, ("src.main.resources").replace(".", separator), "schema.graphqls"), ((String)schema).getBytes(StandardCharsets.UTF_8)); // schema class loader/generator template. transfer(targetPath, ("src.main.java." + schemaPackage).replace(".", separator), schemaClass + ".java", templates.graphql.schemaClass.template(schemaPackage, schemaClass)); } diff --git a/light-graphql-4j/src/main/resources/templates/graphql/dockerfile.rocker.raw b/light-graphql-4j/src/main/resources/templates/graphql/dockerfile.rocker.raw index e4ee9f607..e4bc50f39 100644 --- a/light-graphql-4j/src/main/resources/templates/graphql/dockerfile.rocker.raw +++ b/light-graphql-4j/src/main/resources/templates/graphql/dockerfile.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config, String expose) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config, String expose) FROM azul/zulu-openjdk-alpine:11 as packager RUN { \ @@ -28,5 +28,5 @@ ENV JAVA_MINIMAL=/opt/jre ENV PATH="$PATH:$JAVA_MINIMAL/bin" COPY --from=packager "$JAVA_MINIMAL" "$JAVA_MINIMAL" -@with (name = config.get("artifactId") + "-" + config.get("version") + ".jar") {COPY /target/@name server.jar} +@with (name = config.get("artifactId").textValue() + "-" + config.get("version").textValue() + ".jar") {COPY /target/@name server.jar} CMD ["/bin/sh","-c","java -Dlight-4j-config-dir=/config -Dlogback.configurationFile=/config/logback.xml -jar /server.jar"] diff --git a/light-graphql-4j/src/main/resources/templates/graphql/dockerfileslim.rocker.raw b/light-graphql-4j/src/main/resources/templates/graphql/dockerfileslim.rocker.raw index 6b46b54f0..b1e3a00cc 100644 --- a/light-graphql-4j/src/main/resources/templates/graphql/dockerfileslim.rocker.raw +++ b/light-graphql-4j/src/main/resources/templates/graphql/dockerfileslim.rocker.raw @@ -1,6 +1,6 @@ -@import com.jsoniter.any.Any -@args (Any config, String expose) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config, String expose) FROM openjdk:11.0.3-slim #EXPOSE @expose -@with (name = config.get("artifactId") + "-" + config.get("version") + ".jar") {ADD /target/@name server.jar} +@with (name = config.get("artifactId").textValue() + "-" + config.get("version").textValue() + ".jar") {ADD /target/@name server.jar} CMD ["/bin/sh","-c","java -Dlight-4j-config-dir=/config -Dlogback.configurationFile=/config/logback.xml -jar /server.jar"] diff --git a/light-graphql-4j/src/main/resources/templates/graphql/handlerYml.rocker.raw b/light-graphql-4j/src/main/resources/templates/graphql/handlerYml.rocker.raw index fe9b97464..d4928b65b 100644 --- a/light-graphql-4j/src/main/resources/templates/graphql/handlerYml.rocker.raw +++ b/light-graphql-4j/src/main/resources/templates/graphql/handlerYml.rocker.raw @@ -1,6 +1,5 @@ @import java.util.Map @import java.util.List -@import com.jsoniter.any.Any @args (String serviceId, boolean prometheusMetrics, boolean healthCheck, boolean serverInfo) # Handler middleware chain configuration diff --git a/light-graphql-4j/src/main/resources/templates/graphql/pom.xml.rocker.raw b/light-graphql-4j/src/main/resources/templates/graphql/pom.xml.rocker.raw index 931d5106e..b44e3596d 100644 --- a/light-graphql-4j/src/main/resources/templates/graphql/pom.xml.rocker.raw +++ b/light-graphql-4j/src/main/resources/templates/graphql/pom.xml.rocker.raw @@ -1,12 +1,12 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) 4.0.0 - @config.get("groupId") - @config.get("artifactId") + @config.get("groupId").textValue() + @config.get("artifactId").textValue() jar - @config.get("name") - @config.get("version") + @config.get("name").textValue() + @config.get("version").textValue() 11 @@ -21,19 +21,19 @@ 2.1.3.Final 13.0 2.1.5 - @if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ + @if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ 3.1.0 } - @if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 11.2.0.3 } - @if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 8.0.16 } - @if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 42.1.1 } - @if(config.toBoolean("supportH2ForTest")){ + @if(config.get("supportH2ForTest").booleanValue()){ 1.3.176 } 2.4 @@ -226,28 +226,28 @@ rxjava ${version.rxjava} - @if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ + @if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ com.zaxxer HikariCP ${version.hikaricp} } - @if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ com.oracle ojdbc6 ${version.oracle} } - @if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ mysql mysql-connector-java ${version.mysql} } - @if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ org.postgresql postgresql @@ -261,7 +261,7 @@ ${version.junit} test - @if(config.toBoolean("supportH2ForTest")){ + @if(config.get("supportH2ForTest").booleanValue()){ com.h2database h2 diff --git a/light-graphql-4j/src/main/resources/templates/graphql/serviceYml.rocker.raw b/light-graphql-4j/src/main/resources/templates/graphql/serviceYml.rocker.raw index 24e6ece34..4fc3eb4b2 100644 --- a/light-graphql-4j/src/main/resources/templates/graphql/serviceYml.rocker.raw +++ b/light-graphql-4j/src/main/resources/templates/graphql/serviceYml.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) # Singleton service factory configuration/IoC injection singletons: # HandlerProvider implementation @@ -16,9 +16,9 @@ singletons: # - com.networknt.server.Test1ShutdownHook # GraphQL schema provider interface implementation - com.networknt.graphql.router.SchemaProvider: - - @with(schemaPackage = config.get("schemaPackage").toString() + "." + config.get("schemaClass").toString()) {@schemaPackage} -@if(config.toBoolean("supportDb")){ -@with (driverClassName = config.toString("dbInfo", "driverClassName"), jdbcUrl=config.toString("dbInfo", "jdbcUrl"), username=config.toString("dbInfo", "username"), password=config.toString("dbInfo", "password")) { + - @with(schemaPackage = config.get("schemaPackage").textValue() + "." + config.get("schemaClass").textValue()) {@schemaPackage} +@if(config.get("supportDb").booleanValue()){ +@with (driverClassName = config.path("dbInfo").path("driverClassName").textValue(), jdbcUrl=config.path("dbInfo").path("jdbcUrl").textValue(), username=config.path("dbInfo").path("username").textValue(), password=config.path("dbInfo").path("password").textValue()) { - javax.sql.DataSource: - com.zaxxer.hikari.HikariDataSource: DriverClassName: @driverClassName diff --git a/light-graphql-4j/src/test/java/com/networknt/codegen/graphql/GraphqlGeneratorTest.java b/light-graphql-4j/src/test/java/com/networknt/codegen/graphql/GraphqlGeneratorTest.java index 8797ff433..ddc90a051 100644 --- a/light-graphql-4j/src/test/java/com/networknt/codegen/graphql/GraphqlGeneratorTest.java +++ b/light-graphql-4j/src/test/java/com/networknt/codegen/graphql/GraphqlGeneratorTest.java @@ -1,9 +1,8 @@ package com.networknt.codegen.graphql; -import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; +import com.networknt.codegen.Generator; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -40,19 +39,19 @@ public static void tearDown() throws IOException { @Test public void testGeneratorWithSchema() throws IOException { - Any anyConfig = JsonIterator.parse(GraphqlGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); + JsonNode configNode = Generator.jsonMapper.readTree(GraphqlGeneratorTest.class.getResourceAsStream(configName)); try(InputStream is = GraphqlGenerator.class.getResourceAsStream(schemaName)) { String schema = convertStreamToString(is); GraphqlGenerator generator = new GraphqlGenerator(); - generator.generate(targetPath, schema, anyConfig); + generator.generate(targetPath, schema, configNode); } } @Test public void testGeneratorWithoutSchema() throws IOException { - Any anyConfig = JsonIterator.parse(GraphqlGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); + JsonNode configNode = Generator.jsonMapper.readTree(GraphqlGeneratorTest.class.getResourceAsStream(configName)); GraphqlGenerator generator = new GraphqlGenerator(); - generator.generate(targetPath, null, anyConfig); + generator.generate(targetPath, null, configNode); } static String convertStreamToString(java.io.InputStream is) { diff --git a/light-graphql-4j/src/test/resources/config.json b/light-graphql-4j/src/test/resources/config.json index 7910c4d7d..73d7d9fde 100644 --- a/light-graphql-4j/src/test/resources/config.json +++ b/light-graphql-4j/src/test/resources/config.json @@ -10,8 +10,10 @@ "enableHttp": true, "httpsPort": 8443, "enableHttps": false, + "enableHttp2": true, "enableRegistry": false, "supportDb": true, + "eclipseIDE": false, "dbInfo": { "name": "mysql", "driverClassName": "com.mysql.jdbc.Driver", diff --git a/light-hybrid-4j/pom.xml b/light-hybrid-4j/pom.xml index b6b95ffcb..bbc2a8ede 100644 --- a/light-hybrid-4j/pom.xml +++ b/light-hybrid-4j/pom.xml @@ -58,14 +58,10 @@ com.fizzed rocker-compiler provided - - - com.jsoniter - jsoniter - org.javassist - javassist + com.fasterxml.jackson.core + jackson-databind diff --git a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridGenerator.java b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridGenerator.java new file mode 100644 index 000000000..c458b90de --- /dev/null +++ b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridGenerator.java @@ -0,0 +1,19 @@ +package com.networknt.codegen.hybrid; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.networknt.codegen.Generator; + +public interface HybridGenerator extends Generator { + default String getJsonPath(JsonNode config, String defaultValue) { + String jsonPath = defaultValue == null ? "/api/json" : defaultValue; + JsonNode jsonNode = config.get("jsonPath"); + if(jsonNode == null) { + ((ObjectNode)config).put("jsonPath", jsonPath); + } else { + jsonPath = jsonNode.asText(); + } + return jsonPath; + } + +} diff --git a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServerGenerator.java b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServerGenerator.java index ac8871b80..5dc58d913 100644 --- a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServerGenerator.java +++ b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServerGenerator.java @@ -1,7 +1,6 @@ package com.networknt.codegen.hybrid; -import com.jsoniter.any.Any; -import com.networknt.codegen.Generator; +import com.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.io.InputStream; @@ -14,42 +13,47 @@ /** * Created by steve on 28/04/17. */ -public class HybridServerGenerator implements Generator { +public class HybridServerGenerator implements HybridGenerator { + public static final String FRAMEWORK="light-hybrid-4j-server"; @Override public String getFramework() { - return "light-hybrid-4j-server"; + return FRAMEWORK; } @Override - public void generate(String targetPath, Object model, Any config) throws IOException { + public void generate(String targetPath, Object model, JsonNode config) throws IOException { // whoever is calling this needs to make sure that model is converted to Map - String rootPackage = config.get("rootPackage").toString(); - String modelPackage = config.get("modelPackage").toString(); - String handlerPackage = config.get("handlerPackage").toString(); - - boolean enableHttp = config.toBoolean("enableHttp"); - String httpPort = config.toString("httpPort"); - boolean enableHttps = config.toBoolean("enableHttps"); - String httpsPort = config.toString("httpsPort"); - boolean enableHttp2 = config.toBoolean("enableHttp2"); - boolean enableRegistry = config.toBoolean("enableRegistry"); - boolean eclipseIDE = config.toBoolean("eclipseIDE"); - String dockerOrganization = config.toString("dockerOrganization"); - String artifactId = config.toString("artifactId"); - String version = config.toString("version"); - if(dockerOrganization == null || dockerOrganization.length() == 0) dockerOrganization = "networknt"; - - boolean supportClient = config.toBoolean("supportClient"); - String serviceId = config.get("groupId").toString().trim() + "." + artifactId.trim() + "-" + config.get("version").toString().trim(); - boolean prometheusMetrics = config.toBoolean("prometheusMetrics"); - boolean skipHealthCheck = config.toBoolean("skipHealthCheck"); - boolean skipServerInfo = config.toBoolean("skipServerInfo"); - String jsonPath = config.get("jsonPath").toString(); - boolean kafkaProducer = config.toBoolean("kafkaProducer"); - boolean kafkaConsumer = config.toBoolean("kafkaConsumer"); - boolean supportAvro = config.toBoolean("supportAvro"); - String kafkaTopic = config.get("kafkaTopic").toString(); + String rootPackage = getRootPackage(config, null); + String modelPackage = getModelPackage(config, null); + String handlerPackage = getHandlerPackage(config, null); + boolean overwriteHandler = isOverwriteHandler(config, null); + boolean overwriteHandlerTest = isOverwriteHandlerTest(config, null); + boolean overwriteModel = isOverwriteModel(config, null); + boolean generateModelOnly = isGenerateModelOnly(config, null); + boolean enableHttp = isEnableHttp(config, null); + String httpPort = getHttpPort(config, null); + boolean enableHttps = isEnableHttps(config, null); + String httpsPort = getHttpsPort(config, null); + boolean enableHttp2 = isEnableHttp2(config, null); + boolean enableRegistry = isEnableRegistry(config, null); + boolean eclipseIDE = isEclipseIDE(config, null); + boolean supportClient = isSupportClient(config, null); + boolean prometheusMetrics = isPrometheusMetrics(config, null); + String dockerOrganization = getDockerOrganization(config, null); + String version = getVersion(config, null); + String groupId = getGroupId(config, null); + String artifactId = getArtifactId(config, null); + String serviceId = groupId + "." + artifactId + "-" + version; + boolean specChangeCodeReGenOnly = isSpecChangeCodeReGenOnly(config, null); + boolean enableParamDescription = isEnableParamDescription(config, null); + boolean skipPomFile = isSkipPomFile(config, null); + boolean kafkaProducer = isKafkaProducer(config, null); + boolean kafkaConsumer = isKafkaConsumer(config, null); + boolean supportAvro = isSupportAvro(config, null); + String kafkaTopic = getKafkaTopic(config, null); + String decryptOption = getDecryptOption(config, null); + String jsonPath = getJsonPath(config, null); transfer(targetPath, "", "pom.xml", templates.hybrid.server.pom.template(config)); transferMaven(targetPath); @@ -64,7 +68,7 @@ public void generate(String targetPath, Object model, Any config) throws IOExcep } transfer(targetPath, "docker", "Dockerfile", templates.hybrid.server.dockerfile.template(config, expose)); transfer(targetPath, "docker", "Dockerfile-Slim", templates.hybrid.server.dockerfileslim.template(config, expose)); - transfer(targetPath, "", "build.sh", templates.hybrid.server.buildSh.template(dockerOrganization, config.get("groupId") + "." + config.get("artifactId") + "-" + config.get("version"))); + transfer(targetPath, "", "build.sh", templates.hybrid.server.buildSh.template(dockerOrganization, serviceId)); transfer(targetPath, "", ".gitignore", templates.hybrid.gitignore.template()); transfer(targetPath, "", "README.md", templates.hybrid.server.README.template()); transfer(targetPath, "", "LICENSE", templates.hybrid.LICENSE.template()); @@ -133,11 +137,12 @@ public void generate(String targetPath, Object model, Any config) throws IOExcep } transfer(targetPath, ("src.main.resources.config").replace(".", separator), "handler.yml", - templates.hybrid.handlerYml.template(serviceId, handlerPackage, jsonPath, prometheusMetrics, !skipHealthCheck, !skipServerInfo)); + templates.hybrid.handlerYml.template(serviceId, handlerPackage, jsonPath, prometheusMetrics)); transfer(targetPath, ("src.main.resources.config").replace(".", separator), "rpc-router.yml", templates.hybrid.rpcRouterYml.template(handlerPackage, jsonPath)); } + } diff --git a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServiceGenerator.java b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServiceGenerator.java index 6f62203e0..63dc80ee9 100644 --- a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServiceGenerator.java +++ b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServiceGenerator.java @@ -1,11 +1,11 @@ package com.networknt.codegen.hybrid; -import com.jsoniter.ValueType; -import com.jsoniter.any.Any; -import com.jsoniter.output.JsonStream; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.networknt.codegen.Generator; import org.apache.commons.text.StringEscapeUtils; +import java.io.ByteArrayInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -22,7 +22,7 @@ /** * Created by steve on 28/04/17. */ -public class HybridServiceGenerator implements Generator { +public class HybridServiceGenerator implements HybridGenerator { @Override public String getFramework() { @@ -30,28 +30,38 @@ public String getFramework() { } @Override - public void generate(String targetPath, Object model, Any config) throws IOException { + public void generate(String targetPath, Object model, JsonNode config) throws IOException { // whoever is calling this needs to make sure that model is converted to Map - String handlerPackage = config.get("handlerPackage").toString(); - boolean overwriteHandler = config.toBoolean("overwriteHandler"); - boolean overwriteHandlerTest = config.toBoolean("overwriteHandlerTest"); - boolean enableHttp = config.toBoolean("enableHttp"); - boolean enableHttps = config.toBoolean("enableHttps"); - boolean enableHttp2 = config.toBoolean("enableHttp2"); - boolean enableRegistry = config.toBoolean("enableRegistry"); - boolean eclipseIDE = config.toBoolean("eclipseIDE"); - boolean supportClient = config.toBoolean("supportClient"); - String artifactId = config.toString("artifactId"); - String version = config.toString("version"); - String serviceId = config.get("groupId").toString().trim() + "." + artifactId.trim() + "-" + config.get("version").toString().trim(); - boolean prometheusMetrics = config.toBoolean("prometheusMetrics"); - boolean skipHealthCheck = config.toBoolean("skipHealthCheck"); - boolean skipServerInfo = config.toBoolean("skipServerInfo"); - String jsonPath = config.get("jsonPath").toString(); - boolean kafkaProducer = config.toBoolean("kafkaProducer"); - boolean kafkaConsumer = config.toBoolean("kafkaConsumer"); - boolean supportAvro = config.toBoolean("supportAvro"); - String kafkaTopic = config.get("kafkaTopic").toString(); + String rootPackage = getRootPackage(config, null); + String modelPackage = getModelPackage(config, null); + String handlerPackage = getHandlerPackage(config, null); + boolean overwriteHandler = isOverwriteHandler(config, null); + boolean overwriteHandlerTest = isOverwriteHandlerTest(config, null); + boolean overwriteModel = isOverwriteModel(config, null); + boolean generateModelOnly = isGenerateModelOnly(config, null); + boolean enableHttp = isEnableHttp(config, null); + String httpPort = getHttpPort(config, null); + boolean enableHttps = isEnableHttps(config, null); + String httpsPort = getHttpsPort(config, null); + boolean enableHttp2 = isEnableHttp2(config, null); + boolean enableRegistry = isEnableRegistry(config, null); + boolean eclipseIDE = isEclipseIDE(config, null); + boolean supportClient = isSupportClient(config, null); + boolean prometheusMetrics = isPrometheusMetrics(config, null); + String dockerOrganization = getDockerOrganization(config, null); + String version = getVersion(config, null); + String groupId = getGroupId(config, null); + String artifactId = getArtifactId(config, null); + String serviceId = groupId + "." + artifactId + "-" + version; + boolean specChangeCodeReGenOnly = isSpecChangeCodeReGenOnly(config, null); + boolean enableParamDescription = isEnableParamDescription(config, null); + boolean skipPomFile = isSkipPomFile(config, null); + boolean kafkaProducer = isKafkaProducer(config, null); + boolean kafkaConsumer = isKafkaConsumer(config, null); + boolean supportAvro = isSupportAvro(config, null); + String kafkaTopic = getKafkaTopic(config, null); + String decryptOption = getDecryptOption(config, null); + String jsonPath = getJsonPath(config, null); transfer(targetPath, "", "pom.xml", templates.hybrid.service.pom.template(config)); transferMaven(targetPath); @@ -93,14 +103,15 @@ public void generate(String targetPath, Object model, Any config) throws IOExcep // handler Map services = new HashMap(); - Any anyModel = (Any)model; - String host = anyModel.toString("host"); - String service = anyModel.toString("service"); - List items = anyModel.get("action").asList(); - if(items != null && items.size() > 0) { - for(Any item : items) { - Any any = item.get("example"); - String example = any.valueType() != ValueType.INVALID ? StringEscapeUtils.escapeJson(any.toString()).trim() : ""; + JsonNode anyModel = (JsonNode)model; + String host = anyModel.get("host").textValue(); + String service = anyModel.get("service").textValue(); + JsonNode jsonNode = anyModel.get("action"); + if(jsonNode != null && jsonNode.isArray()) { + ArrayNode items = (ArrayNode)jsonNode; + for(JsonNode item : items) { + JsonNode any = item.get("example"); + String example = any != null ? StringEscapeUtils.escapeJson(any.toString()).trim() : ""; if(!overwriteHandler && checkExist(targetPath, ("src.main.java." + handlerPackage).replace(".", separator), item.get("handler") + ".java")) { continue; } @@ -108,18 +119,18 @@ public void generate(String targetPath, Object model, Any config) throws IOExcep String sId = host + "/" + service + "/" + item.get("name") + "/" + item.get("version"); Map map = new HashMap<>(); map.put("schema", item.get("schema")); - Any anyScope = item.get("scope"); - String scope = anyScope.valueType() != ValueType.INVALID ? anyScope.toString().trim() : null; + JsonNode anyScope = item.get("scope"); + String scope = anyScope != null ? anyScope.textValue().trim() : null; if(scope != null) map.put("scope", scope); - Any anySkipAuth = item.get("skipAuth"); - Boolean skipAuth = anySkipAuth.valueType() != ValueType.INVALID ? anySkipAuth.toBoolean() : null; + JsonNode anySkipAuth = item.get("skipAuth"); + Boolean skipAuth = anySkipAuth != null ? anySkipAuth.booleanValue() : false; if(skipAuth != null) map.put("skipAuth", skipAuth); services.put(sId, map); } // handler test cases transfer(targetPath, ("src.test.java." + handlerPackage + ".").replace(".", separator), "TestServer.java", templates.hybrid.testServer.template(handlerPackage)); - for(Any item : items) { + for(JsonNode item : items) { if(!overwriteHandlerTest && checkExist(targetPath, ("src.test.java." + handlerPackage).replace(".", separator), item.get("handler") + "Test.java")) { continue; } @@ -147,12 +158,14 @@ public void generate(String targetPath, Object model, Any config) throws IOExcep } transfer(targetPath, ("src.test.resources.config").replace(".", separator), "handler.yml", - templates.hybrid.handlerYml.template(serviceId, handlerPackage, jsonPath, prometheusMetrics, !skipHealthCheck, !skipServerInfo)); + templates.hybrid.handlerYml.template(serviceId, handlerPackage, jsonPath, prometheusMetrics)); transfer(targetPath, ("src.test.resources.config").replace(".", separator), "rpc-router.yml", templates.hybrid.rpcRouterYml.template(handlerPackage, jsonPath)); // write the generated schema into the config folder for schema validation. - JsonStream.serialize(services, new FileOutputStream(FileSystems.getDefault().getPath(targetPath, ("src.main.resources").replace(".", separator), "schema.json").toFile())); + try (InputStream is = new ByteArrayInputStream(Generator.jsonMapper.writeValueAsBytes(services))) { + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources").replace(".", separator), "schema.json")); + } } } diff --git a/light-hybrid-4j/src/main/resources/templates/hybrid/handler.rocker.raw b/light-hybrid-4j/src/main/resources/templates/hybrid/handler.rocker.raw index 8d62835ea..411143d91 100644 --- a/light-hybrid-4j/src/main/resources/templates/hybrid/handler.rocker.raw +++ b/light-hybrid-4j/src/main/resources/templates/hybrid/handler.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (String handlerPackage, String host, String service, Any item, String example) +@import com.fasterxml.jackson.databind.JsonNode +@args (String handlerPackage, String host, String service, JsonNode item, String example) package @handlerPackage; import com.networknt.utility.NioUtils; @@ -12,8 +12,8 @@ import io.undertow.server.HttpServerExchange; For more information on how to write business handlers, please check the link below. https://doc.networknt.com/development/business-handler/hybrid/ */ -@@ServiceHandler(id="@host/@service/@item.get("name")/@item.get("version")") -public class @item.get("handler") implements Handler { +@@ServiceHandler(id="@host/@service/@item.get("name").textValue()/@item.get("version").textValue()") +public class @item.get("handler").textValue() implements Handler { @@Override public ByteBuffer handle(HttpServerExchange exchange, Object input) { return NioUtils.toByteBuffer("@example"); diff --git a/light-hybrid-4j/src/main/resources/templates/hybrid/handlerTest.rocker.raw b/light-hybrid-4j/src/main/resources/templates/hybrid/handlerTest.rocker.raw index 1af4cab22..c064e886d 100644 --- a/light-hybrid-4j/src/main/resources/templates/hybrid/handlerTest.rocker.raw +++ b/light-hybrid-4j/src/main/resources/templates/hybrid/handlerTest.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (String handlerPackage, String host, String service, Any item) +@import com.fasterxml.jackson.databind.JsonNode +@args (String handlerPackage, String host, String service, JsonNode item) package @handlerPackage; import com.networknt.client.Http2Client; @@ -23,11 +23,11 @@ import java.net.URI; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; -@with(s1 = item.get("handler") + "Test") {public class @s1} { +@with(s1 = item.get("handler").textValue() + "Test") {public class @s1} { @@ClassRule public static TestServer server = TestServer.getInstance(); - @with(s2 = item.get("handler") + ".class") {static final Logger logger = LoggerFactory.getLogger(@s2); } + @with(s2 = item.get("handler").textValue() + ".class") {static final Logger logger = LoggerFactory.getLogger(@s2); } static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2(); static final boolean enableHttps = server.getServerConfig().isEnableHttps(); static final int httpPort = server.getServerConfig().getHttpPort(); @@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicReference; static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort; @@Test - @with(s3 = item.get("handler") + "()") {public void test@s3} throws ClientException, ApiException { + @with(s3 = item.get("handler").textValue() + "()") {public void test@s3} throws ClientException, ApiException { /* final Http2Client client = Http2Client.getInstance(); final CountDownLatch latch = new CountDownLatch(1); diff --git a/light-hybrid-4j/src/main/resources/templates/hybrid/handlerYml.rocker.raw b/light-hybrid-4j/src/main/resources/templates/hybrid/handlerYml.rocker.raw index 2e7565b5c..fc0f90920 100644 --- a/light-hybrid-4j/src/main/resources/templates/hybrid/handlerYml.rocker.raw +++ b/light-hybrid-4j/src/main/resources/templates/hybrid/handlerYml.rocker.raw @@ -1,7 +1,6 @@ @import java.util.Map @import java.util.List -@import com.jsoniter.any.Any -@args (String serviceId, String handlerPackage, String jsonPath, boolean prometheusMetrics, boolean healthCheck, boolean serverInfo) +@args (String serviceId, String handlerPackage, String jsonPath, boolean prometheusMetrics) # Handler middleware chain configuration #---------------------------------------- @@ -84,16 +83,16 @@ paths: exec: - default -@if(healthCheck){ - path: '/health/${server.serviceId:@serviceId}' + - path: '/health/${server.serviceId:@serviceId}' method: 'get' exec: - health -} -@if(serverInfo){ - path: '/server/info' + + - path: '/server/info' method: 'get' exec: - info -} + @if(prometheusMetrics){ - path: '/prometheus' method: 'get' exec: diff --git a/light-hybrid-4j/src/main/resources/templates/hybrid/server/dockerfile.rocker.raw b/light-hybrid-4j/src/main/resources/templates/hybrid/server/dockerfile.rocker.raw index 8cd8b3681..6895ad013 100644 --- a/light-hybrid-4j/src/main/resources/templates/hybrid/server/dockerfile.rocker.raw +++ b/light-hybrid-4j/src/main/resources/templates/hybrid/server/dockerfile.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config, String expose) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config, String expose) FROM azul/zulu-openjdk-alpine:11 as packager RUN { \ @@ -28,5 +28,5 @@ ENV JAVA_MINIMAL=/opt/jre ENV PATH="$PATH:$JAVA_MINIMAL/bin" COPY --from=packager "$JAVA_MINIMAL" "$JAVA_MINIMAL" -@with (name = config.get("artifactId") + "-" + config.get("version") + ".jar") {COPY /target/@name server.jar} +@with (name = config.get("artifactId").textValue() + "-" + config.get("version").textValue() + ".jar") {COPY /target/@name server.jar} CMD ["/bin/sh","-c","java -Dlight-4j-config-dir=/config -Dlogback.configurationFile=/config/logback.xml -cp /server.jar:/service/* com.networknt.server.Server"] diff --git a/light-hybrid-4j/src/main/resources/templates/hybrid/server/dockerfileslim.rocker.raw b/light-hybrid-4j/src/main/resources/templates/hybrid/server/dockerfileslim.rocker.raw index 917653fc7..696c869aa 100644 --- a/light-hybrid-4j/src/main/resources/templates/hybrid/server/dockerfileslim.rocker.raw +++ b/light-hybrid-4j/src/main/resources/templates/hybrid/server/dockerfileslim.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config, String expose) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config, String expose) FROM openjdk:11.0.3-slim -@with (name = config.get("artifactId") + "-" + config.get("version") + ".jar") {ADD /target/@name server.jar} +@with (name = config.get("artifactId").textValue() + "-" + config.get("version").textValue() + ".jar") {ADD /target/@name server.jar} CMD ["/bin/sh","-c","java -Dlight-4j-config-dir=/config -Dlogback.configurationFile=/config/logback.xml -cp /server.jar:/service/* com.networknt.server.Server"] diff --git a/light-hybrid-4j/src/main/resources/templates/hybrid/server/pom.xml.rocker.raw b/light-hybrid-4j/src/main/resources/templates/hybrid/server/pom.xml.rocker.raw index 44d5cbebf..af549d281 100644 --- a/light-hybrid-4j/src/main/resources/templates/hybrid/server/pom.xml.rocker.raw +++ b/light-hybrid-4j/src/main/resources/templates/hybrid/server/pom.xml.rocker.raw @@ -1,12 +1,12 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) 4.0.0 - @config.get("groupId") - @config.get("artifactId") + @config.get("groupId").textValue() + @config.get("artifactId").textValue() jar - @config.get("name") - @config.get("version") + @config.get("artifactId").textValue() + @config.get("version").textValue() 11 @@ -20,25 +20,25 @@ 4.12 2.1.3.Final 1.0.29 - @if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ + @if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ 3.1.0 } - @if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 11.2.0.3 } - @if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 8.0.16 } - @if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 42.1.1 } - @if(config.toBoolean("supportH2ForTest")){ + @if(config.get("supportH2ForTest").booleanValue()){ 1.3.176 } - @if(config.toBoolean("supportAvro")){ + @if(config.get("supportAvro").booleanValue()){ 5.3.3 } - @if(config.toBoolean("kafkaProducer")||config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaProducer").booleanValue() || config.get("kafkaConsumer").booleanValue()){ 2.5.0 } 2.4 @@ -114,21 +114,21 @@ json-schema-validator ${version.json-schema-validator} - @if(config.toBoolean("kafkaProducer")||config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaProducer").booleanValue() || config.get("kafkaConsumer").booleanValue()){ com.networknt kafka-common ${version.light-4j} } - @if(config.toBoolean("kafkaProducer")){ + @if(config.get("kafkaProducer").booleanValue()){ com.networknt kafka-producer ${version.light-4j} } - @if(config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaConsumer").booleanValue()){ com.networknt kafka-consumer @@ -170,35 +170,35 @@ undertow-core ${version.undertow} - @if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ + @if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ com.zaxxer HikariCP ${version.hikaricp} } - @if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ com.oracle ojdbc6 ${version.oracle} } - @if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ mysql mysql-connector-java ${version.mysql} } - @if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ org.postgresql postgresql ${version.postgres} } - @if(config.toBoolean("supportAvro")){ + @if(config.get("supportAvro").booleanValue()){ io.confluent kafka-schema-registry-client @@ -212,7 +212,7 @@ ${version.junit} test - @if(config.toBoolean("supportH2ForTest")){ + @if(config.get("supportH2ForTest").booleanValue()){ com.h2database h2 @@ -320,7 +320,7 @@ - @if(config.toBoolean("supportAvro")){ + @if(config.get("supportAvro").booleanValue()){ confluent http://packages.confluent.io/maven/ diff --git a/light-hybrid-4j/src/main/resources/templates/hybrid/service/pom.xml.rocker.raw b/light-hybrid-4j/src/main/resources/templates/hybrid/service/pom.xml.rocker.raw index 77813d113..2c2bb4341 100644 --- a/light-hybrid-4j/src/main/resources/templates/hybrid/service/pom.xml.rocker.raw +++ b/light-hybrid-4j/src/main/resources/templates/hybrid/service/pom.xml.rocker.raw @@ -1,12 +1,12 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) 4.0.0 - @config.get("groupId") - @config.get("artifactId") + @config.get("groupId").textValue() + @config.get("artifactId").textValue() jar - @config.get("name") - @config.get("version") + @config.get("artifactId").textValue() + @config.get("version").textValue() 11 @@ -20,25 +20,25 @@ 4.12 2.1.3.Final 1.0.29 - @if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ + @if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ 3.1.0 } - @if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 11.2.0.3 } - @if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 8.0.16 } - @if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 42.1.1 } - @if(config.toBoolean("supportH2ForTest")){ + @if(config.get("supportH2ForTest").booleanValue()){ 1.3.176 } - @if(config.toBoolean("supportAvro")){ + @if(config.get("supportAvro").booleanValue()){ 5.3.3 } - @if(config.toBoolean("kafkaProducer")||config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaProducer").booleanValue() || config.get("kafkaConsumer").booleanValue()){ 2.5.0 } 2.4 @@ -104,21 +104,21 @@ json-schema-validator ${version.json-schema-validator} - @if(config.toBoolean("kafkaProducer")||config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaProducer").booleanValue() || config.get("kafkaConsumer").booleanValue()){ com.networknt kafka-common ${version.light-4j} } - @if(config.toBoolean("kafkaProducer")){ + @if(config.get("kafkaProducer").booleanValue()){ com.networknt kafka-producer ${version.light-4j} } - @if(config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaConsumer").booleanValue()){ com.networknt kafka-consumer @@ -155,35 +155,35 @@ undertow-core ${version.undertow} - @if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ + @if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ com.zaxxer HikariCP ${version.hikaricp} } - @if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ com.oracle ojdbc6 ${version.oracle} } - @if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ mysql mysql-connector-java ${version.mysql} } - @if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ org.postgresql postgresql ${version.postgres} } - @if(config.toBoolean("supportAvro")){ + @if(config.get("supportAvro").booleanValue()){ io.confluent kafka-schema-registry-client @@ -197,7 +197,7 @@ ${version.junit} test - @if(config.toBoolean("supportH2ForTest")){ + @if(config.get("supportH2ForTest").booleanValue()){ com.h2database h2 @@ -305,7 +305,7 @@ - @if(config.toBoolean("supportAvro")){ + @if(config.get("supportAvro").booleanValue()){ confluent http://packages.confluent.io/maven/ diff --git a/light-hybrid-4j/src/main/resources/templates/hybrid/serviceYml.rocker.raw b/light-hybrid-4j/src/main/resources/templates/hybrid/serviceYml.rocker.raw index b17a5897e..b4195519a 100644 --- a/light-hybrid-4j/src/main/resources/templates/hybrid/serviceYml.rocker.raw +++ b/light-hybrid-4j/src/main/resources/templates/hybrid/serviceYml.rocker.raw @@ -1,40 +1,40 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) # Singleton service factory configuration/IoC injection singletons: # StartupHookProvider implementations, there are one to many and they are called in the same sequence defined. - com.networknt.server.StartupHookProvider: # registry all service handlers by from annotations - com.networknt.rpc.router.RpcStartupHookProvider - @if(config.toBoolean("kafkaProducer")){ + @if(config.get("kafkaProducer").booleanValue()){ # Kafka producer startup hook example # - net.lightapi.portal.user.command.UserCommandStartup } - @if(config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaConsumer").booleanValue()){ # Kafka consumer startup hook example # - net.lightapi.portal.user.query.UserQueryStartup } # ShutdownHookProvider implementations, there are one to many and they are called in the same sequence defined. - com.networknt.server.ShutdownHookProvider: - @if(config.toBoolean("kafkaProducer")){ + @if(config.get("kafkaProducer").booleanValue()){ # Kafka producer startup hook example # - net.lightapi.portal.user.command.UserCommandShutdown } - @if(config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaConsumer").booleanValue()){ # Kafka consumer startup hook example # - net.lightapi.portal.user.query.UserQueryShutdown } -@if(config.toBoolean("kafkaProducer")){ +@if(config.get("kafkaProducer").booleanValue()){ - com.networknt.kafka.producer.LightProducer: - com.networknt.kafka.producer.TransactionalProducer } -@if(config.toBoolean("kafkaConsumer")){ +@if(config.get("kafkaConsumer").booleanValue()){ - com.networknt.kafka.streams.LightStreams: # Kafka streams processor example # - net.lightapi.portal.user.query.UserQueryStreams } -@if(config.toBoolean("supportDb")){ -@with (driverClassName = config.toString("dbInfo", "driverClassName"), jdbcUrl=config.toString("dbInfo", "jdbcUrl"), username=config.toString("dbInfo", "username"), password=config.toString("dbInfo", "password")) { +@if(config.get("supportDb").booleanValue()){ +@with (driverClassName = config.path("dbInfo").path("driverClassName").textValue(), jdbcUrl=config.path("dbInfo").path("jdbcUrl").textValue(), username=config.path("dbInfo").path("username").textValue(), password=config.path("dbInfo").path("password").textValue()) { - javax.sql.DataSource: - com.zaxxer.hikari.HikariDataSource: DriverClassName: @driverClassName diff --git a/light-hybrid-4j/src/test/java/com/networknt/codegen/hybrid/HybridServerGeneratorTest.java b/light-hybrid-4j/src/test/java/com/networknt/codegen/hybrid/HybridServerGeneratorTest.java index 8aa743d21..8cfc8f216 100644 --- a/light-hybrid-4j/src/test/java/com/networknt/codegen/hybrid/HybridServerGeneratorTest.java +++ b/light-hybrid-4j/src/test/java/com/networknt/codegen/hybrid/HybridServerGeneratorTest.java @@ -1,9 +1,8 @@ package com.networknt.codegen.hybrid; -import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; +import com.networknt.codegen.Generator; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -11,7 +10,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.Map; /** * Created by steve on 28/04/17. @@ -36,10 +34,9 @@ public static void tearDown() throws IOException { @Test public void testGenerator() throws IOException { - Any anyConfig = JsonIterator.parse(HybridServerGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - + JsonNode config = Generator.jsonMapper.readTree(HybridServerGeneratorTest.class.getResourceAsStream(configName)); HybridServerGenerator generator = new HybridServerGenerator(); - generator.generate(targetPath, null, anyConfig); + generator.generate(targetPath, null, config); } @Test diff --git a/light-hybrid-4j/src/test/java/com/networknt/codegen/hybrid/HybridServiceGeneratorTest.java b/light-hybrid-4j/src/test/java/com/networknt/codegen/hybrid/HybridServiceGeneratorTest.java index 1ae04db32..6bec4bf0f 100644 --- a/light-hybrid-4j/src/test/java/com/networknt/codegen/hybrid/HybridServiceGeneratorTest.java +++ b/light-hybrid-4j/src/test/java/com/networknt/codegen/hybrid/HybridServiceGeneratorTest.java @@ -1,9 +1,7 @@ package com.networknt.codegen.hybrid; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.codegen.Generator; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -11,7 +9,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.Map; /** * Created by steve on 28/04/17. @@ -34,11 +31,10 @@ public static void tearDown() throws IOException { @Test public void testGenerator() throws IOException { - Any anyConfig = JsonIterator.parse(HybridServiceGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - Any anyModel = JsonIterator.parse(HybridServiceGeneratorTest.class.getResourceAsStream(schemaName), 1024).readAny(); - + JsonNode config = Generator.jsonMapper.readTree(HybridServiceGeneratorTest.class.getResourceAsStream(configName)); + JsonNode model = Generator.jsonMapper.readTree(HybridServiceGeneratorTest.class.getResourceAsStream(schemaName)); HybridServiceGenerator generator = new HybridServiceGenerator(); - generator.generate(targetPath, anyModel, anyConfig); + generator.generate(targetPath, model, config); } @Test diff --git a/light-rest-4j/pom.xml b/light-rest-4j/pom.xml index 78c672651..33a706e23 100644 --- a/light-rest-4j/pom.xml +++ b/light-rest-4j/pom.xml @@ -48,12 +48,8 @@ - com.jsoniter - jsoniter - - - org.javassist - javassist + com.fasterxml.jackson.core + jackson-databind org.slf4j @@ -80,10 +76,6 @@ io.github.classgraph classgraph - - io.swagger.core.v3 - swagger-core - ch.qos.logback logback-classic diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java index 365dc23d2..5da3a0b50 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java @@ -1,18 +1,18 @@ package com.networknt.codegen.rest; -import com.jsoniter.JsonIterator; -import com.jsoniter.ValueType; -import com.jsoniter.any.Any; -import com.jsoniter.output.JsonStream; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.networknt.codegen.Generator; import com.networknt.codegen.Utils; +import com.networknt.config.ConfigException; +import com.networknt.config.JsonMapper; import com.networknt.jsonoverlay.Overlay; import com.networknt.oas.OpenApiParser; import com.networknt.oas.model.*; import com.networknt.oas.model.impl.OpenApi3Impl; +import com.networknt.oas.model.impl.SchemaImpl; import com.networknt.utility.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.lang.model.SourceVersion; import java.io.ByteArrayInputStream; @@ -23,32 +23,14 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.util.*; -import java.util.Map.Entry; import java.util.stream.Collectors; -import static com.networknt.codegen.Generator.copyFile; import static java.io.File.separator; -/** - * The input for OpenAPI 3.0 generator include config with json format and OpenAPI specification in yaml format. - * - * The model is OpenAPI spec in yaml format. And config file is config.json in JSON format. - * - * @author Steve Hu - */ public class OpenApiGenerator implements Generator { - private Map typeMapping = new HashMap<>(); - // optional generation parameters. if not set, they use default values as - boolean prometheusMetrics = false; - boolean skipHealthCheck = false; - boolean skipServerInfo = false; - boolean specChangeCodeReGenOnly = false; - boolean enableParamDescription = true; - boolean generateModelOnly = false; - boolean generateValuesYml = false; - boolean skipPomFile = false; + boolean enableParamDescription = false; public OpenApiGenerator() { typeMapping.put("array", "java.util.List"); @@ -82,45 +64,37 @@ public String getFramework() { * @throws IOException IO Exception occurs during code generation */ @Override - public void generate(final String targetPath, Object model, Any config) throws IOException { + public void generate(final String targetPath, Object model, JsonNode config) throws IOException { // whoever is calling this needs to make sure that model is converted to Map - String rootPackage = config.toString("rootPackage").trim(); - final String modelPackage = config.toString("modelPackage").trim(); - String handlerPackage = config.toString("handlerPackage").trim(); - - boolean overwriteHandler = config.toBoolean("overwriteHandler"); - boolean overwriteHandlerTest = config.toBoolean("overwriteHandlerTest"); - boolean overwriteModel = config.toBoolean("overwriteModel"); - generateModelOnly = config.toBoolean("generateModelOnly"); - - boolean enableHttp = config.toBoolean("enableHttp"); - String httpPort = config.toString("httpPort").trim(); - boolean enableHttps = config.toBoolean("enableHttps"); - String httpsPort = config.toString("httpsPort").trim(); - boolean enableHttp2 = config.toBoolean("enableHttp2"); - - boolean enableRegistry = config.toBoolean("enableRegistry"); - boolean eclipseIDE = config.toBoolean("eclipseIDE"); - boolean supportClient = config.toBoolean("supportClient"); - String dockerOrganization = config.toString("dockerOrganization").trim(); - - prometheusMetrics = config.toBoolean("prometheusMetrics"); - skipHealthCheck = config.toBoolean("skipHealthCheck"); - skipServerInfo = config.toBoolean("skipServerInfo"); - specChangeCodeReGenOnly = config.toBoolean("specChangeCodeReGenOnly"); - enableParamDescription = config.toBoolean("enableParamDescription"); - skipPomFile = config.toBoolean("skipPomFile"); - String artifactId = config.toString("artifactId"); - String version = config.toString("version").trim(); - String serviceId = config.get("groupId").toString().trim() + "." + artifactId.trim() + "-" + config.get("version").toString().trim(); - boolean kafkaProducer = config.toBoolean("kafkaProducer"); - boolean kafkaConsumer = config.toBoolean("kafkaConsumer"); - boolean supportAvro = config.toBoolean("supportAvro"); - String kafkaTopic = config.get("kafkaTopic").toString(); - - if (dockerOrganization == null || dockerOrganization.length() == 0) { - dockerOrganization = "networknt"; - } + String rootPackage = getRootPackage(config, null); + String modelPackage = getModelPackage(config, null); + String handlerPackage = getHandlerPackage(config, null); + boolean overwriteHandler = isOverwriteHandler(config, null); + boolean overwriteHandlerTest = isOverwriteHandlerTest(config, null); + boolean overwriteModel = isOverwriteModel(config, null); + boolean generateModelOnly = isGenerateModelOnly(config, null); + boolean enableHttp = isEnableHttp(config, null); + String httpPort = getHttpPort(config, null); + boolean enableHttps = isEnableHttps(config, null); + String httpsPort = getHttpsPort(config, null); + boolean enableHttp2 = isEnableHttp2(config, null); + boolean enableRegistry = isEnableRegistry(config, null); + boolean eclipseIDE = isEclipseIDE(config, null); + boolean supportClient = isSupportClient(config, null); + boolean prometheusMetrics = isPrometheusMetrics(config, null); + String dockerOrganization = getDockerOrganization(config, null); + String version = getVersion(config, null); + String groupId = getGroupId(config, null); + String artifactId = getArtifactId(config, null); + String serviceId = groupId + "." + artifactId + "-" + version; + boolean specChangeCodeReGenOnly = isSpecChangeCodeReGenOnly(config, null); + boolean enableParamDescription = isEnableParamDescription(config, null); + boolean skipPomFile = isSkipPomFile(config, null); + boolean kafkaProducer = isKafkaProducer(config, null); + boolean kafkaConsumer = isKafkaConsumer(config, null); + boolean supportAvro = isSupportAvro(config, null); + String kafkaTopic = getKafkaTopic(config, null); + String decryptOption = getDecryptOption(config, null); // get the list of operations for this model List> operationList = getOperationList(model); @@ -148,7 +122,7 @@ public void generate(final String targetPath, Object model, Any config) throws I transfer(targetPath, "docker", "Dockerfile", templates.rest.dockerfile.template(config, expose)); transfer(targetPath, "docker", "Dockerfile-Slim", templates.rest.dockerfileslim.template(config, expose)); transfer(targetPath, "", "build.sh", templates.rest.buildSh.template(dockerOrganization, serviceId)); - transfer(targetPath, "", "kubernetes.yml", templates.rest.kubernetes.template(dockerOrganization, serviceId, config.get("artifactId").toString().trim(), expose, version)); + transfer(targetPath, "", "kubernetes.yml", templates.rest.kubernetes.template(dockerOrganization, serviceId, config.get("artifactId").textValue(), expose, version)); transfer(targetPath, "", ".gitignore", templates.rest.gitignore.template()); transfer(targetPath, "", "README.md", templates.rest.README.template()); transfer(targetPath, "", "LICENSE", templates.rest.LICENSE.template()); @@ -208,34 +182,26 @@ public void generate(final String targetPath, Object model, Any config) throws I } // routing handler transfer(targetPath, ("src.main.resources.config").replace(".", separator), "handler.yml", - templates.rest.openapi.handlerYml.template(serviceId, handlerPackage, operationList, prometheusMetrics, !skipHealthCheck, !skipServerInfo)); + templates.rest.openapi.handlerYml.template(serviceId, handlerPackage, operationList, prometheusMetrics)); } // model - Any anyComponents; - if (model instanceof Any) { - anyComponents = ((Any)model).get("components"); - } else if (model instanceof String) { - // this must be yaml format and we need to convert to json for JsonIterator. - OpenApi3 openApi3 = null; - try { - openApi3 = (OpenApi3)new OpenApiParser().parse((String)model, new URL("https://oas.lightapi.net/")); - } catch (MalformedURLException e) { - throw new RuntimeException("Failed to parse the model", e); - } - anyComponents = JsonIterator.deserialize(Overlay.toJson((OpenApi3Impl)openApi3).toString()).get("components"); - } else { - throw new RuntimeException("Invalid Model Class: " + model.getClass()); + OpenApi3 openApi3 = null; + try { + openApi3 = (OpenApi3)new OpenApiParser().parse((JsonNode)model, new URL("https://oas.lightapi.net/")); + } catch (MalformedURLException e) { + throw new RuntimeException("Failed to parse the model", e); } - - if (anyComponents.valueType() != ValueType.INVALID) { - Any schemas = anyComponents.asMap().get("schemas"); - if (schemas != null && schemas.valueType() != ValueType.INVALID) { + Map specMap = JsonMapper.string2Map(Overlay.toJson((OpenApi3Impl)openApi3).toString()); + Map components = (Map)specMap.get("components"); + if(components != null) { + Map schemas = (Map)components.get("schemas"); + if(schemas != null) { ArrayList modelCreators = new ArrayList<>(); - final HashMap references = new HashMap<>(); - for (Map.Entry entry : schemas.asMap().entrySet()) { - loadModel(entry.getKey(), null, entry.getValue().asMap(), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, null); + final HashMap references = new HashMap<>(); + for (Map.Entry entry : schemas.entrySet()) { + loadModel(entry.getKey(), null, (Map)entry.getValue(), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, null); } for (Runnable r : modelCreators) { @@ -278,35 +244,29 @@ public void generate(final String targetPath, Object model, Any config) throws I // transfer binary files without touching them. try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.keystore")) { - copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.keystore")); + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.keystore")); } try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.truststore")) { - copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.truststore")); + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.truststore")); } if (supportClient) { try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { - copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.keystore")); + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.keystore")); } try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { - copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.truststore")); + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.truststore")); } } else { try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { - copyFile(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.keystore")); + Generator.copyFile(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.keystore")); } try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { - copyFile(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.truststore")); + Generator.copyFile(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.truststore")); } } - if (model instanceof Any) { - try (InputStream is = new ByteArrayInputStream(model.toString().getBytes(StandardCharsets.UTF_8))) { - copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "openapi.json")); - } - } else if (model instanceof String) { - try (InputStream is = new ByteArrayInputStream(((String)model).getBytes(StandardCharsets.UTF_8))) { - copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "openapi.yaml")); - } + try (InputStream is = new ByteArrayInputStream(Generator.yamlMapper.writeValueAsBytes(model))) { + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "openapi.yaml")); } } @@ -316,19 +276,19 @@ public void generate(final String targetPath, Object model, Any config) throws I * @param entry The entry for which to generate * @param propMap The property map to add to, created in the caller */ - private void initializePropertyMap(Entry entry, Map propMap) { - String name = convertToValidJavaVariableName(entry.getKey()); - propMap.put("jsonProperty", Any.wrap(name)); + private void initializePropertyMap(Map.Entry entry, Map propMap) { + String name = convertToValidJavaVariableName(entry.getKey()); + propMap.put("jsonProperty", name); if (name.startsWith("@")) { name = name.substring(1); } - propMap.put("name", Any.wrap(name)); - propMap.put("getter", Any.wrap("get" + name.substring(0, 1).toUpperCase() + name.substring(1))); - propMap.put("setter", Any.wrap("set" + name.substring(0, 1).toUpperCase() + name.substring(1))); + propMap.put("name", name); + propMap.put("getter", "get" + name.substring(0, 1).toUpperCase() + name.substring(1)); + propMap.put("setter", "set" + name.substring(0, 1).toUpperCase() + name.substring(1)); // assume it is not enum unless it is overwritten - propMap.put("isEnum", Any.wrap(false)); - propMap.put("isNumEnum", Any.wrap(false)); + propMap.put("isEnum", false); + propMap.put("isNumEnum", false); } /** @@ -337,61 +297,61 @@ private void initializePropertyMap(Entry entry, Map pr * @param props The properties map to add to */ //private void handleProperties(List> props, Map.Entry entrySchema) { - private void handleProperties(List> props, Map properties) { + private void handleProperties(List> props, Map properties) { // transform properties - for (Map.Entry entryProp : properties.entrySet()) { + for (Map.Entry entryProp : properties.entrySet()) { //System.out.println("key = " + entryProp.getKey() + " value = " + entryProp.getValue()); - Map propMap = new HashMap<>(); + Map propMap = new HashMap<>(); // initialize property map initializePropertyMap(entryProp, propMap); String name = entryProp.getKey(); - String type = null; + String type = null; boolean isArray = false; - for (Map.Entry entryElement : entryProp.getValue().asMap().entrySet()) { + for (Map.Entry entryElement : ((Map)entryProp.getValue()).entrySet()) { //System.out.println("key = " + entryElement.getKey() + " value = " + entryElement.getValue()); if ("type".equals(entryElement.getKey())) { String t = typeMapping.get(entryElement.getValue().toString()); - type = t; + type = t; if ("java.util.List".equals(t)) { isArray = true; } else { - propMap.putIfAbsent("type", Any.wrap(t)); + propMap.putIfAbsent("type", t); } } if ("items".equals(entryElement.getKey())) { - Any a = entryElement.getValue(); - if (a.get("$ref").valueType() != ValueType.INVALID && isArray) { + Map a = (Map)entryElement.getValue(); + if (a.get("$ref") != null && isArray) { String s = a.get("$ref").toString(); s = s.substring(s.lastIndexOf('/') + 1); s = s.substring(0,1).toUpperCase() + (s.length() > 1 ? s.substring(1) : ""); - propMap.put("type", getListOf(s)); + propMap.put("type", getListOf(s)); } - if (a.get("type").valueType() != ValueType.INVALID && isArray) { + if (a.get("type") != null && isArray) { propMap.put("type", getListOf(typeMapping.get(a.get("type").toString()))); } } if ("$ref".equals(entryElement.getKey())) { String s = entryElement.getValue().toString(); s = s.substring(s.lastIndexOf('/') + 1); - s = s.substring(0,1).toUpperCase() + (s.length() > 1 ? s.substring(1) : ""); - propMap.put("type", Any.wrap(s)); + s = s.substring(0,1).toUpperCase() + (s.length() > 1 ? s.substring(1) : ""); + propMap.put("type", s); } if ("default".equals(entryElement.getKey())) { - Any a = entryElement.getValue(); + Object a = entryElement.getValue(); propMap.put("default", a); } if ("enum".equals(entryElement.getKey())) { - // different generate format for number enum - if ("Integer".equals(type) || "Double".equals(type) || "Float".equals(type) + // different generate format for number enum + if ("Integer".equals(type) || "Double".equals(type) || "Float".equals(type) || "Long".equals(type) || "Short".equals(type) || "java.math.BigDecimal".equals(type)) { - propMap.put("isNumEnum", Any.wrap(true)); + propMap.put("isNumEnum", true); } - propMap.put("isEnum", Any.wrap(true)); - propMap.put("nameWithEnum", Any.wrap(name.substring(0, 1).toUpperCase() + name.substring(1) + "Enum")); - propMap.put("value", getValidEnumName(entryElement)); + propMap.put("isEnum", true); + propMap.put("nameWithEnum", name.substring(0, 1).toUpperCase() + name.substring(1) + "Enum"); + propMap.put("value", getValidEnumName(entryElement)); } if ("format".equals(entryElement.getKey())) { @@ -421,8 +381,8 @@ private void handleProperties(List> props, Map pro case "binary": ultimateType = "byte[]"; - propMap.put(COMPARATOR, Any.wrap("Arrays")); - propMap.put(HASHER, Any.wrap("Arrays")); + propMap.put(COMPARATOR, "Arrays"); + propMap.put(HASHER, "Arrays"); break; case "byte": @@ -434,47 +394,47 @@ private void handleProperties(List> props, Map pro } if (ultimateType != null) { - propMap.put("type", Any.wrap(ultimateType)); + propMap.put("type", ultimateType); } } if ("oneOf".equals(entryElement.getKey())) { - List list = entryElement.getValue().asList(); - Any t = list.get(0).asMap().get("type"); + List list = (List)entryElement.getValue(); + Object t = ((Map)list.get(0)).get("type"); if (t != null) { - propMap.put("type", Any.wrap(typeMapping.get(t.toString()))); + propMap.put("type", typeMapping.get(t.toString())); } else { // maybe reference? default type to object. - propMap.put("type", Any.wrap("Object")); + propMap.put("type", "Object"); } } if ("anyOf".equals(entryElement.getKey())) { - List list = entryElement.getValue().asList(); - Any t = list.get(0).asMap().get("type"); + List list = (List)entryElement.getValue(); + Object t = ((Map)list.get(0)).get("type"); if (t != null) { - propMap.put("type", Any.wrap(typeMapping.get(t.toString()))); + propMap.put("type", typeMapping.get(t.toString())); } else { // maybe reference? default type to object. - propMap.put("type", Any.wrap("Object")); + propMap.put("type", "Object"); } } if ("allOf".equals(entryElement.getKey())) { - List list = entryElement.getValue().asList(); - Any t = list.get(0).asMap().get("type"); + List list = (List)entryElement.getValue(); + Object t = ((Map)list.get(0)).get("type"); if (t != null) { - propMap.put("type", Any.wrap(typeMapping.get(t.toString()))); + propMap.put("type", typeMapping.get(t.toString())); } else { // maybe reference? default type to object. - propMap.put("type", Any.wrap("Object")); + propMap.put("type", "Object"); } } if ("not".equals(entryElement.getKey())) { - Map m = entryElement.getValue().asMap(); - Any t = m.get("type"); + Map m = (Map)entryElement.getValue(); + Object t = m.get("type"); if (t != null) { propMap.put("type", t); } else { - propMap.put("type", Any.wrap("Object")); + propMap.put("type", "Object"); } } } @@ -484,123 +444,63 @@ private void handleProperties(List> props, Map pro public static final String HASHER = "hasher"; public static final String COMPARATOR = "comparator"; - private Any getListOf(String s) { - return new UnresolvedTypeListAny(s); + private String getListOf(String s) { + return String.format("java.util.List<%s>", s); } - private static abstract class UnresolvedTypeAny extends Any { - - Any type; - - UnresolvedTypeAny(Any type) { - this.type = type; - } - - private Any get() { - return type; - } - - private void set(Any type) { - this.type = type; - } - - @Override - public Object object() { - return toString(); - } - - @Override - public boolean toBoolean() { - return Boolean.parseBoolean(toString()); - } - - @Override - public int toInt() { - return Integer.parseInt(toString()); + // method used to convert string to valid java variable name + // 1. replace invalid character with '_' + // 2. prefix number with '_' + // 3. convert the first character of java keywords to upper case + public static String convertToValidJavaVariableName(String string) { + if (string == null || string.equals("") || SourceVersion.isName(string)) { + return string; } - - @Override - public long toLong() { - return Long.parseLong(toString()); + // to validate whether the string is Java keyword + if (SourceVersion.isKeyword(string)) { + return "_" + string; } - - @Override - public float toFloat() { - return Float.parseFloat(toString()); + // replace invalid characters with underscore + StringBuilder stringBuilder = new StringBuilder(); + if (!Character.isJavaIdentifierStart(string.charAt(0))) { + stringBuilder.append('_'); } - - @Override - public double toDouble() { - return Double.parseDouble(toString()); + for (char c : string.toCharArray()) { + if (!Character.isJavaIdentifierPart(c)) { + stringBuilder.append('_'); + } else { + stringBuilder.append(c); + } } + return stringBuilder.toString(); } - private static class UnresolvedTypeHolderAny extends UnresolvedTypeAny { - - UnresolvedTypeHolderAny(Any resolved) { - super(resolved); - } - - @Override - public ValueType valueType() { - return type.valueType(); - } - - @Override - public String toString() { - return type.toString(); - } - - @Override - public void writeTo(JsonStream stream) throws IOException { - type.writeTo(stream); - } + private boolean isEnumHasDescription(String string) { + return string.contains(":") || string.contains("{") || string.contains("("); } - private static class UnresolvedTypeListAny extends UnresolvedTypeAny { - - UnresolvedTypeListAny(Any type) { - super(type); - } - - UnresolvedTypeListAny(String string) { - super(Any.wrap(string)); - } - - @Override - public ValueType valueType() { - return ValueType.ARRAY; - } - - @Override - public void writeTo(JsonStream stream) throws IOException { - stream.writeRaw(toString()); - } + private String getEnumName(String string) { + if (string.contains(":")) return string.substring(0, string.indexOf(":")).trim(); + if (string.contains("(") && string.contains(")")) return string.substring(0, string.indexOf("(")).trim(); + if (string.contains("{") && string.contains("}")) return string.substring(0, string.indexOf("{")).trim(); + return string; + } - @Override - public String toString() { - return UnresolvedTypeListAny.toString(type); - } + private String getEnumDescription(String string) { + if (string.contains(":")) return string.substring(string.indexOf(":") + 1).trim(); + if (string.contains("(") && string.contains(")")) return string.substring(string.indexOf("(") + 1, string.indexOf(")")).trim(); + if (string.contains("{") && string.contains("}")) return string.substring(string.indexOf("{") + 1, string.indexOf("}")).trim(); - private static String toString(Any type) { - return String.format("java.util.List<%s>", type.toString()); - } + return string; } public List> getOperationList(Object model) { List> result = new ArrayList<>(); - String s; - if (model instanceof Any) { - s = ((Any)model).toString(); - } else if (model instanceof String) { - s = (String)model; - } else { - throw new RuntimeException("Invalid Model Class: " + model.getClass()); - } OpenApi3 openApi3 = null; try { - openApi3 = (OpenApi3)new OpenApiParser().parse(s, new URL("https://oas.lightapi.net/")); + openApi3 = (OpenApi3)new OpenApiParser().parse((JsonNode)model, new URL("https://oas.lightapi.net/")); } catch (MalformedURLException e) { + e.printStackTrace(); } String basePath = getBasePath(openApi3); @@ -682,68 +582,21 @@ private static String getBasePath(OpenApi3 openApi3) { } // method used to generate valid enum keys for enum contents - private Any getValidEnumName(Map.Entry entryElement) { - Iterator iterator = entryElement.getValue().iterator(); - Map map = new HashMap<>(); + private Object getValidEnumName(Map.Entry entryElement) { + Iterator iterator = ((List)entryElement.getValue()).iterator(); + Map map = new HashMap<>(); while (iterator.hasNext()) { String string = iterator.next().toString().trim(); if (string.equals("")) continue; if (isEnumHasDescription(string)) { - map.put(convertToValidJavaVariableName(getEnumName(string)).toUpperCase(), Any.wrap(getEnumDescription(string))); + map.put(convertToValidJavaVariableName(getEnumName(string)).toUpperCase(), getEnumDescription(string)); } else { - map.put(convertToValidJavaVariableName(string).toUpperCase(), Any.wrap(string)); + map.put(convertToValidJavaVariableName(string).toUpperCase(), string); } } - return Any.wrap(map); + return map; } - // method used to convert string to valid java variable name - // 1. replace invalid character with '_' - // 2. prefix number with '_' - // 3. convert the first character of java keywords to upper case - public static String convertToValidJavaVariableName(String string) { - if (string == null || string.equals("") || SourceVersion.isName(string)) { - return string; - } - // to validate whether the string is Java keyword - if (SourceVersion.isKeyword(string)) { - return "_" + string; - } - // replace invalid characters with underscore - StringBuilder stringBuilder = new StringBuilder(); - if (!Character.isJavaIdentifierStart(string.charAt(0))) { - stringBuilder.append('_'); - } - for (char c : string.toCharArray()) { - if (!Character.isJavaIdentifierPart(c)) { - stringBuilder.append('_'); - } else { - stringBuilder.append(c); - } - } - return stringBuilder.toString(); - } - - private boolean isEnumHasDescription(String string) { - return string.contains(":") || string.contains("{") || string.contains("("); - } - - private String getEnumName(String string) { - if (string.contains(":")) return string.substring(0, string.indexOf(":")).trim(); - if (string.contains("(") && string.contains(")")) return string.substring(0, string.indexOf("(")).trim(); - if (string.contains("{") && string.contains("}")) return string.substring(0, string.indexOf("{")).trim(); - return string; - } - - private String getEnumDescription(String string) { - if (string.contains(":")) return string.substring(string.indexOf(":") + 1).trim(); - if (string.contains("(") && string.contains(")")) return string.substring(string.indexOf("(") + 1, string.indexOf(")")).trim(); - if (string.contains("{") && string.contains("}")) return string.substring(string.indexOf("{") + 1, string.indexOf("}")).trim(); - - return string; - } - - private String populateRequestBodyExample(Operation operation) { String result = "{\"content\": \"request body to be replaced\"}"; RequestBody body = operation.getRequestBody(); @@ -752,7 +605,7 @@ private String populateRequestBodyExample(Operation operation) { if (mediaType != null) { Object valueToBeStringify = null; if (mediaType.getExamples() != null && !mediaType.getExamples().isEmpty()) { - for (Entry entry : mediaType.getExamples().entrySet()) { + for (Map.Entry entry : mediaType.getExamples().entrySet()) { valueToBeStringify = entry.getValue().getValue(); } } else if (mediaType.getExample() != null) { @@ -761,7 +614,11 @@ private String populateRequestBodyExample(Operation operation) { if (valueToBeStringify == null) { return result; } - result = JsonStream.serialize(valueToBeStringify); + try { + result = Generator.jsonMapper.writeValueAsString(valueToBeStringify); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } if (result.startsWith("\"")) { result = result.substring(1, result.length() - 1); } @@ -784,7 +641,13 @@ private Map populateResponseExample(Operation operation) { example = mediaType.get().getExample(); if (example != null) { result.put("statusCode", statusCode); - result.put("example", JsonStream.serialize(example)); + String ex = null; + try { + ex = Generator.jsonMapper.writeValueAsString(example); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + result.put("example", ex); } else { // check if there are multiple examples Map exampleMap = mediaType.get().getExamples(); @@ -794,7 +657,13 @@ private Map populateResponseExample(Operation operation) { Example e = entry.getValue(); if (e != null) { result.put("statusCode", statusCode); - result.put("example", JsonStream.serialize(e.getValue())); + String s = null; + try { + s = Generator.jsonMapper.writeValueAsString(e.getValue()); + } catch (JsonProcessingException ex) { + ex.printStackTrace(); + } + result.put("example", s); } } } @@ -803,28 +672,26 @@ private Map populateResponseExample(Operation operation) { return result; } - private static final Logger logger = LoggerFactory.getLogger(OpenApiGenerator.class); - - private void loadModel(String classVarName, String parentClassName, Map value, Any schemas, boolean overwriteModel, String targetPath, String modelPackage, List modelCreators, Map references, List> parentClassProps) throws IOException { + private void loadModel(String classVarName, String parentClassName, Map value, Map schemas, boolean overwriteModel, String targetPath, String modelPackage, List modelCreators, Map references, List> parentClassProps) throws IOException { final String modelFileName = classVarName.substring(0, 1).toUpperCase() + classVarName.substring(1); - final List> props = new ArrayList<>(); - final List> parentProps = (parentClassProps == null) ? new ArrayList<>() : new ArrayList<>(parentClassProps); + final List> props = new ArrayList<>(); + final List> parentProps = (parentClassProps == null) ? new ArrayList<>() : new ArrayList<>(parentClassProps); String type = null; String enums = null; boolean isEnumClass = false; - List required = null; + List required = null; boolean isAbstractClass = false; // iterate through each schema in the components - Queue> schemaElementQueue = new LinkedList<>(); + Queue> schemaElementQueue = new LinkedList<>(); // cache the visited elements to prevent loop reference Set seen = new HashSet<>(); // add elements into queue to perform a BFS - for (Map.Entry entrySchema : value.entrySet()) { + for (Map.Entry entrySchema : value.entrySet()) { schemaElementQueue.offer(entrySchema); } while (!schemaElementQueue.isEmpty()) { - Map.Entry currentElement = schemaElementQueue.poll(); + Map.Entry currentElement = schemaElementQueue.poll(); String currentElementKey = currentElement.getKey(); // handle the base elements if ("type".equals(currentElementKey) && type == null) { @@ -832,17 +699,17 @@ private void loadModel(String classVarName, String parentClassName, Map)currentElement.getValue()); } if ("required".equals(currentElementKey)) { if (required == null) { required = new ArrayList<>(); } - required.addAll(currentElement.getValue().asList()); + required.addAll((List)currentElement.getValue()); } // expend the ref elements and add to the queue if ("$ref".equals(currentElementKey)) { @@ -850,14 +717,14 @@ private void loadModel(String classVarName, String parentClassName, Map schema : schemas.get(s).asMap().entrySet()) { + for (Map.Entry schema : ((Map)schemas.get(s)).entrySet()) { schemaElementQueue.offer(schema); } } // expand the allOf elements and add to the queue if ("allOf".equals(currentElementKey)) { - for (Any listItem : currentElement.getValue().asList()) { - for (Map.Entry allOfItem : listItem.asMap().entrySet()) { + for (Object listItem : (List)currentElement.getValue()) { + for (Map.Entry allOfItem : ((Map)listItem).entrySet()) { schemaElementQueue.offer(allOfItem); } } @@ -867,12 +734,12 @@ private void loadModel(String classVarName, String parentClassName, Map oneOfItem : listItem.asMap().entrySet()) { + for (Object listItem : (List)currentElement.getValue()) { + for (Map.Entry oneOfItem : ((Map)listItem).entrySet()) { if ("$ref".equals(oneOfItem.getKey())) { String s = oneOfItem.getValue().toString(); s = s.substring(s.lastIndexOf('/') + 1); - loadModel(extendModelName(s, classVarName), s, schemas.get(s).asMap(), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, parentProps); + loadModel(extendModelName(s, classVarName), s, (Map)schemas.get(s), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, parentProps); } } } @@ -894,24 +761,24 @@ private void loadModel(String classVarName, String parentClassName, Map { final int referencesCount = references.size(); - for (Map properties : props) { - Any any = properties.get("type"); + for (Map properties : props) { + Object any = properties.get("type"); if (any != null) { - if (any.valueType() == ValueType.STRING) { - Any resolved = references.get(any.toString()); + if (any instanceof String) { + Object resolved = references.get(any); if (resolved == null) { continue; } - any = new UnresolvedTypeHolderAny(resolved); - properties.put("type", any); + any = resolved; + properties.put("type", resolved); } int iteration = 0; do { - UnresolvedTypeAny previous = null; - while (any instanceof UnresolvedTypeAny) { - previous = (UnresolvedTypeAny)any; - any = ((UnresolvedTypeAny)any).get(); + Object previous = null; + while (unresolvedListType((String)any)) { + previous = any; + any = getListObjectType((String)any); } if (any == null) { @@ -920,12 +787,13 @@ private void loadModel(String classVarName, String parentClassName, Map map = new HashMap<>(1); - map.put(classVarName, Any.wrap(value)); + HashMap map = new HashMap<>(1); + map.put(classVarName, value); handleProperties(props, map); if (props.isEmpty()) { throw new IllegalStateException("Properties empty for " + classVarName + "!"); @@ -959,4 +827,35 @@ private void loadModel(String classVarName, String parentClassName, Map")) { + return listType.substring(listType.indexOf("<") + 1, listType.indexOf(">")); + } else { + return listType; + } + } + + private String setListObjectType(String original, String resolved) { + if(!original.equals(resolved) && original.contains("<") && original.contains(">")) { + String replace = original.substring(original.indexOf("<") + 1, original.indexOf(">")); + original = original.replace(replace, resolved); + } + return original; + } + + private boolean unresolvedListType(String listType) { + boolean result = false; + if(listType != null && listType.contains("java.util.List")) { + String objType = getListObjectType(listType); + if(!objType.equals("Boolean") + && !objType.equals("Integer") + && !objType.equals("Long") + && !objType.equals("Float") + && !objType.equals("Double") + && !objType.equals("Object")) { + result = true; + } + } + return result; + } } diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiKotlinGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiKotlinGenerator.java index 5feb540ab..624235f5c 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiKotlinGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiKotlinGenerator.java @@ -1,71 +1,13 @@ package com.networknt.codegen.rest; -import com.jsoniter.JsonIterator; -import com.jsoniter.ValueType; -import com.jsoniter.any.Any; -import com.jsoniter.output.JsonStream; +import com.fasterxml.jackson.databind.JsonNode; import com.networknt.codegen.Generator; -import com.networknt.codegen.Utils; -import com.networknt.jsonoverlay.Overlay; -import com.networknt.oas.OpenApiParser; -import com.networknt.oas.model.*; -import com.networknt.oas.model.impl.OpenApi3Impl; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.*; -import java.util.stream.Collectors; +import java.util.List; +import java.util.Map; -import static java.io.File.separator; - -/** - * The input for OpenAPI 3.0 generator include config with json format - * and OpenAPI specification in yaml format. - * - * The model is OpenAPI spec in yaml format. And config file is config.json - * in JSON format. - * - * @author Steve Hu - */ public class OpenApiKotlinGenerator implements Generator { - private Map typeMapping = new HashMap<>(); - - // optional generation parameters. if not set, they use default values as - boolean prometheusMetrics =false; - boolean skipHealthCheck = false; - boolean skipServerInfo = false; - boolean regenerateCodeOnly = false; - boolean enableParamDescription = true; - boolean generateModelOnly = false; - boolean generateValuesYml = false; - - public OpenApiKotlinGenerator() { - typeMapping.put("array", "java.util.List"); - typeMapping.put("map", "java.util.Map"); - typeMapping.put("List", "java.util.List"); - typeMapping.put("boolean", "Boolean"); - typeMapping.put("string", "String"); - typeMapping.put("int", "Integer"); - typeMapping.put("float", "Float"); - typeMapping.put("number", "java.math.BigDecimal"); - typeMapping.put("DateTime", "Date"); - typeMapping.put("long", "Long"); - typeMapping.put("short", "Short"); - typeMapping.put("char", "String"); - typeMapping.put("double", "Double"); - typeMapping.put("object", "Any"); - typeMapping.put("integer", "Int"); - typeMapping.put("ByteArray", "byte[]"); - typeMapping.put("binary", "byte[]"); - } - @Override public String getFramework() { return "openapikotlin"; @@ -79,511 +21,11 @@ public String getFramework() { * @throws IOException IO Exception occurs during code generation */ @Override - public void generate(String targetPath, Object model, Any config) throws IOException { - // whoever is calling this needs to make sure that model is converted to Map - String rootPackage = config.toString("rootPackage").trim(); - String modelPackage = config.toString("modelPackage").trim(); - String handlerPackage = config.toString("handlerPackage").trim(); - - boolean overwriteHandler = config.toBoolean("overwriteHandler"); - boolean overwriteHandlerTest = config.toBoolean("overwriteHandlerTest"); - boolean overwriteModel = config.toBoolean("overwriteModel"); - generateModelOnly = config.toBoolean("generateModelOnly"); - - boolean enableHttp = config.toBoolean("enableHttp"); - String httpPort = config.toString("httpPort").trim(); - boolean enableHttps = config.toBoolean("enableHttps"); - String httpsPort = config.toString("httpsPort").trim(); - boolean enableHttp2 = config.toBoolean("enableHttp2"); - boolean enableRegistry = config.toBoolean("enableRegistry"); - boolean supportClient = config.toBoolean("supportClient"); - String dockerOrganization = config.toString("dockerOrganization").trim(); - - prometheusMetrics = config.toBoolean("prometheusMetrics"); - skipHealthCheck = config.toBoolean("skipHealthCheck"); - skipServerInfo = config.toBoolean("skipServerInfo"); - regenerateCodeOnly = config.toBoolean("specChangeCodeReGenOnly"); - enableParamDescription = config.toBoolean("enableParamDescription"); - String version = config.toString("version").trim(); - String serviceId = config.get("groupId").toString().trim() + "." + config.get("artifactId").toString().trim() + "-" + config.get("version").toString().trim(); - - if(dockerOrganization == null || dockerOrganization.length() == 0) dockerOrganization = "networknt"; - - // get the list of operations for this model - List> operationList = getOperationList(model); - - // bypass project generation if the mode is the only one requested to be built - if(!generateModelOnly) { - // if set to true, regenerate the code only (handlers, model and the handler.yml, potentially affected by operation changes - if (!regenerateCodeOnly) { - // generate configurations, project, masks, certs, etc - // There is only one port that should be exposed in Dockerfile, otherwise, the service - // discovery will be so confused. If https is enabled, expose the https port. Otherwise http port. - String expose = ""; - if(enableHttps) { - expose = httpsPort; - } else { - expose = httpPort; - } - - transfer(targetPath, "docker", "Dockerfile", templates.restkotlin.dockerfile.template(config, expose)); - transfer(targetPath, "docker", "Dockerfile-Slim", templates.restkotlin.dockerfileslim.template(config, expose)); - transfer(targetPath, "", "build.sh", templates.restkotlin.buildSh.template(dockerOrganization, serviceId)); - transfer(targetPath, "", ".gitignore", templates.restkotlin.gitignore.template()); - transfer(targetPath, "", "README.md", templates.restkotlin.README.template()); - transfer(targetPath, "", "LICENSE", templates.restkotlin.LICENSE.template()); - transfer(targetPath, "", "build.gradle.kts", templates.restkotlin.buildGradleKts.template(config)); - transfer(targetPath, "", "gradle.properties", templates.restkotlin.gradleProperties.template(config)); - transfer(targetPath, "", "settings.gradle.kts", templates.restkotlin.settingsGradleKts.template(config)); - - transferGradle(targetPath); - - // config - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "service.yml", templates.restkotlin.openapi.service.template(config)); - - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "server.yml", templates.restkotlin.server.template(serviceId, enableHttp, httpPort, enableHttps, httpsPort, enableHttp2, enableRegistry, version)); - transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", templates.restkotlin.server.template(serviceId, enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); - - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "openapi-security.yml", templates.restkotlin.openapiSecurity.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "openapi-validator.yml", templates.restkotlin.openapiValidator.template()); - if(supportClient) { - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "client.yml", templates.restkotlin.clientYml.template()); - } else { - transfer(targetPath, ("src.test.resources.config").replace(".", separator), "client.yml", templates.restkotlin.clientYml.template()); - } - - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "primary.crt", templates.restkotlin.primaryCrt.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "secondary.crt", templates.restkotlin.secondaryCrt.template()); - - // mask - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "mask.yml", templates.restkotlin.maskYml.template()); - // logging - transfer(targetPath, ("src.main.resources").replace(".", separator), "logback.xml", templates.restkotlin.logback.template()); - transfer(targetPath, ("src.test.resources").replace(".", separator), "logback-test.xml", templates.restkotlin.logback.template()); - transfer(targetPath, ("src.test.resources").replace(".", separator), "junit-platform.properties", templates.restkotlin.junitPlatformProperties.template()); - - // routing handler - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "handler.yml", templates.restkotlin.openapi.handlerYml.template(serviceId, handlerPackage, operationList, prometheusMetrics, !skipHealthCheck, !skipServerInfo)); - - // exclusion list for Config module - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "config.yml", templates.restkotlin.openapi.config.template()); - // added with #471 - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "app-status.yml", templates.restkotlin.appStatusYml.template()); - // values.yml file, transfer to suppress the warning message during start startup and encourage usage. - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "values.yml", templates.restkotlin.openapi.values.template()); - } - } - - // model - Any anyComponents; - if(model instanceof Any) { - anyComponents = ((Any)model).get("components"); - } else if(model instanceof String){ - // this must be yaml format and we need to convert to json for JsonIterator. - OpenApi3 openApi3 = null; - try { - openApi3 = (OpenApi3) new OpenApiParser().parse((String)model, new URL("https://oas.lightapi.net/")); - } catch (MalformedURLException e) { - throw new RuntimeException("Failed to parse the model", e); - } - anyComponents = JsonIterator.deserialize(Overlay.toJson((OpenApi3Impl)openApi3).toString()).get("components"); - } else { - throw new RuntimeException("Invalid Model Class: " + model.getClass()); - } - if(anyComponents.valueType() != ValueType.INVALID) { - Any schemas = anyComponents.asMap().get("schemas"); - if(schemas != null && schemas.valueType() != ValueType.INVALID) { - for(Map.Entry entry : schemas.asMap().entrySet()) { - List> props = new ArrayList<>(); - String key = entry.getKey(); - Map value = entry.getValue().asMap(); - String type = null; - String enums = null; - boolean isEnum = false; - boolean isEnumClass = false; - // Map properties = null; - List required = null; - - // iterate through each schema in the components - for(Map.Entry entrySchema: value.entrySet()) { - if("type".equals(entrySchema.getKey())) { - type = entrySchema.getValue().toString(); - if("enum".equals(type)) isEnum = true; - } - if("enum".equals(entrySchema.getKey())) { - isEnumClass = true; - enums = entrySchema.getValue().asList().toString(); - enums = enums.substring(enums.indexOf("[") + 1, enums.indexOf("]")); - } - if("properties".equals(entrySchema.getKey())) { - handleProperties(props, entrySchema.getValue().asMap()); - } - if("required".equals(entrySchema.getKey())) { - required = entrySchema.getValue().asList(); - } - if("allOf".equals(entrySchema.getKey())) { - type = "object"; - - // could be referred to as "$ref" references or listed in "properties" - for(Any listItem : entrySchema.getValue().asList()) { - //Map allOfItem = (Map)listItem.asMap().entrySet(); - - for(Map.Entry allOfItem : ((Map)listItem.asMap()).entrySet()) { - if("$ref".equals(allOfItem.getKey())) { - String s = allOfItem.getValue().toString(); - s = s.substring(s.lastIndexOf('/') + 1); - handleProperties(props, schemas.get(s).get("properties").asMap()); - } - if("properties".equals(allOfItem.getKey())) { - handleProperties(props, allOfItem.getValue().asMap()); - } - } - } - } - } - String classVarName = key; - String modelFileName = key.substring(0, 1).toUpperCase() + key.substring(1); - //System.out.println("props = " + Any.wrap(props)); - - // Check the type of current schema. Generation will be executed only if the type of the schema equals to object. - // Since generate a model for primitive types and arrays do not make sense, and an error class would be generated - // due to lack of properties if force to generate. - if (!"object".equals(type)) { - continue; - } - if(!overwriteModel && checkExist(targetPath, ("src.main.kotlin." + modelPackage).replace(".", separator), modelFileName + ".kt")) { - continue; - } - if (isEnumClass) { - transfer(targetPath, ("src.main.kotlin." + modelPackage).replace(".", separator), modelFileName + ".kt", templates.restkotlin.enumClass.template(modelPackage, modelFileName, enums)); - continue; - } - transfer(targetPath, ("src.main.kotlin." + modelPackage).replace(".", separator), modelFileName + ".kt", templates.restkotlin.pojo.template(modelPackage, modelFileName, classVarName, props)); - } - } - } + public void generate(String targetPath, Object model, JsonNode config) throws IOException { - // exit after generating the model if the consumer needs only the model classes - if(generateModelOnly) - return; - - // handler - for(Map op : operationList){ - String className = op.get("handlerName").toString(); - String example = null; - List parameters = (List) op.get("parameters"); - if(op.get("example") != null) { - //example = mapper.writeValueAsString(op.get("example")); - example = JsonStream.serialize(op.get("example")); - } - if(checkExist(targetPath, ("src.main.kotlin." + handlerPackage).replace(".", separator), className + ".kt") && !overwriteHandler) { - continue; - } - transfer(targetPath, ("src.main.kotlin." + handlerPackage).replace(".", separator), className + ".kt", templates.restkotlin.handler.template(handlerPackage, className, example, parameters)); - } - - // handler test cases - transfer(targetPath, ("src.test.kotlin." + handlerPackage + ".").replace(".", separator), "LightTestServer.kt", templates.restkotlin.lightTestServerKt.template(handlerPackage)); - for(Map op : operationList){ - if(checkExist(targetPath, ("src.test.kotlin." + handlerPackage).replace(".", separator), op.get("handlerName") + "Test.kt") && !overwriteHandlerTest) { - continue; - } - transfer(targetPath, ("src.test.kotlin." + handlerPackage).replace(".", separator), op.get("handlerName") + "Test.kt", templates.restkotlin.handlerTest.template(handlerPackage, op)); - } - - // transfer binary files without touching them. - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.keystore")) { - Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.keystore"), StandardCopyOption.REPLACE_EXISTING); - } - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.truststore")) { - Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.truststore"), StandardCopyOption.REPLACE_EXISTING); - } - if(supportClient) { - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { - Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.keystore"), StandardCopyOption.REPLACE_EXISTING); - } - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { - Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.truststore"), StandardCopyOption.REPLACE_EXISTING); - } - } else { - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { - Files.copy(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.keystore"), StandardCopyOption.REPLACE_EXISTING); - } - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { - Files.copy(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.truststore"), StandardCopyOption.REPLACE_EXISTING); - } - } - - if(model instanceof Any) { - try (InputStream is = new ByteArrayInputStream(model.toString().getBytes(StandardCharsets.UTF_8))) { - Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "openapi.json"), StandardCopyOption.REPLACE_EXISTING); - } - } else if(model instanceof String){ - try (InputStream is = new ByteArrayInputStream(((String)model).getBytes(StandardCharsets.UTF_8))) { - Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "openapi.yaml"), StandardCopyOption.REPLACE_EXISTING); - } - } - } - - /** - * Initialize the property map with base elements as name, getter, setters, etc - * @param entry The entry for which to generate - * @param propMap The property map to add to, created in the caller - */ - private void initializePropertyMap(Map.Entry entry, Map propMap) { - String name = entry.getKey(); - propMap.put("jsonProperty", Any.wrap(name)); - if(name.startsWith("@")) { - name = name.substring(1); - - } - propMap.put("name", Any.wrap(name)); - propMap.put("getter", Any.wrap("get" + name.substring(0, 1).toUpperCase() + name.substring(1))); - propMap.put("setter", Any.wrap("set" + name.substring(0, 1).toUpperCase() + name.substring(1))); - // assume it is not enum unless it is overwritten - propMap.put("isEnum", Any.wrap(false)); - } - - /** - * Handle elements listed as "properties" - * - * @param props The properties map to add to - */ - //private void handleProperties(List> props, Map.Entry entrySchema) { - private void handleProperties(List> props, Map properties) { - // transform properties - for(Map.Entry entryProp: properties.entrySet()) { - //System.out.println("key = " + entryProp.getKey() + " value = " + entryProp.getValue()); - Map propMap = new HashMap<>(); - - // initialize property map - initializePropertyMap(entryProp, propMap); - - String name = entryProp.getKey(); - boolean isArray = false; - for(Map.Entry entryElement: entryProp.getValue().asMap().entrySet()) { - //System.out.println("key = " + entryElement.getKey() + " value = " + entryElement.getValue()); - - if("type".equals(entryElement.getKey())) { - String t = typeMapping.get(entryElement.getValue().toString()); - if("java.util.List".equals(t)) { - isArray = true; - } else { - propMap.putIfAbsent("type", Any.wrap(t)); - } - } - if("items".equals(entryElement.getKey())) { - Any a = entryElement.getValue(); - if(a.get("$ref").valueType() != ValueType.INVALID && isArray) { - String s = a.get("$ref").toString(); - s = s.substring(s.lastIndexOf('/') + 1); - propMap.put("type", Any.wrap("List<" + s + ">")); - } - if(a.get("type").valueType() != ValueType.INVALID && isArray) { - propMap.put("type", Any.wrap("java.util.List<" + typeMapping.get(a.get("type").toString()) + ">")); - } - } - if("$ref".equals(entryElement.getKey())) { - String s = entryElement.getValue().toString(); - s = s.substring(s.lastIndexOf('/') + 1); - propMap.put("type", Any.wrap(s)); - } - if("default".equals(entryElement.getKey())) { - Any a = entryElement.getValue(); - propMap.put("default", a); - } - if("enum".equals(entryElement.getKey())) { - propMap.put("isEnum", Any.wrap(true)); - propMap.put("nameWithEnum", Any.wrap(name.substring(0, 1).toUpperCase() + name.substring(1) + "Enum")); - this.addUnderscores(entryElement); - propMap.put("value", Any.wrap(entryElement.getValue())); - } - if("format".equals(entryElement.getKey())) { - String s = entryElement.getValue().toString(); - if("date-time".equals(s)) { - propMap.put("type", Any.wrap("java.time.LocalDateTime")); - } - if("date".equals(s)) { - propMap.put("type", Any.wrap("java.time.LocalDate")); - } - if("double".equals(s)) { - propMap.put("type", Any.wrap("Double")); - } - if("float".equals(s)) { - propMap.put("type", Any.wrap("Float")); - } - if("int64".equals(s)){ - propMap.put("type", Any.wrap("Long")); - } - if("int32".equals(s)) { - propMap.put("type", Any.wrap("Int")); - } - } - if("oneOf".equals(entryElement.getKey())) { - List list = entryElement.getValue().asList(); - Any t = list.get(0).asMap().get("type"); - if(t != null) { - propMap.put("type", Any.wrap(typeMapping.get(t.toString()))); - } else { - // maybe reference? default type to object. - propMap.put("type", Any.wrap("Object")); - } - } - if("anyOf".equals(entryElement.getKey())) { - List list = entryElement.getValue().asList(); - Any t = list.get(0).asMap().get("type"); - if(t != null) { - propMap.put("type", Any.wrap(typeMapping.get(t.toString()))); - } else { - // maybe reference? default type to object. - propMap.put("type", Any.wrap("Object")); - } - } - if("allOf".equals(entryElement.getKey())) { - List list = entryElement.getValue().asList(); - Any t = list.get(0).asMap().get("type"); - if(t != null) { - propMap.put("type", Any.wrap(typeMapping.get(t.toString()))); - } else { - // maybe reference? default type to object. - propMap.put("type", Any.wrap("Object")); - } - } - if("not".equals(entryElement.getKey())) { - Map m = entryElement.getValue().asMap(); - Any t = m.get("type"); - if(t != null) { - propMap.put("type", t); - } else { - propMap.put("type", Any.wrap("Object")); - } - } - } - props.add(propMap); - } } - public List> getOperationList(Object model) { - List> result = new ArrayList<>(); - String s; - if(model instanceof Any) { - s = ((Any)model).toString(); - } else if(model instanceof String){ - s = (String)model; - } else { - throw new RuntimeException("Invalid Model Class: " + model.getClass()); - } - OpenApi3 openApi3 = null; - try { - openApi3 = (OpenApi3) new OpenApiParser().parse(s, new URL("https://oas.lightapi.net/")); - } catch (MalformedURLException e) { - } - String basePath = getBasePath(openApi3); - - Map paths = openApi3.getPaths(); - for(Map.Entry entryPath: paths.entrySet()) { - String path = entryPath.getKey(); - Path pathValue = entryPath.getValue(); - for(Map.Entry entryOps: pathValue.getOperations().entrySet()) { - // skip all the entries that are not http method. The only possible entries - // here are extensions. which will be just a key value pair. - if(entryOps.getKey().startsWith("x-")) continue; - Map flattened = new HashMap<>(); - flattened.put("method", entryOps.getKey().toUpperCase()); - flattened.put("capMethod", entryOps.getKey().substring(0, 1).toUpperCase() + entryOps.getKey().substring(1)); - flattened.put("path", basePath + path); - String normalizedPath = path.replace("{", "").replace("}", ""); - flattened.put("handlerName", Utils.camelize(normalizedPath) + Utils.camelize(entryOps.getKey()) + "Handler"); - Operation operation = entryOps.getValue(); - flattened.put("normalizedPath", UrlGenerator.generateUrl(basePath, path, entryOps.getValue().getParameters())); - //eg. 200 || statusCode == 400 || statusCode == 500 - flattened.put("supportedStatusCodesStr", operation.getResponses().keySet().stream().collect(Collectors.joining(" || statusCode = "))); - Map headerNameValueMap = operation.getParameters() - .stream() - .filter(parameter -> parameter.getIn().equals("header")) - .collect(Collectors.toMap(k -> k.getName(), v -> UrlGenerator.generateValidParam(v))); - flattened.put("headerNameValueMap", headerNameValueMap); - if (enableParamDescription) { - //get parameters info and put into result - List parameterRawList = operation.getParameters(); - List parametersResultList = new LinkedList<>(); - parameterRawList.forEach(parameter -> { - Map parameterMap = new HashMap<>(); - parameterMap.put("name", parameter.getName()); - parameterMap.put("description", parameter.getDescription()); - if(parameter.getRequired() != null) { - parameterMap.put("required", String.valueOf(parameter.getRequired())); - } - Schema schema = parameter.getSchema(); - if(schema != null) { - parameterMap.put("type", schema.getType()); - if(schema.getMinLength() != null) { - parameterMap.put("minLength", String.valueOf(schema.getMinLength())); - } - if(schema.getMaxLength() != null) { - parameterMap.put("maxLength", String.valueOf(schema.getMaxLength())); - } - } - parametersResultList.add(parameterMap); - }); - flattened.put("parameters", parametersResultList); - } - Response response = operation.getResponse("200"); - if(response != null) { - MediaType mediaType = response.getContentMediaType("application/json"); - if(mediaType != null) { - // first check if there is a single example defined. - Object example = mediaType.getExample(); - if(example != null) { - flattened.put("example", example); - } else { - // check if there are multiple examples - Map exampleMap = mediaType.getExamples(); - // use the first example if there are multiple - if(exampleMap.size() > 0) { - Map.Entry entry = exampleMap.entrySet().iterator().next(); - Example e = entry.getValue(); - if(e != null) { - flattened.put("example", e.getValue()); - } - } - } - } - } - result.add(flattened); - } - } - return result; - } - - private static String getBasePath(OpenApi3 openApi3) { - String basePath = ""; - String url = null; - if (openApi3.getServers().size() > 0) { - Server server = openApi3.getServer(0); - url = server.getUrl(); - } - if(url != null) { - // find "://" index - int protocolIndex = url.indexOf("://"); - int pathIndex = url.indexOf('/', protocolIndex + 3); - if(pathIndex > 0) { - basePath = url.substring(pathIndex); - } - } - return basePath; + return null; } - private static void addUnderscores(Map.Entry entryElement) { - Iterator iterator = entryElement.getValue().iterator(); - List list = new ArrayList<>(); - while (iterator.hasNext()) { - Any any = iterator.next(); - String value = any.toString().trim().replaceAll(" ", "_"); - list.add(Any.wrap(value)); - } - entryElement.setValue(Any.wrap(list)); - } - - } diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java index fa7aa2660..248452e96 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java @@ -1,77 +1,14 @@ package com.networknt.codegen.rest; -import com.jsoniter.JsonIterator; -import com.jsoniter.ValueType; -import com.jsoniter.any.Any; -import com.jsoniter.output.JsonStream; +import com.fasterxml.jackson.databind.JsonNode; import com.networknt.codegen.Generator; -import com.networknt.codegen.Utils; -import com.networknt.jsonoverlay.Overlay; -import com.networknt.oas.OpenApiParser; -import com.networknt.oas.model.*; -import com.networknt.oas.model.impl.OpenApi3Impl; -import com.networknt.utility.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import javax.lang.model.SourceVersion; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.*; -import java.util.stream.Collectors; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; -import static com.networknt.codegen.Generator.copyFile; -import static java.io.File.separator; - -/** - * The input for OpenAPI 3.0 AWS Lambda generator include config with json format and OpenAPI specification - * in yaml format. - * - * The model is OpenAPI specification in yaml format. And config file is config.json in JSON format. - * - * The generated project can be built, tested and debugged locally with AWS CLI, SAM and Docker installed. - * - * @author Steve Hu - */ public class OpenApiLambdaGenerator implements Generator { - - private Map typeMapping = new HashMap<>(); - - // optional generation parameters. if not set, they use default values as - boolean prometheusMetrics = false; - boolean skipHealthCheck = false; - boolean skipServerInfo = false; - boolean specChangeCodeReGenOnly = false; - boolean enableParamDescription = true; - boolean generateModelOnly = false; - boolean buildMaven = false; - boolean useLightProxy = false; - boolean publicVpc = true; - boolean skipPomFile = false; - - public OpenApiLambdaGenerator() { - typeMapping.put("array", "java.util.List"); - typeMapping.put("map", "java.util.Map"); - typeMapping.put("List", "java.util.List"); - typeMapping.put("boolean", "Boolean"); - typeMapping.put("string", "String"); - typeMapping.put("int", "Integer"); - typeMapping.put("float", "Float"); - typeMapping.put("number", "java.math.BigDecimal"); - typeMapping.put("DateTime", "Date"); - typeMapping.put("long", "Long"); - typeMapping.put("short", "Short"); - typeMapping.put("char", "String"); - typeMapping.put("double", "Double"); - typeMapping.put("object", "Object"); - typeMapping.put("integer", "Integer"); - } - @Override public String getFramework() { return "openapilambda"; @@ -86,919 +23,11 @@ public String getFramework() { * @throws IOException IO Exception occurs during code generation */ @Override - public void generate(final String targetPath, Object model, Any config) throws IOException { - // whoever is calling this needs to make sure that model is converted to Map - String projectName = config.toString("projectName").trim(); - - final String modelPackage = config.toString("modelPackage").trim(); - final String rootPackage = config.toString("rootPackage").trim(); - String handlerPackage = config.toString("handlerPackage").trim(); - - boolean overwriteHandler = config.toBoolean("overwriteHandler"); - boolean overwriteHandlerTest = config.toBoolean("overwriteHandlerTest"); - boolean overwriteModel = config.toBoolean("overwriteModel"); - boolean packageDocker = config.toBoolean("packageDocker"); - boolean enableRegistry = config.toBoolean("enableRegistry"); - generateModelOnly = config.toBoolean("generateModelOnly"); - useLightProxy = config.toBoolean("useLightProxy"); - buildMaven = config.toBoolean("buildMaven"); - String launchType = config.toString("launchType").trim(); - String region = config.toString("region").trim(); - publicVpc = config.toBoolean("publicVpc"); - specChangeCodeReGenOnly = config.toBoolean("specChangeCodeReGenOnly"); - enableParamDescription = config.toBoolean("enableParamDescription"); - skipPomFile = config.toBoolean("skipPomFile"); - String artifactId = config.toString("artifactId"); - String serviceId = config.get("groupId").toString().trim() + "." + artifactId.trim() + "-" + config.get("version").toString().trim(); - String version = config.toString("version").trim(); - // get the list of operations for this model - List> operationList = getOperationList(model); - List pathList = getPathList(operationList); - transfer(targetPath, "", ".gitignore", templates.lambda.gitignore.template()); - - transfer(targetPath, "", "README.md", templates.lambda.README.template(projectName, packageDocker, operationList)); - - if(!useLightProxy) { - // use AWS API Gateway to access Lambda functions. - transfer(targetPath, "", "template.yaml", templates.lambda.template.template(projectName, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); - } else { - // use light-proxy for Lambda function - if("EC2".equals(launchType)) { - if(publicVpc) { - transfer(targetPath, "", "public-vpc.yaml", templates.lambda.EC2.publicVpcYaml.template()); - transfer(targetPath, "", "public-proxy.yaml", templates.lambda.EC2.publicProxyYaml.template()); - transfer(targetPath, "", "template.yaml", templates.lambda.template.template(projectName, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); - } else { - transfer(targetPath, "", "private-vpc.yaml", templates.lambda.EC2.privateVpcYaml.template()); - transfer(targetPath, "", "private-proxy.yaml", templates.lambda.EC2.privateProxyYaml.template()); - transfer(targetPath, "", "template.yaml", templates.lambda.template.template(projectName, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); - } - } else { - // fargate as default - if(publicVpc) { - transfer(targetPath, "", "public-vpc.yaml", templates.lambda.Fargate.publicVpcYaml.template()); - transfer(targetPath, "", "public-proxy.yaml", templates.lambda.Fargate.publicProxyYaml.template()); - transfer(targetPath, "", "template.yaml", templates.lambda.template.template(projectName, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); - } else { - transfer(targetPath, "", "private-vpc.yaml", templates.lambda.Fargate.privateVpcYaml.template()); - transfer(targetPath, "", "private-proxy.yaml", templates.lambda.Fargate.privateProxyYaml.template()); - transfer(targetPath, "", "template.yaml", templates.lambda.template.template(projectName, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); - } - } - transfer(targetPath, "proxy", "handler.yml", - templates.lambda.proxy.handlerYml.template(serviceId, handlerPackage, operationList, prometheusMetrics, !skipHealthCheck, !skipServerInfo)); - - transfer(targetPath, "proxy", "lambda-invoker.yml", - templates.lambda.proxy.lambdaInvokerYml.template(region, operationList)); - try (InputStream is = new ByteArrayInputStream(((String)model).getBytes(StandardCharsets.UTF_8))) { - copyFile(is, Paths.get(targetPath, "proxy", "openapi.yaml")); - } - transfer(targetPath, "proxy", "server.yml", templates.lambda.proxy.server.template(serviceId, enableRegistry, version)); - - transfer(targetPath, "proxy", "openapi-security.yml", templates.rest.openapiSecurity.template()); - transfer(targetPath, "proxy", "openapi-validator.yml", templates.rest.openapiValidator.template()); - transfer(targetPath, "proxy", "client.yml", templates.rest.clientYml.template()); - - transfer(targetPath, "proxy", "primary.crt", templates.rest.primaryCrt.template()); - transfer(targetPath, "proxy", "secondary.crt", templates.rest.secondaryCrt.template()); - // transfer binary files without touching them. - try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/server.keystore")) { - copyFile(is, Paths.get(targetPath, "proxy", "server.keystore")); - } - try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/server.truststore")) { - copyFile(is, Paths.get(targetPath, "proxy", "server.truststore")); - } - try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/client.keystore")) { - copyFile(is, Paths.get(targetPath, "proxy", "client.keystore")); - } - try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/client.truststore")) { - copyFile(is, Paths.get(targetPath, "proxy", "client.truststore")); - } - // logging - transfer(targetPath, "proxy", "logback.xml", templates.rest.logback.template(rootPackage)); - // proxy.yml - transfer(targetPath, "proxy", "proxy.yml", templates.lambda.proxy.proxy.template()); - - // exclusion list for Config module - transfer(targetPath, "proxy", "config.yml", templates.rest.openapi.config.template(config)); - - transfer(targetPath, "proxy", "audit.yml", templates.rest.auditYml.template()); - transfer(targetPath, "proxy", "body.yml", templates.rest.bodyYml.template()); - transfer(targetPath, "proxy", "info.yml", templates.rest.infoYml.template()); - transfer(targetPath, "proxy", "correlation.yml", templates.rest.correlationYml.template()); - transfer(targetPath, "proxy", "metrics.yml", templates.rest.metricsYml.template()); - transfer(targetPath, "proxy", "sanitizer.yml", templates.rest.sanitizerYml.template()); - transfer(targetPath, "proxy", "traceability.yml", templates.rest.traceabilityYml.template()); - transfer(targetPath, "proxy", "health.yml", templates.rest.healthYml.template()); - // values.yml file, transfer to suppress the warning message during start startup and encourage usage. - transfer(targetPath, "proxy", "values.yml", templates.rest.openapi.values.template()); - // buildSh.rocker.raw for the docker image build - transfer(targetPath, "", "build.sh", templates.lambda.buildSh.template()); - // Dockerfile for the proxy - transfer(targetPath, "", "Dockerfile-proxy", templates.lambda.DockerfileProxy.template()); - } - - // handler - for (Map op : operationList) { - // for each operation, we need to generate a function in a separate folder. - String functionName = op.get("functionName").toString(); - - // generate event.json - transfer(targetPath, "events", "event" + functionName + ".json", templates.lambda.event.template()); - - - // generate Dockerfile if packageDocker is true - if(packageDocker) { - transfer(targetPath, functionName, "Dockerfile", templates.lambda.Dockerfile.template(handlerPackage)); - } - - if(buildMaven) { - // generate pom.xml - transfer(targetPath, functionName, "pom.xml", templates.lambda.pom.template(config, functionName)); - transferMaven(targetPath + separator + functionName); - } else { - transfer(targetPath, functionName, "build.gradle", templates.lambda.buildGradle.template(config)); - transfer(targetPath, functionName, "gradle.properties", templates.lambda.gradleProperties.template()); - transferGradle(targetPath + separator + functionName); - transfer(targetPath, functionName, "bootstrap", templates.lambda.bootstrap.template()); - transfer(targetPath, functionName, "build_graalvm.sh", templates.lambda.buildGraalvmSh.template(functionName)); - transfer(targetPath, functionName, "reflect.json", templates.lambda.reflectJson.template(handlerPackage)); - transfer(targetPath, functionName, "resource-config.json", templates.lambda.resourceJson.template()); - transfer(targetPath, functionName, "Makefile", templates.lambda.Makefile.template(functionName)); - - } - - // generate handler - String className = op.get("handlerName").toString(); - @SuppressWarnings("unchecked") - List parameters = (List)op.get("parameters"); - Map responseExample = (Map)op.get("responseExample"); - String example = responseExample.get("example"); - String statusCode = responseExample.get("statusCode"); - statusCode = StringUtils.isBlank(statusCode) || statusCode.equals("default") ? "-1" : statusCode; - - if (checkExist(targetPath + separator + functionName, ("src.main.java." + handlerPackage).replace(".", separator), "BusinessHandler.java")) { - continue; - } else { - transfer(targetPath + separator + functionName, ("src.main.java." + handlerPackage).replace(".", separator), "BusinessHandler.java", templates.lambda.BusinessHandler.template(handlerPackage, example)); - } - if (checkExist(targetPath + separator + functionName, ("src.test.java." + handlerPackage).replace(".", separator), "BusinessHandlerTest.java")) { - continue; - } else { - transfer(targetPath + separator + functionName, ("src.test.java." + handlerPackage).replace(".", separator), "BusinessHandlerTest.java", templates.lambda.BusinessHandlerTest.template(handlerPackage, op)); - } - if(useLightProxy) { - transfer(targetPath + separator + functionName, ("src.main.java." + handlerPackage).replace(".", separator), "App.java", templates.lambda.AppProxy.template(handlerPackage)); - } else { - transfer(targetPath + separator + functionName, ("src.main.java." + handlerPackage).replace(".", separator), "App.java", templates.lambda.AppGateway.template(handlerPackage)); - } - transfer(targetPath + separator + functionName, ("src.test.java." + handlerPackage).replace(".", separator), "AppTest.java", templates.lambda.AppTest.template(handlerPackage)); - - // generate model - Any anyComponents; - if (model instanceof Any) { - anyComponents = ((Any)model).get("components"); - } else if (model instanceof String) { - // this must be yaml format and we need to convert to json for JsonIterator. - OpenApi3 openApi3 = null; - try { - openApi3 = (OpenApi3)new OpenApiParser().parse((String)model, new URL("https://oas.lightapi.net/")); - } catch (MalformedURLException e) { - throw new RuntimeException("Failed to parse the model", e); - } - anyComponents = JsonIterator.deserialize(Overlay.toJson((OpenApi3Impl)openApi3).toString()).get("components"); - } else { - throw new RuntimeException("Invalid Model Class: " + model.getClass()); - } - - if (anyComponents.valueType() != ValueType.INVALID) { - Any schemas = anyComponents.asMap().get("schemas"); - if (schemas != null && schemas.valueType() != ValueType.INVALID) { - ArrayList modelCreators = new ArrayList<>(); - final HashMap references = new HashMap<>(); - for (Map.Entry entry : schemas.asMap().entrySet()) { - loadModel(entry.getKey(), null, entry.getValue().asMap(), schemas, overwriteModel, targetPath + separator + functionName, modelPackage, modelCreators, references, null); - } - - for (Runnable r : modelCreators) { - r.run(); - } - } - } - if(!useLightProxy) { - if (model instanceof Any) { - try (InputStream is = new ByteArrayInputStream(model.toString().getBytes(StandardCharsets.UTF_8))) { - copyFile(is, Paths.get(targetPath + separator + functionName, ("src.main.resources").replace(".", separator), "openapi.yaml")); - } - } else if (model instanceof String) { - try (InputStream is = new ByteArrayInputStream(((String)model).getBytes(StandardCharsets.UTF_8))) { - copyFile(is, Paths.get(targetPath + separator + functionName, ("src.main.resources").replace(".", separator), "openapi.yaml")); - } - } - } - - // app.yml - transfer(targetPath + separator + functionName, ("src.main.resources").replace(".", separator), "app.yml", templates.lambda.appYml.template(useLightProxy)); - - // logback.xml - transfer(targetPath + separator + functionName, ("src.main.resources").replace(".", separator), "logback.xml", templates.lambda.logback.template(rootPackage)); - transfer(targetPath + separator + functionName, ("src.test.resources").replace(".", separator), "logback-test.xml", templates.lambda.logback.template(rootPackage)); - // client truststore for the Prod stage. - if(!useLightProxy) { - try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/client.truststore")) { - copyFile(is, Paths.get(targetPath + separator + functionName, ("src.main.resources").replace(".", separator), "prod.truststore")); - } - } - } - } - - /** - * Initialize the property map with base elements as name, getter, setters, etc - * - * @param entry The entry for which to generate - * @param propMap The property map to add to, created in the caller - */ - private void initializePropertyMap(Map.Entry entry, Map propMap) { - String name = convertToValidJavaVariableName(entry.getKey()); - propMap.put("jsonProperty", Any.wrap(name)); - if (name.startsWith("@")) { - name = name.substring(1); - - } - propMap.put("name", Any.wrap(name)); - propMap.put("getter", Any.wrap("get" + name.substring(0, 1).toUpperCase() + name.substring(1))); - propMap.put("setter", Any.wrap("set" + name.substring(0, 1).toUpperCase() + name.substring(1))); - // assume it is not enum unless it is overwritten - propMap.put("isEnum", Any.wrap(false)); - propMap.put("isNumEnum", Any.wrap(false)); - } - - /** - * Handle elements listed as "properties" - * - * @param props The properties map to add to - */ - //private void handleProperties(List> props, Map.Entry entrySchema) { - private void handleProperties(List> props, Map properties) { - // transform properties - for (Map.Entry entryProp : properties.entrySet()) { - //System.out.println("key = " + entryProp.getKey() + " value = " + entryProp.getValue()); - Map propMap = new HashMap<>(); - - // initialize property map - initializePropertyMap(entryProp, propMap); - - String name = entryProp.getKey(); - String type = null; - boolean isArray = false; - for (Map.Entry entryElement : entryProp.getValue().asMap().entrySet()) { - //System.out.println("key = " + entryElement.getKey() + " value = " + entryElement.getValue()); - - if ("type".equals(entryElement.getKey())) { - String t = typeMapping.get(entryElement.getValue().toString()); - type = t; - if ("java.util.List".equals(t)) { - isArray = true; - } else { - propMap.putIfAbsent("type", Any.wrap(t)); - } - } - if ("items".equals(entryElement.getKey())) { - Any a = entryElement.getValue(); - if (a.get("$ref").valueType() != ValueType.INVALID && isArray) { - String s = a.get("$ref").toString(); - s = s.substring(s.lastIndexOf('/') + 1); - s = s.substring(0,1).toUpperCase() + (s.length() > 1 ? s.substring(1) : ""); - propMap.put("type", getListOf(s)); - } - if (a.get("type").valueType() != ValueType.INVALID && isArray) { - propMap.put("type", getListOf(typeMapping.get(a.get("type").toString()))); - } - } - if ("$ref".equals(entryElement.getKey())) { - String s = entryElement.getValue().toString(); - s = s.substring(s.lastIndexOf('/') + 1); - s = s.substring(0,1).toUpperCase() + (s.length() > 1 ? s.substring(1) : ""); - propMap.put("type", Any.wrap(s)); - } - if ("default".equals(entryElement.getKey())) { - Any a = entryElement.getValue(); - propMap.put("default", a); - } - if ("enum".equals(entryElement.getKey())) { - // different generate format for number enum - if ("Integer".equals(type) || "Double".equals(type) || "Float".equals(type) - || "Long".equals(type) || "Short".equals(type) || "java.math.BigDecimal".equals(type)) { - propMap.put("isNumEnum", Any.wrap(true)); - } - propMap.put("isEnum", Any.wrap(true)); - propMap.put("nameWithEnum", Any.wrap(name.substring(0, 1).toUpperCase() + name.substring(1) + "Enum")); - propMap.put("value", getValidEnumName(entryElement)); - } - - if ("format".equals(entryElement.getKey())) { - String s = entryElement.getValue().toString(); - - String ultimateType; - switch (s) { - case "date-time": - ultimateType = "java.time.LocalDateTime"; - break; - - case "date": - ultimateType = "java.time.LocalDate"; - break; - - case "double": - ultimateType = "java.lang.Double"; - break; - - case "float": - ultimateType = "java.lang.Float"; - break; - - case "int64": - ultimateType = "java.lang.Long"; - break; - - case "binary": - ultimateType = "byte[]"; - propMap.put(COMPARATOR, Any.wrap("Arrays")); - propMap.put(HASHER, Any.wrap("Arrays")); - break; - - case "byte": - ultimateType = "byte"; - break; - - default: - ultimateType = null; - } - - if (ultimateType != null) { - propMap.put("type", Any.wrap(ultimateType)); - } - } - - if ("oneOf".equals(entryElement.getKey())) { - List list = entryElement.getValue().asList(); - Any t = list.get(0).asMap().get("type"); - if (t != null) { - propMap.put("type", Any.wrap(typeMapping.get(t.toString()))); - } else { - // maybe reference? default type to object. - propMap.put("type", Any.wrap("Object")); - } - } - if ("anyOf".equals(entryElement.getKey())) { - List list = entryElement.getValue().asList(); - Any t = list.get(0).asMap().get("type"); - if (t != null) { - propMap.put("type", Any.wrap(typeMapping.get(t.toString()))); - } else { - // maybe reference? default type to object. - propMap.put("type", Any.wrap("Object")); - } - } - if ("allOf".equals(entryElement.getKey())) { - List list = entryElement.getValue().asList(); - Any t = list.get(0).asMap().get("type"); - if (t != null) { - propMap.put("type", Any.wrap(typeMapping.get(t.toString()))); - } else { - // maybe reference? default type to object. - propMap.put("type", Any.wrap("Object")); - } - } - if ("not".equals(entryElement.getKey())) { - Map m = entryElement.getValue().asMap(); - Any t = m.get("type"); - if (t != null) { - propMap.put("type", t); - } else { - propMap.put("type", Any.wrap("Object")); - } - } - } - props.add(propMap); - } - } - public static final String HASHER = "hasher"; - public static final String COMPARATOR = "comparator"; - - private Any getListOf(String s) { - return new UnresolvedTypeListAny(s); - } - - private static abstract class UnresolvedTypeAny extends Any { - - Any type; - - UnresolvedTypeAny(Any type) { - this.type = type; - } - - private Any get() { - return type; - } - - private void set(Any type) { - this.type = type; - } - - @Override - public Object object() { - return toString(); - } - - @Override - public boolean toBoolean() { - return Boolean.parseBoolean(toString()); - } - - @Override - public int toInt() { - return Integer.parseInt(toString()); - } - - @Override - public long toLong() { - return Long.parseLong(toString()); - } - - @Override - public float toFloat() { - return Float.parseFloat(toString()); - } + public void generate(final String targetPath, Object model, JsonNode config) throws IOException { - @Override - public double toDouble() { - return Double.parseDouble(toString()); - } - } - - private static class UnresolvedTypeHolderAny extends UnresolvedTypeAny { - - UnresolvedTypeHolderAny(Any resolved) { - super(resolved); - } - - @Override - public ValueType valueType() { - return type.valueType(); - } - - @Override - public String toString() { - return type.toString(); - } - - @Override - public void writeTo(JsonStream stream) throws IOException { - type.writeTo(stream); - } } - - private static class UnresolvedTypeListAny extends UnresolvedTypeAny { - - UnresolvedTypeListAny(Any type) { - super(type); - } - - UnresolvedTypeListAny(String string) { - super(Any.wrap(string)); - } - - @Override - public ValueType valueType() { - return ValueType.ARRAY; - } - - @Override - public void writeTo(JsonStream stream) throws IOException { - stream.writeRaw(toString()); - } - - @Override - public String toString() { - return UnresolvedTypeListAny.toString(type); - } - - private static String toString(Any type) { - return String.format("java.util.List<%s>", type.toString()); - } - } - public List> getOperationList(Object model) { - List> result = new ArrayList<>(); - String s; - if (model instanceof Any) { - s = ((Any)model).toString(); - } else if (model instanceof String) { - s = (String)model; - } else { - throw new RuntimeException("Invalid Model Class: " + model.getClass()); - } - OpenApi3 openApi3 = null; - try { - openApi3 = (OpenApi3)new OpenApiParser().parse(s, new URL("https://oas.lightapi.net/")); - } catch (MalformedURLException e) { - } - String basePath = getBasePath(openApi3); - - Map paths = openApi3.getPaths(); - for (Map.Entry entryPath : paths.entrySet()) { - String path = entryPath.getKey(); - Path pathValue = entryPath.getValue(); - for (Map.Entry entryOps : pathValue.getOperations().entrySet()) { - // skip all the entries that are not http method. The only possible entries - // here are extensions. which will be just a key value pair. - if (entryOps.getKey().startsWith("x-")) { - continue; - } - Map flattened = new HashMap<>(); - flattened.put("method", entryOps.getKey().toUpperCase()); - flattened.put("capMethod", entryOps.getKey().substring(0, 1).toUpperCase() + entryOps.getKey().substring(1)); - flattened.put("path", basePath + path); - String normalizedPath = path.replace("{", "").replace("}", ""); - flattened.put("handlerName", Utils.camelize(normalizedPath) + Utils.camelize(entryOps.getKey()) + "Handler"); - flattened.put("functionName", Utils.camelize(normalizedPath) + Utils.camelize(entryOps.getKey()) + "Function"); - flattened.put("endpoint", path + "@" + entryOps.getKey().toLowerCase()); - flattened.put("apiName", Utils.camelize(normalizedPath) + Utils.camelize(entryOps.getKey())); - Operation operation = entryOps.getValue(); - flattened.put("normalizedPath", UrlGenerator.generateUrl(basePath, path, entryOps.getValue().getParameters())); - //eg. 200 || statusCode == 400 || statusCode == 500 - flattened.put("supportedStatusCodesStr", operation.getResponses().keySet().stream().collect(Collectors.joining(" || statusCode = "))); - Map headerNameValueMap = operation.getParameters() - .stream() - .filter(parameter -> parameter.getIn().equals("header")) - .collect(Collectors.toMap(k -> k.getName(), v -> UrlGenerator.generateValidParam(v))); - flattened.put("headerNameValueMap", headerNameValueMap); - flattened.put("requestBodyExample", populateRequestBodyExample(operation)); - Map responseExample = populateResponseExample(operation); - flattened.put("responseExample", responseExample); - flattened.put("scopes", getScopes(operation)); - if (enableParamDescription) { - //get parameters info and put into result - List parameterRawList = operation.getParameters(); - List parametersResultList = new LinkedList<>(); - parameterRawList.forEach(parameter -> { - Map parameterMap = new HashMap<>(); - parameterMap.put("name", parameter.getName()); - parameterMap.put("description", parameter.getDescription()); - if (parameter.getRequired() != null) { - parameterMap.put("required", String.valueOf(parameter.getRequired())); - } - Schema schema = parameter.getSchema(); - if (schema != null) { - parameterMap.put("type", schema.getType()); - if (schema.getMinLength() != null) { - parameterMap.put("minLength", String.valueOf(schema.getMinLength())); - } - if (schema.getMaxLength() != null) { - parameterMap.put("maxLength", String.valueOf(schema.getMaxLength())); - } - } - parametersResultList.add(parameterMap); - }); - flattened.put("parameters", parametersResultList); - } - result.add(flattened); - } - } - return result; - } - - private static String getBasePath(OpenApi3 openApi3) { - String basePath = ""; - String url = null; - if (openApi3.getServers().size() > 0) { - Server server = openApi3.getServer(0); - url = server.getUrl(); - } - if (url != null) { - // find "://" index - int protocolIndex = url.indexOf("://"); - int pathIndex = url.indexOf('/', protocolIndex + 3); - if (pathIndex > 0) { - basePath = url.substring(pathIndex); - } - } - return basePath; - } - - // method used to generate valid enum keys for enum contents - private Any getValidEnumName(Map.Entry entryElement) { - Iterator iterator = entryElement.getValue().iterator(); - Map map = new HashMap<>(); - while (iterator.hasNext()) { - String string = iterator.next().toString().trim(); - if (string.equals("")) continue; - if (isEnumHasDescription(string)) { - map.put(convertToValidJavaVariableName(getEnumName(string)).toUpperCase(), Any.wrap(getEnumDescription(string))); - } else { - map.put(convertToValidJavaVariableName(string).toUpperCase(), Any.wrap(string)); - } - } - return Any.wrap(map); - } - - // method used to convert string to valid java variable name - // 1. replace invalid character with '_' - // 2. prefix number with '_' - // 3. convert the first character of java keywords to upper case - public static String convertToValidJavaVariableName(String string) { - if (string == null || string.equals("") || SourceVersion.isName(string)) { - return string; - } - // to validate whether the string is Java keyword - if (SourceVersion.isKeyword(string)) { - return "_" + string; - } - // replace invalid characters with underscore - StringBuilder stringBuilder = new StringBuilder(); - if (!Character.isJavaIdentifierStart(string.charAt(0))) { - stringBuilder.append('_'); - } - for (char c : string.toCharArray()) { - if (!Character.isJavaIdentifierPart(c)) { - stringBuilder.append('_'); - } else { - stringBuilder.append(c); - } - } - return stringBuilder.toString(); - } - - private boolean isEnumHasDescription(String string) { - return string.contains(":") || string.contains("{") || string.contains("("); - } - - private String getEnumName(String string) { - if (string.contains(":")) return string.substring(0, string.indexOf(":")).trim(); - if (string.contains("(") && string.contains(")")) return string.substring(0, string.indexOf("(")).trim(); - if (string.contains("{") && string.contains("}")) return string.substring(0, string.indexOf("{")).trim(); - return string; - } - - private String getEnumDescription(String string) { - if (string.contains(":")) return string.substring(string.indexOf(":") + 1).trim(); - if (string.contains("(") && string.contains(")")) return string.substring(string.indexOf("(") + 1, string.indexOf(")")).trim(); - if (string.contains("{") && string.contains("}")) return string.substring(string.indexOf("{") + 1, string.indexOf("}")).trim(); - - return string; - } - - private String getScopes(Operation operation) { - String scopes = null; - SecurityRequirement securityRequirement = operation.getSecurityRequirement(0); - if(securityRequirement != null) { - Map requirements = securityRequirement.getRequirements(); - for(SecurityParameter parameter : requirements.values()) { - List ls = parameter.getParameters(); - if(ls != null) scopes = StringUtils.join(ls, ' '); - } - } - return scopes; - } - - private String populateRequestBodyExample(Operation operation) { - String result = "{\"content\": \"request body to be replaced\"}"; - RequestBody body = operation.getRequestBody(); - if (body != null) { - MediaType mediaType = body.getContentMediaType("application/json"); - if (mediaType != null) { - Object valueToBeStringify = null; - if (mediaType.getExamples() != null && !mediaType.getExamples().isEmpty()) { - for (Map.Entry entry : mediaType.getExamples().entrySet()) { - valueToBeStringify = entry.getValue().getValue(); - } - } else if (mediaType.getExample() != null) { - valueToBeStringify = mediaType.getExample(); - } - if (valueToBeStringify == null) { - return result; - } - result = JsonStream.serialize(valueToBeStringify); - if (result.startsWith("\"")) { - result = result.substring(1, result.length() - 1); - } - } - } - return result; - } - - private Map populateResponseExample(Operation operation) { - Map result = new HashMap<>(); - Object example; - for (String statusCode : operation.getResponses().keySet()) { - Optional response = Optional.ofNullable(operation.getResponse(String.valueOf(statusCode))); - if (response.get().getContentMediaTypes().size() == 0) { - result.put("statusCode", statusCode); - result.put("example", "{}"); - } - for (String mediaTypeStr : response.get().getContentMediaTypes().keySet()) { - Optional mediaType = Optional.ofNullable(response.get().getContentMediaType(mediaTypeStr)); - example = mediaType.get().getExample(); - if (example != null) { - result.put("statusCode", statusCode); - result.put("example", JsonStream.serialize(example)); - } else { - // check if there are multiple examples - Map exampleMap = mediaType.get().getExamples(); - // use the first example if there are multiple - if (exampleMap.size() > 0) { - Map.Entry entry = exampleMap.entrySet().iterator().next(); - Example e = entry.getValue(); - if (e != null) { - result.put("statusCode", statusCode); - result.put("example", JsonStream.serialize(e.getValue())); - } - } - } - } - } - return result; - } - - private static final Logger logger = LoggerFactory.getLogger(OpenApiGenerator.class); - - private void loadModel(String classVarName, String parentClassName, Map value, Any schemas, boolean overwriteModel, String targetPath, String modelPackage, List modelCreators, Map references, List> parentClassProps) throws IOException { - final String modelFileName = classVarName.substring(0, 1).toUpperCase() + classVarName.substring(1); - final List> props = new ArrayList<>(); - final List> parentProps = (parentClassProps == null) ? new ArrayList<>() : new ArrayList<>(parentClassProps); - String type = null; - String enums = null; - boolean isEnumClass = false; - List required = null; - boolean isAbstractClass = false; - - // iterate through each schema in the components - Queue> schemaElementQueue = new LinkedList<>(); - // cache the visited elements to prevent loop reference - Set seen = new HashSet<>(); - // add elements into queue to perform a BFS - for (Map.Entry entrySchema : value.entrySet()) { - schemaElementQueue.offer(entrySchema); - } - while (!schemaElementQueue.isEmpty()) { - Map.Entry currentElement = schemaElementQueue.poll(); - String currentElementKey = currentElement.getKey(); - // handle the base elements - if ("type".equals(currentElementKey) && type == null) { - type = currentElement.getValue().toString(); - } - if ("enum".equals(currentElementKey)) { - isEnumClass = true; - enums = currentElement.getValue().asList().toString(); - enums = enums.substring(enums.indexOf("[") + 1, enums.indexOf("]")); - } - if ("properties".equals(currentElementKey)) { - handleProperties(props, currentElement.getValue().asMap()); - } - if ("required".equals(currentElementKey)) { - if (required == null) { - required = new ArrayList<>(); - } - required.addAll(currentElement.getValue().asList()); - } - // expend the ref elements and add to the queue - if ("$ref".equals(currentElementKey)) { - String s = currentElement.getValue().toString(); - s = s.substring(s.lastIndexOf('/') + 1); - if (seen.contains(s)) continue; - seen.add(s); - for (Map.Entry schema : schemas.get(s).asMap().entrySet()) { - schemaElementQueue.offer(schema); - } - } - // expand the allOf elements and add to the queue - if ("allOf".equals(currentElementKey)) { - for (Any listItem : currentElement.getValue().asList()) { - for (Map.Entry allOfItem : listItem.asMap().entrySet()) { - schemaElementQueue.offer(allOfItem); - } - } - } - // call loadModel recursively to generate new model corresponding to each oneOf elements - if ("oneOf".equals(currentElementKey)) { - isAbstractClass = true; - parentProps.addAll(props); - String parentName = classVarName.substring(0, 1) + classVarName.substring(1); - for (Any listItem : currentElement.getValue().asList()) { - for (Map.Entry oneOfItem : listItem.asMap().entrySet()) { - if ("$ref".equals(oneOfItem.getKey())) { - String s = oneOfItem.getValue().toString(); - s = s.substring(s.lastIndexOf('/') + 1); - loadModel(extendModelName(s, classVarName), s, schemas.get(s).asMap(), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, parentProps); - } - } - } - } - } - // Check the type of current schema. Generation will be executed only if the type of the schema equals to object. - // Since generate a model for primitive types and arrays do not make sense, and an error class would be generated - // due to lack of properties if force to generate. - if (type == null && !isAbstractClass) { - throw new RuntimeException("Cannot find the schema type of \"" + modelFileName + "\" in #/components/schemas/ of the specification file. In most cases, you need to add \"type: object\" if you want to generate a POJO. Otherwise, give it a type of primary like string or number."); - } - - if ("object".equals(type) || isEnumClass) { - if (!overwriteModel && checkExist(targetPath, ("src.main.java." + modelPackage).replace(".", separator), modelFileName + ".java")) { - return; - } - - final String enumsIfClass = isEnumClass ? enums : null; - final boolean abstractIfClass = isAbstractClass; - modelCreators.add(() -> { - final int referencesCount = references.size(); - for (Map properties : props) { - Any any = properties.get("type"); - if (any != null) { - if (any.valueType() == ValueType.STRING) { - Any resolved = references.get(any.toString()); - if (resolved == null) { - continue; - } - any = new UnresolvedTypeHolderAny(resolved); - properties.put("type", any); - } - - int iteration = 0; - do { - UnresolvedTypeAny previous = null; - while (any instanceof UnresolvedTypeAny) { - previous = (UnresolvedTypeAny)any; - any = ((UnresolvedTypeAny)any).get(); - } - - if (any == null) { - break; - } else if (iteration++ > referencesCount) { - throw new TypeNotPresentException(any.toString(), null); - } - - if (any.valueType() == ValueType.STRING) { - any = references.get(any.toString()); - if (any == null) { - break; - } else { - previous.set(any); - } - } else { - break; - } - } while (true); - } - } - - try { - transfer(targetPath, - ("src.main.java." + modelPackage).replace(".", separator), - modelFileName + ".java", - enumsIfClass == null - ? templates.rest.pojo.template(modelPackage, modelFileName, parentClassName, classVarName, abstractIfClass, props, parentClassProps) - : templates.rest.enumClass.template(modelPackage, modelFileName, enumsIfClass)); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - }); - } else { - HashMap map = new HashMap<>(1); - map.put(classVarName, Any.wrap(value)); - handleProperties(props, map); - if (props.isEmpty()) { - throw new IllegalStateException("Properties empty for " + classVarName + "!"); - } - - references.put(modelFileName, props.get(0).get("type")); - } - } - private String extendModelName(String str1, String str2) { - return str1 + str2.substring(0, 1).toUpperCase() + str2.substring(1); - } - - private List getPathList(List> operationList) { - List pathList = new ArrayList<>(); - Set pathSet = new HashSet<>(); - OpenApiPath openApiPath = null; - for(Map op : operationList) { - String path = (String)op.get("path"); - String method = ((String)op.get("method")).toLowerCase(); - String functionName = (String)op.get("functionName"); - if(!pathSet.contains(path)) { - openApiPath = new OpenApiPath(); - openApiPath.setPath(path); - pathSet.add(path); - MethodFunction methodFunction = new MethodFunction(method, functionName); - openApiPath.addMethodFunction(methodFunction); - pathList.add(openApiPath); - } else { - MethodFunction methodFunction = new MethodFunction(method, functionName); - openApiPath.addMethodFunction(methodFunction); - } - } - return pathList; + return null; } public class OpenApiPath { diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpecGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpecGenerator.java index 7e90f9eef..2330a5ac3 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpecGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpecGenerator.java @@ -1,184 +1,26 @@ package com.networknt.codegen.rest; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.codegen.Generator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.jsoniter.any.Any; -import com.networknt.codegen.Generator; - -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ScanResult; -import io.swagger.v3.core.converter.ModelConverters; -import io.swagger.v3.core.util.Json; -import io.swagger.v3.core.util.Yaml; -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.media.Schema; +import java.io.IOException; public class OpenApiSpecGenerator implements Generator { - private static final Logger logger = LoggerFactory.getLogger(OpenApiSpecGenerator.class); - - private static final String FRAMEWORK="openapi-spec"; - - /** -- configuration items begin -- */ - private static final String CONFIG_SPECGENERATION ="specGeneration"; - // comma delimited package names - private static final String CONFIG_MODELPACKAGES ="modelPackages"; - // absolute path of an existing spec file. If this is specified, the generated models (a.k.a. components) will be added to this file and override existing schemas, if any. - private static final String CONFIG_MERGETO ="mergeTo"; - // comma delimited formats, currently support json, yml, or yaml - private static final String CONFIG_OUTPUTFORMAT="outputFormat"; - // the output file name without extension - private static final String CONFIG_OUTPUTFILENAME="outputFilename"; - /** -- configuration items end -- */ - - private static final String DOT = "."; - private static final String COMMA_SPACE = "\\s*,\\s*"; - private static final String JSON="json"; - private static final String YAML="yaml"; - private static final String YML="yml"; - private static final String DEFAULT_OUTPUT_NAME="openapi_generated"; - + private static final Logger logger = LoggerFactory.getLogger(OpenApiSpecGenerator.class); + + private static final String FRAMEWORK="openapi-spec"; + + @Override + public String getFramework() { + return FRAMEWORK; + } - @Override - public String getFramework() { - return FRAMEWORK; - } + @SuppressWarnings("rawtypes") + @Override + public void generate(String targetPath, Object model, JsonNode config) throws IOException { - @SuppressWarnings("rawtypes") - @Override - public void generate(String targetPath, Object model, Any config) throws IOException { - if (StringUtils.isBlank(targetPath)) { - logger.error("Output location is not specified."); - return; - } - - if (!config.keys().contains(CONFIG_SPECGENERATION)) { - logger.error("Missing config: cannot find {} in the specified config file", CONFIG_SPECGENERATION); - return; - } - - Map genConfig = config.get(CONFIG_SPECGENERATION).asMap(); - String modelPackages = StringUtils.trimToEmpty(genConfig.get(CONFIG_MODELPACKAGES).toString()); - String mergeTo = StringUtils.trimToEmpty(genConfig.get(CONFIG_MERGETO).toString()); - String outputFormat = StringUtils.trimToEmpty(genConfig.get(CONFIG_OUTPUTFORMAT).toString()); - String outputFilename = StringUtils.trimToEmpty(genConfig.get(CONFIG_OUTPUTFILENAME).toString()); - - File output_dir = new File(targetPath); - - if (!output_dir.exists() || !output_dir.isDirectory()) { - output_dir.mkdirs(); - } - - String[] basePackageArray = modelPackages.split(COMMA_SPACE); - - Map schemas = new HashMap<>(); - - for (String packageName: basePackageArray) { - try (ScanResult scanResult = - new ClassGraph() - .enableClassInfo() - .whitelistPackages(packageName) - .scan()) { - - List> classes = scanResult.getAllClasses().loadClasses(); - - for (Class cls: classes) { - schemas.putAll(ModelConverters.getInstance().read(cls)); - } - } - } - - OpenAPI openApi = new OpenAPI(); - - openApi.setComponents(new Components().schemas(schemas)); - - openApi = merge(openApi, mergeTo); - - String[] formats = outputFormat.split(COMMA_SPACE); - - String filename = StringUtils.isBlank(outputFilename)?DEFAULT_OUTPUT_NAME:outputFilename; - - for (String format: formats) { - dump(openApi, format, new File(output_dir, filename + DOT + format)); - } - } - - private void dump(OpenAPI openApi, String format, File outputFile) throws IOException { - String specStr=StringUtils.EMPTY; - - if (StringUtils.equalsIgnoreCase(format, JSON)) { - specStr = Json.pretty(openApi); - }else if (StringUtils.equalsIgnoreCase(format, YML) || StringUtils.equalsIgnoreCase(format, YAML)){ - specStr = Yaml.pretty(openApi); - }else { - throw new UnsupportedOperationException("Unknow output format " + format); - } - - Files.write(Paths.get(outputFile.toURI()), specStr.getBytes()); - } + } - @SuppressWarnings("rawtypes") - private OpenAPI merge(OpenAPI generatedSpec, String mergeTo) { - if (StringUtils.isNotBlank(mergeTo)) { - File destFile = new File(mergeTo); - - if (destFile.isFile()) { - try { - OpenAPI openAPI=null; - String ext = getFileExtension(destFile); - if (StringUtils.equalsIgnoreCase(ext, JSON)) { - openAPI = Json.mapper().readValue(destFile, OpenAPI.class); - }else if (StringUtils.equalsIgnoreCase(ext, YML)||StringUtils.equalsIgnoreCase(ext, YAML)) { - openAPI = Yaml.mapper().readValue(destFile, OpenAPI.class); - }else { - throw new UnsupportedOperationException("Unknow file format " + ext); - } - - if (null!=openAPI) { - Components components = openAPI.getComponents(); - - if (null == components) { - components = new Components(); - } - - Map schemas = new HashMap<>(); - - schemas.putAll(components.getSchemas()); - - schemas.putAll(generatedSpec.getComponents().getSchemas()); - - components.setSchemas(schemas); - - openAPI.setComponents(components); - - return openAPI; - } - - } catch (Exception e) { - logger.error(e.getMessage(), e); - } - } - } - - return generatedSpec; - } - - private String getFileExtension(File file) { - String name = file.getName(); - int lastIndexOf = name.lastIndexOf(DOT); - if (lastIndexOf < 0) { - return StringUtils.EMPTY; - } - return name.substring(lastIndexOf+1); - } } diff --git a/light-rest-4j/src/main/resources/templates/lambda/buildGradle.rocker.raw b/light-rest-4j/src/main/resources/templates/lambda/buildGradle.rocker.raw index 7daee0986..d8a9cf431 100644 --- a/light-rest-4j/src/main/resources/templates/lambda/buildGradle.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/lambda/buildGradle.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) plugins { id 'java' id "com.github.johnrengelman.shadow" version "5.2.0" @@ -17,7 +17,7 @@ dependencies { implementation "com.networknt:utility:$light4jVersion" implementation "com.networknt:slf4j-logback:$light4jVersion" implementation "com.networknt:custom-runtime:$light4jVersion" - implementation "com.networknt:@if(config.toBoolean("useLightProxy")){lambda-interceptor}else{request-handler}:$light4jVersion" + implementation "com.networknt:@if(config.get("useLightProxy").booleanValue()){lambda-interceptor}else{request-handler}:$light4jVersion" implementation "com.amazonaws:aws-lambda-java-core:$lambdaCoreVersion" implementation "com.amazonaws:aws-lambda-java-events:$lambdaEventsVersion" testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" diff --git a/light-rest-4j/src/main/resources/templates/lambda/pom.xml.rocker.raw b/light-rest-4j/src/main/resources/templates/lambda/pom.xml.rocker.raw index e7d96b373..6c39126ac 100644 --- a/light-rest-4j/src/main/resources/templates/lambda/pom.xml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/lambda/pom.xml.rocker.raw @@ -1,11 +1,11 @@ -@import com.jsoniter.any.Any -@args (Any config, String functionName) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config, String functionName) 4.0.0 - @config.get("groupId") + @config.get("groupId").textValue() @functionName - @config.get("version") + @config.get("version").textValue() jar @functionName @@ -61,7 +61,7 @@ com.networknt - @if(config.toBoolean("useLightProxy")){lambda-interceptor}else{request-handler} + @if(config.get("useLightProxy").booleanValue()){lambda-interceptor}else{request-handler} ${version.light-4j} diff --git a/light-rest-4j/src/main/resources/templates/lambda/proxy/handlerYml.rocker.raw b/light-rest-4j/src/main/resources/templates/lambda/proxy/handlerYml.rocker.raw index 2300dd8dd..9a7e64f50 100644 --- a/light-rest-4j/src/main/resources/templates/lambda/proxy/handlerYml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/lambda/proxy/handlerYml.rocker.raw @@ -1,6 +1,5 @@ @import java.util.Map @import java.util.List -@import com.jsoniter.any.Any @args (String serviceId, String handlerPackage, List> items, boolean prometheusMetrics, boolean healthCheck, boolean serverInfo) # Handler middleware chain configuration diff --git a/light-rest-4j/src/main/resources/templates/lambda/proxy/lambdaInvokerYml.rocker.raw b/light-rest-4j/src/main/resources/templates/lambda/proxy/lambdaInvokerYml.rocker.raw index 520e91a75..53dbcbe30 100644 --- a/light-rest-4j/src/main/resources/templates/lambda/proxy/lambdaInvokerYml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/lambda/proxy/lambdaInvokerYml.rocker.raw @@ -1,6 +1,5 @@ @import java.util.Map @import java.util.List -@import com.jsoniter.any.Any @args (String region, List> items) # The aws region that is used to create the LambdaClient. region: @region diff --git a/light-rest-4j/src/main/resources/templates/rest/dockerfile.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/dockerfile.rocker.raw index e4ee9f607..e4bc50f39 100644 --- a/light-rest-4j/src/main/resources/templates/rest/dockerfile.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/dockerfile.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config, String expose) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config, String expose) FROM azul/zulu-openjdk-alpine:11 as packager RUN { \ @@ -28,5 +28,5 @@ ENV JAVA_MINIMAL=/opt/jre ENV PATH="$PATH:$JAVA_MINIMAL/bin" COPY --from=packager "$JAVA_MINIMAL" "$JAVA_MINIMAL" -@with (name = config.get("artifactId") + "-" + config.get("version") + ".jar") {COPY /target/@name server.jar} +@with (name = config.get("artifactId").textValue() + "-" + config.get("version").textValue() + ".jar") {COPY /target/@name server.jar} CMD ["/bin/sh","-c","java -Dlight-4j-config-dir=/config -Dlogback.configurationFile=/config/logback.xml -jar /server.jar"] diff --git a/light-rest-4j/src/main/resources/templates/rest/dockerfileslim.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/dockerfileslim.rocker.raw index abf212e19..a0509eb67 100644 --- a/light-rest-4j/src/main/resources/templates/rest/dockerfileslim.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/dockerfileslim.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config, String expose) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config, String expose) FROM openjdk:11.0.3-slim -@with (name = config.get("artifactId") + "-" + config.get("version") + ".jar") {ADD /target/@name server.jar} +@with (name = config.get("artifactId").textValue() + "-" + config.get("version").textValue() + ".jar") {ADD /target/@name server.jar} CMD ["/bin/sh","-c","java -Dlight-4j-config-dir=/config -Dlogback.configurationFile=/config/logback.xml -jar /server.jar"] diff --git a/light-rest-4j/src/main/resources/templates/rest/enumInline.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/enumInline.rocker.raw index 9b8a937bd..ed10c26bb 100644 --- a/light-rest-4j/src/main/resources/templates/rest/enumInline.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/enumInline.rocker.raw @@ -1,11 +1,10 @@ -@import com.jsoniter.any.Any @import java.util.Map @import java.util.List -@args (Map prop) - @with (v = prop.get("nameWithEnum") + ".values()", value = prop.get("value").asMap()) { +@args (Map prop) + @with (v = prop.get("nameWithEnum") + ".values()", value = (Map)prop.get("value")) { public enum @prop.get("nameWithEnum") { @for((i, key, item) : value) { - @if (!prop.get("isNumEnum").toBoolean()) {@if (i.index() < value.size() - 1) {@key ("@item"),}@if(i.index() == value.size() - 1) {@key ("@item");}} @if (prop.get("isNumEnum").toBoolean()) {@if (i.index() < value.size() - 1) {@key (new @prop.get("type")("@item")),}@if(i.index() == value.size() - 1) {@key (new @prop.get("type")("@item"));}} + @if (!(Boolean)prop.get("isNumEnum")) {@if (i.index() < value.size() - 1) {@key ("@item"),}@if(i.index() == value.size() - 1) {@key ("@item");}} @if ((Boolean)prop.get("isNumEnum")) {@if (i.index() < value.size() - 1) {@key (new @prop.get("type")("@item")),}@if(i.index() == value.size() - 1) {@key (new @prop.get("type")("@item"));}} } private final @prop.get("type") value; diff --git a/light-rest-4j/src/main/resources/templates/rest/handlerTest.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/handlerTest.rocker.raw index f7511416d..ad48dc905 100644 --- a/light-rest-4j/src/main/resources/templates/rest/handlerTest.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/handlerTest.rocker.raw @@ -1,6 +1,5 @@ @import java.util.Map -@import com.jsoniter.any.Any -@args (String handlerPackage, Map map) +@args (String handlerPackage, Map map) package @handlerPackage; import com.networknt.client.Http2Client; diff --git a/light-rest-4j/src/main/resources/templates/rest/model.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/model.rocker.raw index dbee02280..747bebbdf 100644 --- a/light-rest-4j/src/main/resources/templates/rest/model.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/model.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (String modelPackage, String className, Any props) +@import com.fasterxml.jackson.databind.JsonNode +@args (String modelPackage, String className) package @modelPackage; import java.io.Serializable; diff --git a/light-rest-4j/src/main/resources/templates/rest/openapi/config.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/openapi/config.rocker.raw index d8095110d..f947fa675 100644 --- a/light-rest-4j/src/main/resources/templates/rest/openapi/config.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/openapi/config.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) #---------------------------------------------------------------------------------------------------------------- # Scalable Config file # @@ -18,4 +18,4 @@ exclusionConfigFileList: - values - status -decryptorClass: @with(decryptOption = config.toString("decryptOption")) {@if(decryptOption.equals("stdin")) {com.networknt.decrypt.ManualAESDecryptor} else if(decryptOption.equals("environment")) {com.networknt.decrypt.AutoAESDecryptor} else {com.networknt.decrypt.AESDecryptor}} \ No newline at end of file +decryptorClass: @with(decryptOption = config.get("decryptOption").textValue()) {@if(decryptOption.equals("stdin")) {com.networknt.decrypt.ManualAESDecryptor} else if(decryptOption.equals("environment")) {com.networknt.decrypt.AutoAESDecryptor} else {com.networknt.decrypt.AESDecryptor}} \ No newline at end of file diff --git a/light-rest-4j/src/main/resources/templates/rest/openapi/handlerTest.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/openapi/handlerTest.rocker.raw index 1850395f2..f8b24526d 100644 --- a/light-rest-4j/src/main/resources/templates/rest/openapi/handlerTest.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/openapi/handlerTest.rocker.raw @@ -1,5 +1,4 @@ @import java.util.Map -@import com.jsoniter.any.Any @import java.util.stream.Collectors @import org.apache.commons.text.StringEscapeUtils @args (String handlerPackage, Map map) diff --git a/light-rest-4j/src/main/resources/templates/rest/openapi/handlerYml.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/openapi/handlerYml.rocker.raw index 7ac6d8684..bcc226a29 100644 --- a/light-rest-4j/src/main/resources/templates/rest/openapi/handlerYml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/openapi/handlerYml.rocker.raw @@ -1,7 +1,6 @@ @import java.util.Map @import java.util.List -@import com.jsoniter.any.Any -@args (String serviceId, String handlerPackage, List> items, boolean prometheusMetrics, boolean healthCheck, boolean serverInfo) +@args (String serviceId, String handlerPackage, List> items, boolean prometheusMetrics) # Handler middleware chain configuration #---------------------------------------- @@ -89,16 +88,16 @@ paths: - default - @with (p = handlerPackage + ".") {@p}@item.get("handlerName") } -@if(healthCheck){ - path: '/health/${server.serviceId:@serviceId}' + - path: '/health/${server.serviceId:@serviceId}' method: 'get' exec: - health -} -@if(serverInfo){ - path: '/server/info' + + - path: '/server/info' method: 'get' exec: - info -} + @if(prometheusMetrics){ - path: '/prometheus' method: 'get' exec: diff --git a/light-rest-4j/src/main/resources/templates/rest/openapi/pom.xml.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/openapi/pom.xml.rocker.raw index 8d359eb7f..44859752f 100644 --- a/light-rest-4j/src/main/resources/templates/rest/openapi/pom.xml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/openapi/pom.xml.rocker.raw @@ -1,12 +1,12 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) 4.0.0 - @config.get("groupId") - @config.get("artifactId") + @config.get("groupId").textValue() + @config.get("artifactId").textValue() jar - @config.get("name") - @config.get("version") + @config.get("name").textValue() + @config.get("version").textValue() 11 @@ -20,25 +20,25 @@ 4.12 2.1.3.Final 1.0.29 - @if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ + @if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ 3.1.0 } - @if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 11.2.0.3 } - @if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 8.0.16 } - @if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ 42.1.1 } - @if(config.toBoolean("supportH2ForTest")){ + @if(config.get("supportH2ForTest").booleanValue()){ 1.3.176 } - @if(config.toBoolean("supportAvro")){ + @if(config.get("supportAvro").booleanValue()){ 5.3.3 } - @if(config.toBoolean("kafkaProducer")||config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaProducer").booleanValue() ||config.get("kafkaConsumer").booleanValue()){ 2.5.0 } 2.4 @@ -119,7 +119,7 @@ metrics ${version.light-4j} - @if(config.toBoolean("prometheusMetrics")){ + @if(config.get("prometheusMetrics").booleanValue()){ com.networknt prometheus @@ -211,21 +211,21 @@ specification ${version.light-4j} - @if(config.toBoolean("kafkaProducer")||config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaProducer").booleanValue() ||config.get("kafkaConsumer").booleanValue()){ com.networknt kafka-common ${version.light-4j} } - @if(config.toBoolean("kafkaProducer")){ + @if(config.get("kafkaProducer").booleanValue()){ com.networknt kafka-producer ${version.light-4j} } - @if(config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaConsumer").booleanValue()){ com.networknt kafka-consumer @@ -262,28 +262,28 @@ undertow-core ${version.undertow} - @if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ + @if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ com.zaxxer HikariCP ${version.hikaricp} } - @if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ com.oracle ojdbc6 ${version.oracle} } - @if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ mysql mysql-connector-java ${version.mysql} } - @if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ org.postgresql postgresql @@ -297,7 +297,7 @@ ${version.junit} test - @if(config.toBoolean("supportH2ForTest")){ + @if(config.get("supportH2ForTest").booleanValue()){ com.h2database h2 @@ -305,7 +305,7 @@ test } - @if(config.toBoolean("supportAvro")){ + @if(config.get("supportAvro").booleanValue()){ io.confluent kafka-schema-registry-client @@ -412,7 +412,7 @@ - @if(config.toBoolean("supportAvro")){ + @if(config.get("supportAvro").booleanValue()){ confluent http://packages.confluent.io/maven/ diff --git a/light-rest-4j/src/main/resources/templates/rest/openapi/service.yml.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/openapi/service.yml.rocker.raw index db61d521b..a1695832e 100644 --- a/light-rest-4j/src/main/resources/templates/rest/openapi/service.yml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/openapi/service.yml.rocker.raw @@ -1,38 +1,38 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) # Singleton service factory configuration/IoC injection singletons: # StartupHookProvider implementations, there are one to many and they are called in the same sequence defined. - com.networknt.server.StartupHookProvider: - @if(config.toBoolean("kafkaProducer")){ + @if(config.get("kafkaProducer").booleanValue()){ # Kafka producer startup hook example # - net.lightapi.portal.user.command.UserCommandStartup } - @if(config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaConsumer").booleanValue()){ # Kafka consumer startup hook example # - net.lightapi.portal.user.query.UserQueryStartup } # ShutdownHookProvider implementations, there are one to many and they are called in the same sequence defined. - com.networknt.server.ShutdownHookProvider: - @if(config.toBoolean("kafkaProducer")){ + @if(config.get("kafkaProducer").booleanValue()){ # Kafka producer startup hook example # - net.lightapi.portal.user.command.UserCommandShutdown } - @if(config.toBoolean("kafkaConsumer")){ + @if(config.get("kafkaConsumer").booleanValue()){ # Kafka consumer startup hook example # - net.lightapi.portal.user.query.UserQueryShutdown } -@if(config.toBoolean("kafkaProducer")){ +@if(config.get("kafkaProducer").booleanValue()){ - com.networknt.kafka.producer.LightProducer: - com.networknt.kafka.producer.TransactionalProducer } -@if(config.toBoolean("kafkaConsumer")){ +@if(config.get("kafkaConsumer").booleanValue()){ - com.networknt.kafka.streams.LightStreams: # Kafka streams processor example # - net.lightapi.portal.user.query.UserQueryStreams } -@if(config.toBoolean("supportDb") ){ -@with (driverClassName = config.toString("dbInfo", "driverClassName"), jdbcUrl=config.toString("dbInfo", "jdbcUrl"), username=config.toString("dbInfo", "username"), password=config.toString("dbInfo", "password")) { +@if(config.get("supportDb").booleanValue()){ +@with (driverClassName = config.path("dbInfo").path("driverClassName").textValue(), jdbcUrl=config.path("dbInfo").path("jdbcUrl").textValue(), username=config.path("dbInfo").path("username").textValue(), password=config.path("dbInfo").path("password").textValue()) { - javax.sql.DataSource: - com.zaxxer.hikari.HikariDataSource: DriverClassName: @driverClassName diff --git a/light-rest-4j/src/main/resources/templates/rest/pojo.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/pojo.rocker.raw index 4cf08b8d5..a9b5e6665 100644 --- a/light-rest-4j/src/main/resources/templates/rest/pojo.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/pojo.rocker.raw @@ -1,9 +1,7 @@ -@import com.jsoniter.any.Any @import java.util.Map @import java.util.List @option discardLogicWhitespace=true - -@args (String modelPackage, String className, String parentClassName, String classVarName, boolean isAbstractClass, List> props, List> parentProps) +@args (String modelPackage, String className, String parentClassName, String classVarName, boolean isAbstractClass, List> props, List> parentProps) package @modelPackage; import java.util.Arrays; @@ -14,14 +12,14 @@ import com.fasterxml.jackson.annotation.JsonProperty; public@if(isAbstractClass){ abstract} class @className @if(parentClassName != null) {extends @parentClassName} { @for (prop: props) { - @if(prop.get("isEnum").toBoolean()) {@templates.rest.enumInline.template(prop)} else {private @prop.get("type") @prop.get("name");} + @if((Boolean)prop.get("isEnum")) {@templates.rest.enumInline.template(prop)} else {private @prop.get("type") @prop.get("name");} } public @className () { } @for (prop: props) { - @if(prop.get("isEnum").toBoolean()) { + @if((Boolean)prop.get("isEnum")) { @@JsonProperty("@prop.get("jsonProperty")") public @prop.get("nameWithEnum") @prop.get("getter")() { return @prop.get("name"); diff --git a/light-rest-4j/src/main/resources/templates/rest/project.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/project.rocker.raw index 3174b1e5c..b3f728e7e 100644 --- a/light-rest-4j/src/main/resources/templates/rest/project.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/project.rocker.raw @@ -1,9 +1,9 @@ @option discardLogicWhitespace=true -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) - @config.get("name") + @config.get("name").textValue() diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/buildGradleKts.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/buildGradleKts.rocker.raw index 6d5f6b518..3b9907529 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/buildGradleKts.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/buildGradleKts.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) plugins { application kotlin("jvm") version "1.3.21" @@ -62,19 +62,19 @@ dependencies { val kotlinLoggingVersion: String by project compile("io.github.microutils", "kotlin-logging", kotlinLoggingVersion) - @if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ + @if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ val hikaricpVersion: String by project compile("com.zaxxer", "HikariCP", hikaricpVersion) } - @if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ val oracleVersion: String by project compile("com.oracle", "ojdbc6", oracleVersion) } - @if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ val mysqlVersion: String by project compile("mysql", "mysql-connector-java", mysqlVersion) } - @if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ + @if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ val postgresVersion: String by project compile("org.postgresql", "postgresql", postgresVersion) } @@ -88,7 +88,7 @@ dependencies { // assertk val assertkVersion: String by project testCompile("com.willowtreeapps.assertk", "assertk-jvm", assertkVersion) - @if(config.toBoolean("supportH2ForTest")){ + @if(config.get("supportH2ForTest").booleanValue()){ val h2Version: String by project testCompile("com.h2database", "h2", h2Version) } diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/dockerfile.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/dockerfile.rocker.raw index 45dcd4afa..b808a36f0 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/dockerfile.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/dockerfile.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config, String expose) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config, String expose) FROM azul/zulu-openjdk-alpine:11 as packager RUN { \ @@ -28,5 +28,5 @@ ENV JAVA_MINIMAL=/opt/jre ENV PATH="$PATH:$JAVA_MINIMAL/bin" COPY --from=packager "$JAVA_MINIMAL" "$JAVA_MINIMAL" -@with (name = config.get("artifactId") + "-" + config.get("version") + ".jar") {COPY /target/@name server.jar} +@with (name = config.get("artifactId").textValue() + "-" + config.get("version").textValue() + ".jar") {COPY /target/@name server.jar} CMD ["/bin/sh","-c","java -Dlight-4j-config-dir=/config -Dlogback.configurationFile=/config/logback.xml -jar /server.jar"] diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/dockerfileslim.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/dockerfileslim.rocker.raw index abf212e19..a0509eb67 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/dockerfileslim.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/dockerfileslim.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config, String expose) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config, String expose) FROM openjdk:11.0.3-slim -@with (name = config.get("artifactId") + "-" + config.get("version") + ".jar") {ADD /target/@name server.jar} +@with (name = config.get("artifactId").textValue() + "-" + config.get("version").textValue() + ".jar") {ADD /target/@name server.jar} CMD ["/bin/sh","-c","java -Dlight-4j-config-dir=/config -Dlogback.configurationFile=/config/logback.xml -jar /server.jar"] diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/enumInline.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/enumInline.rocker.raw index 59e831aa7..62f2c6fd5 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/enumInline.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/enumInline.rocker.raw @@ -1,8 +1,7 @@ -@import com.jsoniter.any.Any @import java.util.Map @import java.util.List -@args (Map prop) - @with (v = prop.get("nameWithEnum") + ".values()", value = prop.get("value").asList()) { +@args (Map prop) + @with (v = prop.get("nameWithEnum") + ".values()", value = (List)prop.get("value")) { public enum @prop.get("nameWithEnum") { @for((i, item) : value) { @with(u = item.toString().toUpperCase().replaceAll("-", "_")) {@if (i.index() < value.size() - 1) {@u ("@item"),}@if(i.index() == value.size() - 1) {@u ("@item");}} diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/gradleProperties.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/gradleProperties.rocker.raw index 89434273c..90e3f8252 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/gradleProperties.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/gradleProperties.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) # Versions of Frequently used Libraries kafkaVersion=2.0.0 light4jVersion=2.0.24-SNAPSHOT @@ -10,18 +10,18 @@ jsonSchemaValidatorVersion=1.0.29 junitVersion=5.3.1 kotlinLoggingVersion=1.6.22 assertkVersion=0.13 -@if(config.toBoolean("supportDb") || config.toBoolean("supportH2ForTest")){ +@if(config.get("supportDb").booleanValue() || config.get("supportH2ForTest").booleanValue()){ hikaricpVersion=3.1.0 } -@if(config.toBoolean("supportDb") && "oracle".equalsIgnoreCase(config.toString("dbInfo", "name"))){ +@if(config.get("supportDb").booleanValue() && "oracle".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ oracleVersion=11.2.0.3 } -@if(config.toBoolean("supportDb") && "mysql".equalsIgnoreCase(config.toString("dbInfo", "name"))){ +@if(config.get("supportDb").booleanValue() && "mysql".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ mysqlVersion=8.0.16 } -@if(config.toBoolean("supportDb") && "postgres".equalsIgnoreCase(config.toString("dbInfo", "name"))){ +@if(config.get("supportDb").booleanValue() && "postgres".equalsIgnoreCase(config.path("dbInfo").path("name").textValue())){ postgresVersion=42.1.1 } -@if(config.toBoolean("supportH2ForTest")){ +@if(config.get("supportH2ForTest").booleanValue()){ h2Version=1.3.176 } diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/handlerTest.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/handlerTest.rocker.raw index fbdb2663b..f69af0c5c 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/handlerTest.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/handlerTest.rocker.raw @@ -1,5 +1,4 @@ @import java.util.Map -@import com.jsoniter.any.Any @import java.util.stream.Collectors @args (String handlerPackage, Map map) package @handlerPackage diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/model.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/model.rocker.raw index dbee02280..32d6d1245 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/model.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/model.rocker.raw @@ -1,5 +1,4 @@ -@import com.jsoniter.any.Any -@args (String modelPackage, String className, Any props) +@args (String modelPackage, String className) package @modelPackage; import java.io.Serializable; diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/openapi/handlerYml.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/openapi/handlerYml.rocker.raw index dae4be467..b947b8a06 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/openapi/handlerYml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/openapi/handlerYml.rocker.raw @@ -1,6 +1,5 @@ @import java.util.Map @import java.util.List -@import com.jsoniter.any.Any @args (String serviceId, String handlerPackage, List> items, boolean prometheusMetrics, boolean healthCheck, boolean serverInfo) # Handler middleware chain configuration diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/openapi/service.yml.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/openapi/service.yml.rocker.raw index 3e7157a99..4d20645c0 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/openapi/service.yml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/openapi/service.yml.rocker.raw @@ -1,5 +1,5 @@ -@import com.jsoniter.any.Any -@args (Any config) +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) # Singleton service factory configuration/IoC injection singletons: # StartupHookProvider implementations, there are one to many and they are called in the same sequence defined. @@ -11,8 +11,8 @@ singletons: # ShutdownHookProvider implementations, there are one to many and they are called in the same sequence defined. # - com.networknt.server.ShutdownHookProvider: # - com.networknt.server.Test1ShutdownHook -@if(config.toBoolean("supportDb") ){ -@with (driverClassName = config.toString("dbInfo", "driverClassName"), jdbcUrl=config.toString("dbInfo", "jdbcUrl"), username=config.toString("dbInfo", "username"), password=config.toString("dbInfo", "password")) { +@if(config.get("supportDb").booleanValue()){ +@with (driverClassName = config.path("dbInfo").path("driverClassName").textValue(), jdbcUrl=config.path("dbInfo").path("jdbcUrl").textValue(), username=config.path("dbInfo").path("username").textValue(), password=config.path("dbInfo").path("password").textValue()) { - javax.sql.DataSource: - com.zaxxer.hikari.HikariDataSource: DriverClassName: @driverClassName diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/pojo.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/pojo.rocker.raw index 38afae1ea..ac3cf6431 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/pojo.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/pojo.rocker.raw @@ -1,8 +1,7 @@ -@import com.jsoniter.any.Any @option discardLogicWhitespace=true @import java.util.Map @import java.util.List -@args (String modelPackage, String className, String classVarName, List> props) +@args (String modelPackage, String className, String classVarName, List> props) package @modelPackage; data class @className ( @for ((i, prop): props) { diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/settingsGradleKts.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/settingsGradleKts.rocker.raw index dcf063d85..626328f6e 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/settingsGradleKts.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/settingsGradleKts.rocker.raw @@ -1,3 +1,3 @@ -@import com.jsoniter.any.Any -@args (Any config) -rootProject.name = "@config.get("artifactId")" \ No newline at end of file +@import com.fasterxml.jackson.databind.JsonNode +@args (JsonNode config) +rootProject.name = "@config.get("artifactId").textValue()" \ No newline at end of file diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/ConfigTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/ConfigTest.java index 0a970d7bb..4d0a75476 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/ConfigTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/ConfigTest.java @@ -1,32 +1,39 @@ package com.networknt.codegen; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; +import com.fasterxml.jackson.databind.JsonNode; import org.junit.Assert; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import java.io.IOException; public class ConfigTest { public static String configName = "/config.json"; - public static Any anyConfig = null; + public static String configYamlName = "/config.yaml"; + public static JsonNode anyConfig = null; + public static JsonNode anyYamlConfig = null; @BeforeClass public static void setUp() throws IOException { // load config file - anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); + anyConfig = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); + anyYamlConfig = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configYamlName)); } @Test public void testDbName() { - Assert.assertEquals("mysql", anyConfig.toString("dbInfo", "name")); + Assert.assertEquals("mysql", anyConfig.path("dbInfo").path("name").textValue()); } @Test public void testDbSupport() { - Assert.assertTrue(anyConfig.toBoolean("supportDb")); + Assert.assertTrue(anyConfig.get("supportDb").booleanValue()); + } + + @Test + public void testUseSidecar() { + Assert.assertTrue(anyYamlConfig.get("useSidecar").booleanValue()); } - } diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiArrayReferenceGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiArrayReferenceGeneratorTest.java index 772181392..0664a4d27 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiArrayReferenceGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiArrayReferenceGeneratorTest.java @@ -5,8 +5,7 @@ import java.nio.file.Files; import java.nio.file.Paths; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; +import com.fasterxml.jackson.databind.JsonNode; import com.thoughtworks.qdox.JavaProjectBuilder; import com.thoughtworks.qdox.model.JavaClass; import com.thoughtworks.qdox.model.JavaField; @@ -51,11 +50,10 @@ static void delete(File f) throws IOException { } public static JavaPackage prepareJavaPackage(String targetPath, String packageName) throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(OpenApiArrayReferenceGeneratorTest.configName), 1024).readAny(); - Any anyModel = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(OpenApiArrayReferenceGeneratorTest.openapiJson), 1024).readAny(); - + JsonNode config = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(OpenApiArrayReferenceGeneratorTest.configName)); + JsonNode model = Generator.jsonMapper.readTree(OpenApiKotlinGeneratorTest.class.getResourceAsStream(OpenApiArrayReferenceGeneratorTest.openapiJson)); OpenApiGenerator generator = new OpenApiGenerator(); - generator.generate(targetPath, anyModel, anyConfig); + generator.generate(targetPath, model, config); File file = new File(targetPath); JavaProjectBuilder javaProjectBuilder = new JavaProjectBuilder(); diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiGeneratorTest.java index eff04062c..14d62a52a 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiGeneratorTest.java @@ -1,25 +1,24 @@ package com.networknt.codegen; import java.io.File; +import java.io.FilenameFilter; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; -import java.util.Scanner; +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.config.Config; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; import com.networknt.codegen.rest.OpenApiGenerator; -import com.thoughtworks.qdox.JavaProjectBuilder; -import com.thoughtworks.qdox.model.JavaClass; -import com.thoughtworks.qdox.model.JavaField; -import com.thoughtworks.qdox.model.JavaPackage; + +import static java.io.File.separator; +import static org.junit.Assert.assertTrue; /** * @author Steve Hu @@ -49,32 +48,31 @@ public static void tearDown() throws IOException { @Test public void testGeneratorJson() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - Any anyModel = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(openapiJson), 1024).readAny(); - + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiJson)); OpenApiGenerator generator = new OpenApiGenerator(); - generator.generate(targetPath, anyModel, anyConfig); + generator.generate(targetPath, modelNode, configNode); } @Test public void testGeneratorYaml() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - String strModel = new Scanner(OpenApiGeneratorTest.class.getResourceAsStream(openapiYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiYaml)); OpenApiGenerator generator = new OpenApiGenerator(); - generator.generate(targetPath, strModel, anyConfig); + generator.generate(targetPath, modelNode, configNode); } @Test public void testGeneratorAccountInfo() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - String strModel = new Scanner(OpenApiGeneratorTest.class.getResourceAsStream(accountInfoYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(accountInfoYaml)); OpenApiGenerator generator = new OpenApiGenerator(); - generator.generate(targetPath, strModel, anyConfig); + generator.generate(targetPath, modelNode, configNode); } @Test public void testGetOperationList() throws IOException { - Any anyModel = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(openapiJson), 1024).readAny(); + JsonNode anyModel = Config.getInstance().getMapper().readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiJson)); OpenApiGenerator generator = new OpenApiGenerator(); List list = generator.getOperationList(anyModel); System.out.println(list); @@ -95,10 +93,10 @@ public void testGetConfigSchema() throws IOException { } @Test public void testNoServersGeneratorYaml() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - String strModel = new Scanner(OpenApiGeneratorTest.class.getResourceAsStream(openapiNoServersYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiNoServersYaml)); OpenApiGenerator generator = new OpenApiGenerator(); - generator.generate(targetPath, strModel, anyConfig); + generator.generate(targetPath, modelNode, configNode); } @Test @@ -113,27 +111,27 @@ public void testConvertInvalidVariableName() { @Test public void testGeneratorYamlEnum() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - String strModel = new Scanner(OpenApiGeneratorTest.class.getResourceAsStream(openapiEnumYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiEnumYaml)); OpenApiGenerator generator = new OpenApiGenerator(); - generator.generate(targetPath, strModel, anyConfig); + generator.generate(targetPath, modelNode, configNode); } @Test @Ignore public void testGeneratorYamlError() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - String strModel = new Scanner(OpenApiGeneratorTest.class.getResourceAsStream(openapiErrorYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiErrorYaml)); OpenApiGenerator generator = new OpenApiGenerator(); - generator.generate(targetPath, strModel, anyConfig); + generator.generate(targetPath, modelNode, configNode); } @Test public void testGeneratorKafka() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configKafkaName), 1024).readAny(); - String strModel = new Scanner(OpenApiGeneratorTest.class.getResourceAsStream(openapiYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configKafkaName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiYaml)); OpenApiGenerator generator = new OpenApiGenerator(); - generator.generate(targetPath, strModel, anyConfig); + generator.generate(targetPath, modelNode, configNode); } @Test diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java index 26ceb799a..efe6129e8 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java @@ -1,13 +1,7 @@ package com.networknt.codegen; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; -import com.networknt.codegen.rest.OpenApiGenerator; +import com.fasterxml.jackson.databind.JsonNode; import com.networknt.codegen.rest.OpenApiKotlinGenerator; -import com.thoughtworks.qdox.JavaProjectBuilder; -import com.thoughtworks.qdox.model.JavaClass; -import com.thoughtworks.qdox.model.JavaField; -import com.thoughtworks.qdox.model.JavaPackage; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -40,24 +34,23 @@ public static void tearDown() throws IOException { @Test public void testGeneratorJson() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiKotlinGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - Any anyModel = JsonIterator.parse(OpenApiKotlinGeneratorTest.class.getResourceAsStream(openapiJson), 1024).readAny(); - + JsonNode config = Generator.jsonMapper.readTree(OpenApiKotlinGeneratorTest.class.getResourceAsStream(configName)); + JsonNode model = Generator.yamlMapper.readTree(OpenApiKotlinGeneratorTest.class.getResourceAsStream(openapiYaml)); OpenApiKotlinGenerator generator = new OpenApiKotlinGenerator(); - generator.generate(targetPath, anyModel, anyConfig); + generator.generate(targetPath, model, config); } @Test public void testGeneratorYaml() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiKotlinGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - String strModel = new Scanner(OpenApiKotlinGeneratorTest.class.getResourceAsStream(openapiYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode config = Generator.jsonMapper.readTree(OpenApiKotlinGeneratorTest.class.getResourceAsStream(configName)); + JsonNode model = Generator.yamlMapper.readTree(OpenApiKotlinGeneratorTest.class.getResourceAsStream(openapiYaml)); OpenApiKotlinGenerator generator = new OpenApiKotlinGenerator(); - generator.generate(targetPath, strModel, anyConfig); + generator.generate(targetPath, model, config); } @Test public void testGetOperationList() throws IOException { - Any anyModel = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(openapiJson), 1024).readAny(); + String anyModel = new Scanner(OpenApiKotlinGeneratorTest.class.getResourceAsStream(openapiYaml), "UTF-8").useDelimiter("\\A").next(); OpenApiKotlinGenerator generator = new OpenApiKotlinGenerator(); List list = generator.getOperationList(anyModel); System.out.println(list); @@ -78,9 +71,9 @@ public void testGetConfigSchema() throws IOException { } @Test public void testNoServersGeneratorYaml() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiKotlinGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - String strModel = new Scanner(OpenApiKotlinGeneratorTest.class.getResourceAsStream(openapiNoServersYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode config = Generator.jsonMapper.readTree(OpenApiKotlinGeneratorTest.class.getResourceAsStream(configName)); + JsonNode model = Generator.yamlMapper.readTree(OpenApiKotlinGeneratorTest.class.getResourceAsStream(openapiNoServersYaml)); OpenApiKotlinGenerator generator = new OpenApiKotlinGenerator(); - generator.generate(targetPath, strModel, anyConfig); + generator.generate(targetPath, model, config); } } diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLambdaGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLambdaGeneratorTest.java index 1797fc402..50238871d 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLambdaGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLambdaGeneratorTest.java @@ -1,7 +1,6 @@ package com.networknt.codegen; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; +import com.fasterxml.jackson.databind.JsonNode; import com.networknt.codegen.rest.OpenApiLambdaGenerator; import org.junit.Assert; import org.junit.BeforeClass; @@ -12,7 +11,6 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; -import java.util.Scanner; public class OpenApiLambdaGeneratorTest { public static String targetPath = "/tmp/openapilambda"; @@ -37,41 +35,41 @@ public static void tearDown() throws IOException { @Test public void testGeneratorJson() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiLambdaGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - Any anyModel = JsonIterator.parse(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiJson), 1024).readAny(); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(configName)); + JsonNode model = Generator.jsonMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiJson)); OpenApiLambdaGenerator generator = new OpenApiLambdaGenerator(); - generator.generate(targetPath, anyModel, anyConfig); + generator.generate(targetPath, model, config); } @Test public void testGeneratorYaml() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiLambdaGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); - String strModel = new Scanner(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(configName)); + JsonNode model = Generator.yamlMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiYaml)); OpenApiLambdaGenerator generator = new OpenApiLambdaGenerator(); - generator.generate(targetPath, strModel, anyConfig); + generator.generate(targetPath, model, config); } @Test public void testProxyGeneratorYaml() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiLambdaGeneratorTest.class.getResourceAsStream(configProxyLambda), 1024).readAny(); - String strModel = new Scanner(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(configProxyLambda)); + JsonNode model = Generator.yamlMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiYaml)); OpenApiLambdaGenerator generator = new OpenApiLambdaGenerator(); - generator.generate(proxyTargetPath, strModel, anyConfig); + generator.generate(proxyTargetPath, model, config); } @Test public void testProxyMavenGeneratorYaml() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiLambdaGeneratorTest.class.getResourceAsStream(configProxyMaven), 1024).readAny(); - String strModel = new Scanner(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(configProxyMaven)); + JsonNode model = Generator.yamlMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiYaml)); OpenApiLambdaGenerator generator = new OpenApiLambdaGenerator(); - generator.generate(proxyMavenTargetPath, strModel, anyConfig); + generator.generate(proxyMavenTargetPath, model, config); } @Test public void testGetOperationList() throws IOException { - Any anyModel = JsonIterator.parse(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiJson), 1024).readAny(); + JsonNode model = Generator.jsonMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiJson)); OpenApiLambdaGenerator generator = new OpenApiLambdaGenerator(); - List list = generator.getOperationList(anyModel); + List list = generator.getOperationList(model); System.out.println(list); } diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/rest/OpenApiSpecGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/rest/OpenApiSpecGeneratorTest.java index f6a1bc231..10002f96d 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/rest/OpenApiSpecGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/rest/OpenApiSpecGeneratorTest.java @@ -2,11 +2,11 @@ import java.io.IOException; +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.codegen.Generator; import org.junit.Ignore; import org.junit.Test; -import com.jsoniter.JsonIterator; -import com.jsoniter.any.Any; import com.networknt.codegen.OpenApiGeneratorTest; @Ignore @@ -16,10 +16,10 @@ public class OpenApiSpecGeneratorTest { @Test public void test() throws IOException { - Any anyConfig = JsonIterator.parse(OpenApiGeneratorTest.class.getResourceAsStream(configName), 1024).readAny(); + JsonNode config = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); OpenApiSpecGenerator generator = new OpenApiSpecGenerator(); - generator.generate(outputDir, null, anyConfig); + generator.generate(outputDir, null, config); } } diff --git a/light-rest-4j/src/test/resources/config.json b/light-rest-4j/src/test/resources/config.json index adebec376..16be9b55e 100644 --- a/light-rest-4j/src/test/resources/config.json +++ b/light-rest-4j/src/test/resources/config.json @@ -13,6 +13,7 @@ "enableHttp": true, "httpsPort": 8443, "enableHttps": false, + "enableHttp2": false, "enableRegistry": false, "supportDb": true, "dbInfo": { diff --git a/light-rest-4j/src/test/resources/config.yaml b/light-rest-4j/src/test/resources/config.yaml new file mode 100644 index 000000000..9d3be7da7 --- /dev/null +++ b/light-rest-4j/src/test/resources/config.yaml @@ -0,0 +1,26 @@ +--- +# A light-codegen config file to generate the project used with light-proxy sidecar. +# It is the same as the original petstore config.json but with useSidecar set to true. +# This will make sure that the API doesn't wire in some of the middleware handlers as +# they are used in the light-proxy to address these cross-cutting concerns. +name: petstore +version: 3.0.1 +groupId: com.networknt +artifactId: petstore +rootPackage: com.networknt.petstore +handlerPackage: com.networknt.petstore.handler +modelPackage: com.networknt.petstore.model +overwriteHandler: true +overwriteHandlerTest: true +overwriteModel: true +useSidecar: true +httpPort: 8080 +enableHttp: false +httpsPort: 8443 +enableHttps: true +enableHttp2: true +enableRegistry: false +supportDb: false +supportH2ForTest: false +supportClient: true +dockerOrganization: networknt diff --git a/pom.xml b/pom.xml index b01644ab3..8f9bab828 100644 --- a/pom.xml +++ b/pom.xml @@ -67,8 +67,7 @@ 11 UTF-8 2.0.24-SNAPSHOT - 0.9.15 - 3.21.0-GA + 2.11.1 1.7.25 0.6.3 4.7 @@ -79,16 +78,10 @@ 1.2.3 4.13.1 2.1.3.Final - 1.0.34 1.3 3.8.0 2.1.10 - 3.0.2 - 1.0.29 - 1.2.4 - 3.5.3-beta - 0.3 - 4.0.0 + 1.0.47 1.26 1.2.1 1.72 @@ -96,7 +89,6 @@ 2.0-M9 2.4 4.8.22 - 2.0.7 1.0.0 3.1.0 3.2.0 @@ -236,16 +228,15 @@ openapi-parser ${version.light-4j} - - com.jsoniter - jsoniter - ${version.jsoniter} + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${version.jackson} - org.javassist - javassist - ${version.javassist} + com.fasterxml.jackson.core + jackson-databind + ${version.jackson} org.slf4j @@ -317,11 +308,6 @@ classgraph ${versions.classgraph} - - io.swagger.core.v3 - swagger-core - ${version.swagger} - From f0d80597b9ec5f31bd2a0acf83cfa7048aabea08 Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Tue, 16 Feb 2021 12:47:32 -0500 Subject: [PATCH 2/7] fixes #528 change OpenApiGenerator to interface for default methods --- .../src/main/resources/config/service.yml | 2 +- .../codegen/graphql/GraphqlGenerator.java | 164 ++++------- .../templates/graphql/handlerYml.rocker.raw | 10 +- .../codegen/rest/OpenApiGenerator.java | 271 ++---------------- .../codegen/rest/OpenApiLightGenerator.java | 254 ++++++++++++++++ .../codegen/rest/OpenApiSpringGenerator.java | 4 + .../com/networknt/codegen/ConfigTest.java | 5 +- .../OpenApiArrayReferenceGeneratorTest.java | 5 +- ...st.java => OpenApiLightGeneratorTest.java} | 59 ++-- .../rest/OpenApiSpecGeneratorTest.java | 4 +- light-rest-4j/src/test/resources/config.json | 1 + 11 files changed, 388 insertions(+), 391 deletions(-) create mode 100644 light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java create mode 100644 light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpringGenerator.java rename light-rest-4j/src/test/java/com/networknt/codegen/{OpenApiGeneratorTest.java => OpenApiLightGeneratorTest.java} (69%) diff --git a/codegen-web/src/main/resources/config/service.yml b/codegen-web/src/main/resources/config/service.yml index 047b871c7..f7b24684e 100644 --- a/codegen-web/src/main/resources/config/service.yml +++ b/codegen-web/src/main/resources/config/service.yml @@ -14,7 +14,7 @@ singletons: # Generator interface implementations - com.networknt.codegen.Generator: - - com.networknt.codegen.rest.OpenApiGenerator + - com.networknt.codegen.rest.OpenApiLightGenerator - com.networknt.codegen.hybrid.HybridServerGenerator - com.networknt.codegen.hybrid.HybridServiceGenerator - com.networknt.codegen.graphql.GraphqlGenerator diff --git a/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java b/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java index 19306fb53..ca9acac88 100644 --- a/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java +++ b/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java @@ -25,109 +25,34 @@ public String getFramework() { @Override public void generate(String targetPath, Object schema, JsonNode config) throws IOException { - // whoever is calling this needs to make sure that model is converted to Map - JsonNode jsonNode = config.get("schemaPackage"); - if(jsonNode == null) throw new ConfigException("schemaPackage is missing in config"); - String schemaPackage = jsonNode.textValue(); - - jsonNode = config.get("schemaClass"); - if(jsonNode == null) throw new ConfigException("schemaClass is missing in config"); - String schemaClass = jsonNode.textValue(); - - jsonNode = config.get("overwriteSchemaClass"); - if(jsonNode == null) throw new ConfigException("overwriteSchemaClass is missing in config"); - boolean overwriteSchemaClass = jsonNode.booleanValue(); - - boolean enableHttp = false; - jsonNode = config.get("enableHttp"); - if(jsonNode == null) { - ((ObjectNode)config).put("enableHttp", false); - } else { - enableHttp = jsonNode.booleanValue(); - } - - String httpPort = "8080"; - jsonNode = config.get("httpPort"); - if(jsonNode == null) { - ((ObjectNode)config).put("httpPort", httpPort); - } else { - httpPort = jsonNode.asText(); - } - - boolean enableHttps = true; - jsonNode = config.get("enableHttps"); - if(jsonNode == null) { - ((ObjectNode)config).put("enableHttps", true); - } else { - enableHttps = jsonNode.booleanValue(); - } - - String httpsPort = "8443"; - jsonNode = config.get("httpsPort"); - if(jsonNode == null) { - ((ObjectNode)config).put("httpsPort", httpsPort); - } else { - httpsPort = jsonNode.asText(); - } - - boolean enableHttp2 = true; - jsonNode = config.get("enableHttp2"); - if(jsonNode == null) { - ((ObjectNode)config).put("enableHttp2", enableHttp2); - } else { - enableHttp2 = jsonNode.booleanValue(); - } - - boolean enableRegistry = false; - jsonNode = config.get("enableRegistry"); - if(jsonNode == null) { - ((ObjectNode)config).put("enableRegistry", enableRegistry); - } else { - enableRegistry = jsonNode.booleanValue(); - } - - boolean eclipseIDE = false; - jsonNode = config.get("eclipseIDE"); - if(jsonNode == null) { - ((ObjectNode)config).put("eclipseIDE", eclipseIDE); - } else { - eclipseIDE = jsonNode.booleanValue(); - } - - jsonNode = config.get("supportClient"); - if(jsonNode == null) throw new ConfigException("supportClient is missing in config"); - boolean supportClient = jsonNode.booleanValue(); - - boolean prometheusMetrics = false; - jsonNode = config.get("prometheusMetrics"); - if(jsonNode != null) prometheusMetrics = jsonNode.booleanValue(); - - boolean skipHealthCheck = false; - jsonNode = config.get("skipHealthCheck"); - if(jsonNode != null) skipHealthCheck = jsonNode.booleanValue(); - - - boolean skipServerInfo = false; - jsonNode = config.get("skipServerInfo"); - if(jsonNode != null) skipServerInfo = jsonNode.booleanValue(); - - String dockerOrganization = "networknt"; - jsonNode = config.get("dockerOrganization"); - if(jsonNode != null) dockerOrganization = jsonNode.textValue(); - - jsonNode = config.get("version"); - if(jsonNode == null) throw new ConfigException("version is missing in config"); - String version = jsonNode.textValue(); - - jsonNode = config.get("groupId"); - if(jsonNode == null) throw new ConfigException("groupId is missing in config"); - String groupId = jsonNode.textValue(); - - jsonNode = config.get("artifactId"); - if(jsonNode == null) throw new ConfigException("artifactId is missing in config"); - String artifactId = jsonNode.textValue(); - + // GraphQL specific config + String schemaPackage = getSchemaPackage(config, null); + String schemaClass = getSchemaClass(config, null); + boolean overwriteSchemaClass = isOverwriteSchemaClass(config, null); + + // Generic config + boolean enableHttp = isEnableHttp(config, null); + String httpPort = getHttpPort(config, null); + boolean enableHttps = isEnableHttps(config, null); + String httpsPort = getHttpsPort(config, null); + boolean enableHttp2 = isEnableHttp2(config, null); + boolean enableRegistry = isEnableRegistry(config, null); + boolean eclipseIDE = isEclipseIDE(config, null); + boolean supportClient = isSupportClient(config, null); + boolean prometheusMetrics = isPrometheusMetrics(config, null); + String dockerOrganization = getDockerOrganization(config, null); + String version = getVersion(config, null); + String groupId = getGroupId(config, null); + String artifactId = getArtifactId(config, null); String serviceId = groupId + "." + artifactId + "-" + version; + boolean specChangeCodeReGenOnly = isSpecChangeCodeReGenOnly(config, null); + boolean enableParamDescription = isEnableParamDescription(config, null); + boolean skipPomFile = isSkipPomFile(config, null); + boolean kafkaProducer = isKafkaProducer(config, null); + boolean kafkaConsumer = isKafkaConsumer(config, null); + boolean supportAvro = isSupportAvro(config, null); + String kafkaTopic = getKafkaTopic(config, null); + String decryptOption = getDecryptOption(config, null); transfer(targetPath, "", "pom.xml", templates.graphql.pom.template(config)); transferMaven(targetPath); @@ -176,7 +101,7 @@ public void generate(String targetPath, Object schema, JsonNode config) throws I transfer(targetPath, ("src.main.resources").replace(".", separator), "logback.xml", templates.graphql.logback.template()); transfer(targetPath, ("src.test.resources").replace(".", separator), "logback-test.xml", templates.graphql.logback.template()); // handler.yml - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "handler.yml", templates.graphql.handlerYml.template(serviceId, prometheusMetrics, !skipHealthCheck, !skipServerInfo)); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "handler.yml", templates.graphql.handlerYml.template(serviceId, prometheusMetrics)); // Copy schema // The generator support both manually coded schema or schema defined in IDL. If schema.graphqls exists @@ -220,4 +145,37 @@ public void generate(String targetPath, Object schema, JsonNode config) throws I } } + + private String getSchemaPackage(JsonNode config, String defaultValue) { + String schemaPackage = defaultValue == null ? "com.networknt.graphql.schema" : defaultValue; + JsonNode jsonNode = config.get("schemaPackage"); + if(jsonNode == null) { + ((ObjectNode)config).put("schemaPackage", schemaPackage); + } else { + schemaPackage = jsonNode.asText(); + } + return schemaPackage; + } + + private String getSchemaClass(JsonNode config, String defaultValue) { + String schemaClass = defaultValue == null ? "GraphQlSchema" : defaultValue; + JsonNode jsonNode = config.get("schemaClass"); + if(jsonNode == null) { + ((ObjectNode)config).put("schemaClass", schemaClass); + } else { + schemaClass = jsonNode.asText(); + } + return schemaClass; + } + + private boolean isOverwriteSchemaClass(JsonNode config, Boolean defaultValue) { + boolean overwriteSchemaClass = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("overwriteSchemaClass"); + if(jsonNode == null) { + ((ObjectNode)config).put("overwriteSchemaClass", overwriteSchemaClass); + } else { + overwriteSchemaClass = jsonNode.booleanValue(); + } + return overwriteSchemaClass; + } } diff --git a/light-graphql-4j/src/main/resources/templates/graphql/handlerYml.rocker.raw b/light-graphql-4j/src/main/resources/templates/graphql/handlerYml.rocker.raw index d4928b65b..1229727f4 100644 --- a/light-graphql-4j/src/main/resources/templates/graphql/handlerYml.rocker.raw +++ b/light-graphql-4j/src/main/resources/templates/graphql/handlerYml.rocker.raw @@ -1,6 +1,6 @@ @import java.util.Map @import java.util.List -@args (String serviceId, boolean prometheusMetrics, boolean healthCheck, boolean serverInfo) +@args (String serviceId, boolean prometheusMetrics) # Handler middleware chain configuration --- @@ -76,16 +76,16 @@ paths: - default - options -@if(healthCheck){ - path: '/health/${server.serviceId:@serviceId}' + - path: '/health/${server.serviceId:@serviceId}' method: 'get' exec: - health -} -@if(serverInfo){ - path: '/server/info' + + - path: '/server/info' method: 'get' exec: - info -} + @if(prometheusMetrics){ - path: '/prometheus' method: 'get' exec: diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java index 5da3a0b50..2a0bd5357 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java @@ -27,12 +27,11 @@ import static java.io.File.separator; -public class OpenApiGenerator implements Generator { - private Map typeMapping = new HashMap<>(); +public interface OpenApiGenerator extends Generator { + Map typeMapping = initTypeMapping(); - boolean enableParamDescription = false; - - public OpenApiGenerator() { + static Map initTypeMapping() { + Map typeMapping = new HashMap<>(); typeMapping.put("array", "java.util.List"); typeMapping.put("map", "java.util.Map"); typeMapping.put("List", "java.util.List"); @@ -48,226 +47,7 @@ public OpenApiGenerator() { typeMapping.put("double", "Double"); typeMapping.put("object", "Object"); typeMapping.put("integer", "Integer"); - } - - @Override - public String getFramework() { - return "openapi"; - } - - /** - * - * @param targetPath The output directory of the generated project - * @param model The optional model data that trigger the generation, i.e. swagger specification, graphql IDL etc. - * @param config A json object that controls how the generator behaves. - * - * @throws IOException IO Exception occurs during code generation - */ - @Override - public void generate(final String targetPath, Object model, JsonNode config) throws IOException { - // whoever is calling this needs to make sure that model is converted to Map - String rootPackage = getRootPackage(config, null); - String modelPackage = getModelPackage(config, null); - String handlerPackage = getHandlerPackage(config, null); - boolean overwriteHandler = isOverwriteHandler(config, null); - boolean overwriteHandlerTest = isOverwriteHandlerTest(config, null); - boolean overwriteModel = isOverwriteModel(config, null); - boolean generateModelOnly = isGenerateModelOnly(config, null); - boolean enableHttp = isEnableHttp(config, null); - String httpPort = getHttpPort(config, null); - boolean enableHttps = isEnableHttps(config, null); - String httpsPort = getHttpsPort(config, null); - boolean enableHttp2 = isEnableHttp2(config, null); - boolean enableRegistry = isEnableRegistry(config, null); - boolean eclipseIDE = isEclipseIDE(config, null); - boolean supportClient = isSupportClient(config, null); - boolean prometheusMetrics = isPrometheusMetrics(config, null); - String dockerOrganization = getDockerOrganization(config, null); - String version = getVersion(config, null); - String groupId = getGroupId(config, null); - String artifactId = getArtifactId(config, null); - String serviceId = groupId + "." + artifactId + "-" + version; - boolean specChangeCodeReGenOnly = isSpecChangeCodeReGenOnly(config, null); - boolean enableParamDescription = isEnableParamDescription(config, null); - boolean skipPomFile = isSkipPomFile(config, null); - boolean kafkaProducer = isKafkaProducer(config, null); - boolean kafkaConsumer = isKafkaConsumer(config, null); - boolean supportAvro = isSupportAvro(config, null); - String kafkaTopic = getKafkaTopic(config, null); - String decryptOption = getDecryptOption(config, null); - - // get the list of operations for this model - List> operationList = getOperationList(model); - - // bypass project generation if the mode is the only one requested to be built - if (!generateModelOnly) { - // if set to true, regenerate the code only (handlers, model and the handler.yml, potentially affected by operation changes - if (!specChangeCodeReGenOnly) { - // generate configurations, project, masks, certs, etc - if (!skipPomFile) { - transfer(targetPath, "", "pom.xml", templates.rest.openapi.pom.template(config)); - } - - - transferMaven(targetPath); - // There is only one port that should be exposed in Dockerfile, otherwise, the service - // discovery will be so confused. If https is enabled, expose the https port. Otherwise http port. - String expose = ""; - if (enableHttps) { - expose = httpsPort; - } else { - expose = httpPort; - } - - transfer(targetPath, "docker", "Dockerfile", templates.rest.dockerfile.template(config, expose)); - transfer(targetPath, "docker", "Dockerfile-Slim", templates.rest.dockerfileslim.template(config, expose)); - transfer(targetPath, "", "build.sh", templates.rest.buildSh.template(dockerOrganization, serviceId)); - transfer(targetPath, "", "kubernetes.yml", templates.rest.kubernetes.template(dockerOrganization, serviceId, config.get("artifactId").textValue(), expose, version)); - transfer(targetPath, "", ".gitignore", templates.rest.gitignore.template()); - transfer(targetPath, "", "README.md", templates.rest.README.template()); - transfer(targetPath, "", "LICENSE", templates.rest.LICENSE.template()); - if(eclipseIDE) { - transfer(targetPath, "", ".classpath", templates.rest.classpath.template()); - transfer(targetPath, "", ".project", templates.rest.project.template(config)); - } - // config - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "service.yml", templates.rest.openapi.service.template(config)); - - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "server.yml", - templates.rest.server.template(serviceId, enableHttp, httpPort, enableHttps, httpsPort, enableHttp2, enableRegistry, version)); - transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", - templates.rest.server.template(serviceId, enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); - - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "openapi-security.yml", templates.rest.openapiSecurity.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "openapi-validator.yml", templates.rest.openapiValidator.template()); - if (supportClient) { - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "client.yml", templates.rest.clientYml.template()); - } else { - transfer(targetPath, ("src.test.resources.config").replace(".", separator), "client.yml", templates.rest.clientYml.template()); - } - - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "primary.crt", templates.rest.primaryCrt.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "secondary.crt", templates.rest.secondaryCrt.template()); - if(kafkaProducer) { - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "kafka-producer.yml", templates.rest.kafkaProducerYml.template(kafkaTopic)); - } - if(kafkaConsumer) { - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "kafka-streams.yml", templates.rest.kafkaStreamsYml.template(artifactId)); - } - if(supportAvro) { - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "schema-registry.yml", templates.rest.schemaRegistryYml.template()); - } - - // mask - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "mask.yml", templates.rest.maskYml.template()); - // logging - transfer(targetPath, ("src.main.resources").replace(".", separator), "logback.xml", templates.rest.logback.template(rootPackage)); - transfer(targetPath, ("src.test.resources").replace(".", separator), "logback-test.xml", templates.rest.logback.template(rootPackage)); - - // exclusion list for Config module - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "config.yml", templates.rest.openapi.config.template(config)); - - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "audit.yml", templates.rest.auditYml.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "body.yml", templates.rest.bodyYml.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "info.yml", templates.rest.infoYml.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "correlation.yml", templates.rest.correlationYml.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "metrics.yml", templates.rest.metricsYml.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "sanitizer.yml", templates.rest.sanitizerYml.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "traceability.yml", templates.rest.traceabilityYml.template()); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "health.yml", templates.rest.healthYml.template()); - // added with #471 - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "app-status.yml", templates.rest.appStatusYml.template()); - // values.yml file, transfer to suppress the warning message during start startup and encourage usage. - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "values.yml", templates.rest.openapi.values.template()); - } - // routing handler - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "handler.yml", - templates.rest.openapi.handlerYml.template(serviceId, handlerPackage, operationList, prometheusMetrics)); - - } - - // model - OpenApi3 openApi3 = null; - try { - openApi3 = (OpenApi3)new OpenApiParser().parse((JsonNode)model, new URL("https://oas.lightapi.net/")); - } catch (MalformedURLException e) { - throw new RuntimeException("Failed to parse the model", e); - } - Map specMap = JsonMapper.string2Map(Overlay.toJson((OpenApi3Impl)openApi3).toString()); - Map components = (Map)specMap.get("components"); - if(components != null) { - Map schemas = (Map)components.get("schemas"); - if(schemas != null) { - ArrayList modelCreators = new ArrayList<>(); - final HashMap references = new HashMap<>(); - for (Map.Entry entry : schemas.entrySet()) { - loadModel(entry.getKey(), null, (Map)entry.getValue(), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, null); - } - - for (Runnable r : modelCreators) { - r.run(); - } - } - } - - // exit after generating the model if the consumer needs only the model classes - if (generateModelOnly) { - return; - } - - // handler - for (Map op : operationList) { - String className = op.get("handlerName").toString(); - @SuppressWarnings("unchecked") - List parameters = (List)op.get("parameters"); - Map responseExample = (Map)op.get("responseExample"); - String example = responseExample.get("example"); - String statusCode = responseExample.get("statusCode"); - statusCode = StringUtils.isBlank(statusCode) || statusCode.equals("default") ? "-1" : statusCode; - if (checkExist(targetPath, ("src.main.java." + handlerPackage).replace(".", separator), className + ".java") && !overwriteHandler) { - continue; - } - transfer(targetPath, ("src.main.java." + handlerPackage).replace(".", separator), className + ".java", templates.rest.handler.template(handlerPackage, className, statusCode, example, parameters)); - } - - // handler test cases - if (!specChangeCodeReGenOnly) { - transfer(targetPath, ("src.test.java." + handlerPackage + ".").replace(".", separator), "TestServer.java", templates.rest.testServer.template(handlerPackage)); - } - - for (Map op : operationList) { - if (checkExist(targetPath, ("src.test.java." + handlerPackage).replace(".", separator), op.get("handlerName") + "Test.java") && !overwriteHandlerTest) { - continue; - } - transfer(targetPath, ("src.test.java." + handlerPackage).replace(".", separator), op.get("handlerName") + "Test.java", templates.rest.openapi.handlerTest.template(handlerPackage, op)); - } - - // transfer binary files without touching them. - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.keystore")) { - Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.keystore")); - } - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.truststore")) { - Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.truststore")); - } - if (supportClient) { - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { - Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.keystore")); - } - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { - Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.truststore")); - } - } else { - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { - Generator.copyFile(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.keystore")); - } - try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { - Generator.copyFile(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.truststore")); - } - } - - try (InputStream is = new ByteArrayInputStream(Generator.yamlMapper.writeValueAsBytes(model))) { - Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "openapi.yaml")); - } + return typeMapping; } /** @@ -276,7 +56,7 @@ public void generate(final String targetPath, Object model, JsonNode config) thr * @param entry The entry for which to generate * @param propMap The property map to add to, created in the caller */ - private void initializePropertyMap(Map.Entry entry, Map propMap) { + default void initializePropertyMap(Map.Entry entry, Map propMap) { String name = convertToValidJavaVariableName(entry.getKey()); propMap.put("jsonProperty", name); if (name.startsWith("@")) { @@ -297,7 +77,7 @@ private void initializePropertyMap(Map.Entry entry, Map> props, Map.Entry entrySchema) { - private void handleProperties(List> props, Map properties) { + default void handleProperties(List> props, Map properties) { // transform properties for (Map.Entry entryProp : properties.entrySet()) { //System.out.println("key = " + entryProp.getKey() + " value = " + entryProp.getValue()); @@ -441,10 +221,11 @@ private void handleProperties(List> props, Map", s); } @@ -452,7 +233,7 @@ private String getListOf(String s) { // 1. replace invalid character with '_' // 2. prefix number with '_' // 3. convert the first character of java keywords to upper case - public static String convertToValidJavaVariableName(String string) { + static String convertToValidJavaVariableName(String string) { if (string == null || string.equals("") || SourceVersion.isName(string)) { return string; } @@ -475,18 +256,18 @@ public static String convertToValidJavaVariableName(String string) { return stringBuilder.toString(); } - private boolean isEnumHasDescription(String string) { + default boolean isEnumHasDescription(String string) { return string.contains(":") || string.contains("{") || string.contains("("); } - private String getEnumName(String string) { + default String getEnumName(String string) { if (string.contains(":")) return string.substring(0, string.indexOf(":")).trim(); if (string.contains("(") && string.contains(")")) return string.substring(0, string.indexOf("(")).trim(); if (string.contains("{") && string.contains("}")) return string.substring(0, string.indexOf("{")).trim(); return string; } - private String getEnumDescription(String string) { + default String getEnumDescription(String string) { if (string.contains(":")) return string.substring(string.indexOf(":") + 1).trim(); if (string.contains("(") && string.contains(")")) return string.substring(string.indexOf("(") + 1, string.indexOf(")")).trim(); if (string.contains("{") && string.contains("}")) return string.substring(string.indexOf("{") + 1, string.indexOf("}")).trim(); @@ -494,7 +275,7 @@ private String getEnumDescription(String string) { return string; } - public List> getOperationList(Object model) { + default List> getOperationList(Object model, JsonNode config) { List> result = new ArrayList<>(); OpenApi3 openApi3 = null; try { @@ -532,7 +313,7 @@ public List> getOperationList(Object model) { flattened.put("requestBodyExample", populateRequestBodyExample(operation)); Map responseExample = populateResponseExample(operation); flattened.put("responseExample", responseExample); - if (enableParamDescription) { + if (config.get("enableParamDescription").booleanValue()) { //get parameters info and put into result List parameterRawList = operation.getParameters(); List parametersResultList = new LinkedList<>(); @@ -563,7 +344,7 @@ public List> getOperationList(Object model) { return result; } - private static String getBasePath(OpenApi3 openApi3) { + static String getBasePath(OpenApi3 openApi3) { String basePath = ""; String url = null; if (openApi3.getServers().size() > 0) { @@ -582,7 +363,7 @@ private static String getBasePath(OpenApi3 openApi3) { } // method used to generate valid enum keys for enum contents - private Object getValidEnumName(Map.Entry entryElement) { + default Object getValidEnumName(Map.Entry entryElement) { Iterator iterator = ((List)entryElement.getValue()).iterator(); Map map = new HashMap<>(); while (iterator.hasNext()) { @@ -597,7 +378,7 @@ private Object getValidEnumName(Map.Entry entryElement) { return map; } - private String populateRequestBodyExample(Operation operation) { + default String populateRequestBodyExample(Operation operation) { String result = "{\"content\": \"request body to be replaced\"}"; RequestBody body = operation.getRequestBody(); if (body != null) { @@ -627,7 +408,7 @@ private String populateRequestBodyExample(Operation operation) { return result; } - private Map populateResponseExample(Operation operation) { + default Map populateResponseExample(Operation operation) { Map result = new HashMap<>(); Object example; for (String statusCode : operation.getResponses().keySet()) { @@ -672,7 +453,7 @@ private Map populateResponseExample(Operation operation) { return result; } - private void loadModel(String classVarName, String parentClassName, Map value, Map schemas, boolean overwriteModel, String targetPath, String modelPackage, List modelCreators, Map references, List> parentClassProps) throws IOException { + default void loadModel(String classVarName, String parentClassName, Map value, Map schemas, boolean overwriteModel, String targetPath, String modelPackage, List modelCreators, Map references, List> parentClassProps) throws IOException { final String modelFileName = classVarName.substring(0, 1).toUpperCase() + classVarName.substring(1); final List> props = new ArrayList<>(); final List> parentProps = (parentClassProps == null) ? new ArrayList<>() : new ArrayList<>(parentClassProps); @@ -824,10 +605,10 @@ private void loadModel(String classVarName, String parentClassName, Map")) { return listType.substring(listType.indexOf("<") + 1, listType.indexOf(">")); } else { @@ -835,7 +616,7 @@ private String getListObjectType(String listType) { } } - private String setListObjectType(String original, String resolved) { + default String setListObjectType(String original, String resolved) { if(!original.equals(resolved) && original.contains("<") && original.contains(">")) { String replace = original.substring(original.indexOf("<") + 1, original.indexOf(">")); original = original.replace(replace, resolved); @@ -843,7 +624,7 @@ private String setListObjectType(String original, String resolved) { return original; } - private boolean unresolvedListType(String listType) { + default boolean unresolvedListType(String listType) { boolean result = false; if(listType != null && listType.contains("java.util.List")) { String objType = getListObjectType(listType); diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java new file mode 100644 index 000000000..c0c7b4892 --- /dev/null +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java @@ -0,0 +1,254 @@ +package com.networknt.codegen.rest; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.codegen.Generator; +import com.networknt.codegen.Utils; +import com.networknt.config.JsonMapper; +import com.networknt.jsonoverlay.Overlay; +import com.networknt.oas.OpenApiParser; +import com.networknt.oas.model.*; +import com.networknt.oas.model.impl.OpenApi3Impl; +import com.networknt.utility.StringUtils; + +import javax.lang.model.SourceVersion; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.*; +import java.util.stream.Collectors; + +import static java.io.File.separator; + +public class OpenApiLightGenerator implements OpenApiGenerator { + public static final String FRAMEWORK="openapi"; + + @Override + public String getFramework() { + return FRAMEWORK; + } + + boolean enableParamDescription = false; + + public OpenApiLightGenerator() { + } + + /** + * + * @param targetPath The output directory of the generated project + * @param model The optional model data that trigger the generation, i.e. swagger specification, graphql IDL etc. + * @param config A json object that controls how the generator behaves. + * + * @throws IOException IO Exception occurs during code generation + */ + @Override + public void generate(final String targetPath, Object model, JsonNode config) throws IOException { + // whoever is calling this needs to make sure that model is converted to Map + String rootPackage = getRootPackage(config, null); + String modelPackage = getModelPackage(config, null); + String handlerPackage = getHandlerPackage(config, null); + boolean overwriteHandler = isOverwriteHandler(config, null); + boolean overwriteHandlerTest = isOverwriteHandlerTest(config, null); + boolean overwriteModel = isOverwriteModel(config, null); + boolean generateModelOnly = isGenerateModelOnly(config, null); + boolean enableHttp = isEnableHttp(config, null); + String httpPort = getHttpPort(config, null); + boolean enableHttps = isEnableHttps(config, null); + String httpsPort = getHttpsPort(config, null); + boolean enableHttp2 = isEnableHttp2(config, null); + boolean enableRegistry = isEnableRegistry(config, null); + boolean eclipseIDE = isEclipseIDE(config, null); + boolean supportClient = isSupportClient(config, null); + boolean prometheusMetrics = isPrometheusMetrics(config, null); + String dockerOrganization = getDockerOrganization(config, null); + String version = getVersion(config, null); + String groupId = getGroupId(config, null); + String artifactId = getArtifactId(config, null); + String serviceId = groupId + "." + artifactId + "-" + version; + boolean specChangeCodeReGenOnly = isSpecChangeCodeReGenOnly(config, null); + boolean enableParamDescription = isEnableParamDescription(config, null); + boolean skipPomFile = isSkipPomFile(config, null); + boolean kafkaProducer = isKafkaProducer(config, null); + boolean kafkaConsumer = isKafkaConsumer(config, null); + boolean supportAvro = isSupportAvro(config, null); + String kafkaTopic = getKafkaTopic(config, null); + String decryptOption = getDecryptOption(config, null); + + // get the list of operations for this model + List> operationList = getOperationList(model, config); + + // bypass project generation if the mode is the only one requested to be built + if (!generateModelOnly) { + // if set to true, regenerate the code only (handlers, model and the handler.yml, potentially affected by operation changes + if (!specChangeCodeReGenOnly) { + // generate configurations, project, masks, certs, etc + if (!skipPomFile) { + transfer(targetPath, "", "pom.xml", templates.rest.openapi.pom.template(config)); + } + + + transferMaven(targetPath); + // There is only one port that should be exposed in Dockerfile, otherwise, the service + // discovery will be so confused. If https is enabled, expose the https port. Otherwise http port. + String expose = ""; + if (enableHttps) { + expose = httpsPort; + } else { + expose = httpPort; + } + + transfer(targetPath, "docker", "Dockerfile", templates.rest.dockerfile.template(config, expose)); + transfer(targetPath, "docker", "Dockerfile-Slim", templates.rest.dockerfileslim.template(config, expose)); + transfer(targetPath, "", "build.sh", templates.rest.buildSh.template(dockerOrganization, serviceId)); + transfer(targetPath, "", "kubernetes.yml", templates.rest.kubernetes.template(dockerOrganization, serviceId, config.get("artifactId").textValue(), expose, version)); + transfer(targetPath, "", ".gitignore", templates.rest.gitignore.template()); + transfer(targetPath, "", "README.md", templates.rest.README.template()); + transfer(targetPath, "", "LICENSE", templates.rest.LICENSE.template()); + if(eclipseIDE) { + transfer(targetPath, "", ".classpath", templates.rest.classpath.template()); + transfer(targetPath, "", ".project", templates.rest.project.template(config)); + } + // config + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "service.yml", templates.rest.openapi.service.template(config)); + + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "server.yml", + templates.rest.server.template(serviceId, enableHttp, httpPort, enableHttps, httpsPort, enableHttp2, enableRegistry, version)); + transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", + templates.rest.server.template(serviceId, enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); + + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "openapi-security.yml", templates.rest.openapiSecurity.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "openapi-validator.yml", templates.rest.openapiValidator.template()); + if (supportClient) { + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "client.yml", templates.rest.clientYml.template()); + } else { + transfer(targetPath, ("src.test.resources.config").replace(".", separator), "client.yml", templates.rest.clientYml.template()); + } + + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "primary.crt", templates.rest.primaryCrt.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "secondary.crt", templates.rest.secondaryCrt.template()); + if(kafkaProducer) { + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "kafka-producer.yml", templates.rest.kafkaProducerYml.template(kafkaTopic)); + } + if(kafkaConsumer) { + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "kafka-streams.yml", templates.rest.kafkaStreamsYml.template(artifactId)); + } + if(supportAvro) { + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "schema-registry.yml", templates.rest.schemaRegistryYml.template()); + } + + // mask + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "mask.yml", templates.rest.maskYml.template()); + // logging + transfer(targetPath, ("src.main.resources").replace(".", separator), "logback.xml", templates.rest.logback.template(rootPackage)); + transfer(targetPath, ("src.test.resources").replace(".", separator), "logback-test.xml", templates.rest.logback.template(rootPackage)); + + // exclusion list for Config module + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "config.yml", templates.rest.openapi.config.template(config)); + + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "audit.yml", templates.rest.auditYml.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "body.yml", templates.rest.bodyYml.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "info.yml", templates.rest.infoYml.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "correlation.yml", templates.rest.correlationYml.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "metrics.yml", templates.rest.metricsYml.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "sanitizer.yml", templates.rest.sanitizerYml.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "traceability.yml", templates.rest.traceabilityYml.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "health.yml", templates.rest.healthYml.template()); + // added with #471 + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "app-status.yml", templates.rest.appStatusYml.template()); + // values.yml file, transfer to suppress the warning message during start startup and encourage usage. + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "values.yml", templates.rest.openapi.values.template()); + } + // routing handler + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "handler.yml", + templates.rest.openapi.handlerYml.template(serviceId, handlerPackage, operationList, prometheusMetrics)); + + } + + // model + OpenApi3 openApi3 = null; + try { + openApi3 = (OpenApi3)new OpenApiParser().parse((JsonNode)model, new URL("https://oas.lightapi.net/")); + } catch (MalformedURLException e) { + throw new RuntimeException("Failed to parse the model", e); + } + Map specMap = JsonMapper.string2Map(Overlay.toJson((OpenApi3Impl)openApi3).toString()); + Map components = (Map)specMap.get("components"); + if(components != null) { + Map schemas = (Map)components.get("schemas"); + if(schemas != null) { + ArrayList modelCreators = new ArrayList<>(); + final HashMap references = new HashMap<>(); + for (Map.Entry entry : schemas.entrySet()) { + loadModel(entry.getKey(), null, (Map)entry.getValue(), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, null); + } + + for (Runnable r : modelCreators) { + r.run(); + } + } + } + + // exit after generating the model if the consumer needs only the model classes + if (generateModelOnly) { + return; + } + + // handler + for (Map op : operationList) { + String className = op.get("handlerName").toString(); + @SuppressWarnings("unchecked") + List parameters = (List)op.get("parameters"); + Map responseExample = (Map)op.get("responseExample"); + String example = responseExample.get("example"); + String statusCode = responseExample.get("statusCode"); + statusCode = StringUtils.isBlank(statusCode) || statusCode.equals("default") ? "-1" : statusCode; + if (checkExist(targetPath, ("src.main.java." + handlerPackage).replace(".", separator), className + ".java") && !overwriteHandler) { + continue; + } + transfer(targetPath, ("src.main.java." + handlerPackage).replace(".", separator), className + ".java", templates.rest.handler.template(handlerPackage, className, statusCode, example, parameters)); + } + + // handler test cases + if (!specChangeCodeReGenOnly) { + transfer(targetPath, ("src.test.java." + handlerPackage + ".").replace(".", separator), "TestServer.java", templates.rest.testServer.template(handlerPackage)); + } + + for (Map op : operationList) { + if (checkExist(targetPath, ("src.test.java." + handlerPackage).replace(".", separator), op.get("handlerName") + "Test.java") && !overwriteHandlerTest) { + continue; + } + transfer(targetPath, ("src.test.java." + handlerPackage).replace(".", separator), op.get("handlerName") + "Test.java", templates.rest.openapi.handlerTest.template(handlerPackage, op)); + } + + // transfer binary files without touching them. + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.keystore")) { + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.keystore")); + } + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.truststore")) { + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.truststore")); + } + if (supportClient) { + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.keystore")); + } + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.truststore")); + } + } else { + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { + Generator.copyFile(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.keystore")); + } + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { + Generator.copyFile(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.truststore")); + } + } + + try (InputStream is = new ByteArrayInputStream(Generator.yamlMapper.writeValueAsBytes(model))) { + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "openapi.yaml")); + } + } + +} diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpringGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpringGenerator.java new file mode 100644 index 000000000..e334deba4 --- /dev/null +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpringGenerator.java @@ -0,0 +1,4 @@ +package com.networknt.codegen.rest; + +public class OpenApiSpringGenerator { +} diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/ConfigTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/ConfigTest.java index 4d0a75476..c92fb8da8 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/ConfigTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/ConfigTest.java @@ -3,7 +3,6 @@ import com.fasterxml.jackson.databind.JsonNode; import org.junit.Assert; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import java.io.IOException; @@ -17,8 +16,8 @@ public class ConfigTest { @BeforeClass public static void setUp() throws IOException { // load config file - anyConfig = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); - anyYamlConfig = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configYamlName)); + anyConfig = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); + anyYamlConfig = Generator.yamlMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configYamlName)); } @Test diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiArrayReferenceGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiArrayReferenceGeneratorTest.java index 0664a4d27..51aea1971 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiArrayReferenceGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiArrayReferenceGeneratorTest.java @@ -6,6 +6,7 @@ import java.nio.file.Paths; import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.codegen.rest.OpenApiLightGenerator; import com.thoughtworks.qdox.JavaProjectBuilder; import com.thoughtworks.qdox.model.JavaClass; import com.thoughtworks.qdox.model.JavaField; @@ -50,9 +51,9 @@ static void delete(File f) throws IOException { } public static JavaPackage prepareJavaPackage(String targetPath, String packageName) throws IOException { - JsonNode config = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(OpenApiArrayReferenceGeneratorTest.configName)); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(OpenApiArrayReferenceGeneratorTest.configName)); JsonNode model = Generator.jsonMapper.readTree(OpenApiKotlinGeneratorTest.class.getResourceAsStream(OpenApiArrayReferenceGeneratorTest.openapiJson)); - OpenApiGenerator generator = new OpenApiGenerator(); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); generator.generate(targetPath, model, config); File file = new File(targetPath); diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLightGeneratorTest.java similarity index 69% rename from light-rest-4j/src/test/java/com/networknt/codegen/OpenApiGeneratorTest.java rename to light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLightGeneratorTest.java index 14d62a52a..36deb89fe 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLightGeneratorTest.java @@ -1,7 +1,5 @@ package com.networknt.codegen; -import java.io.File; -import java.io.FilenameFilter; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.file.Files; @@ -9,6 +7,7 @@ import java.util.List; import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.codegen.rest.OpenApiLightGenerator; import com.networknt.config.Config; import org.junit.Assert; import org.junit.BeforeClass; @@ -17,13 +16,12 @@ import com.networknt.codegen.rest.OpenApiGenerator; -import static java.io.File.separator; import static org.junit.Assert.assertTrue; /** * @author Steve Hu */ -public class OpenApiGeneratorTest { +public class OpenApiLightGeneratorTest { public static String targetPath = "/tmp/openapi"; public static String configName = "/config.json"; public static String configKafkaName = "/configKafka.json"; @@ -48,54 +46,55 @@ public static void tearDown() throws IOException { @Test public void testGeneratorJson() throws IOException { - JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); - JsonNode modelNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiJson)); - OpenApiGenerator generator = new OpenApiGenerator(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(openapiJson)); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); generator.generate(targetPath, modelNode, configNode); } @Test public void testGeneratorYaml() throws IOException { - JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); - JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiYaml)); - OpenApiGenerator generator = new OpenApiGenerator(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(openapiYaml)); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); generator.generate(targetPath, modelNode, configNode); } @Test public void testGeneratorAccountInfo() throws IOException { - JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); - JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(accountInfoYaml)); - OpenApiGenerator generator = new OpenApiGenerator(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(accountInfoYaml)); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); generator.generate(targetPath, modelNode, configNode); } @Test public void testGetOperationList() throws IOException { - JsonNode anyModel = Config.getInstance().getMapper().readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiJson)); - OpenApiGenerator generator = new OpenApiGenerator(); - List list = generator.getOperationList(anyModel); + JsonNode model = Config.getInstance().getMapper().readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(openapiJson)); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); + List list = generator.getOperationList(model, config); System.out.println(list); } @Test public void testGetFramework() { - OpenApiGenerator generator = new OpenApiGenerator(); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); Assert.assertEquals("openapi", generator.getFramework()); } @Test public void testGetConfigSchema() throws IOException { - OpenApiGenerator generator = new OpenApiGenerator(); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); ByteBuffer bf = generator.getConfigSchema(); Assert.assertNotNull(bf); System.out.println(bf.toString()); } @Test public void testNoServersGeneratorYaml() throws IOException { - JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); - JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiNoServersYaml)); - OpenApiGenerator generator = new OpenApiGenerator(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(openapiNoServersYaml)); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); generator.generate(targetPath, modelNode, configNode); } @@ -111,26 +110,26 @@ public void testConvertInvalidVariableName() { @Test public void testGeneratorYamlEnum() throws IOException { - JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); - JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiEnumYaml)); - OpenApiGenerator generator = new OpenApiGenerator(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(openapiEnumYaml)); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); generator.generate(targetPath, modelNode, configNode); } @Test @Ignore public void testGeneratorYamlError() throws IOException { - JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); - JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiErrorYaml)); - OpenApiGenerator generator = new OpenApiGenerator(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(openapiErrorYaml)); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); generator.generate(targetPath, modelNode, configNode); } @Test public void testGeneratorKafka() throws IOException { - JsonNode configNode = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configKafkaName)); - JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(openapiYaml)); - OpenApiGenerator generator = new OpenApiGenerator(); + JsonNode configNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configKafkaName)); + JsonNode modelNode = Generator.yamlMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(openapiYaml)); + OpenApiLightGenerator generator = new OpenApiLightGenerator(); generator.generate(targetPath, modelNode, configNode); } diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/rest/OpenApiSpecGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/rest/OpenApiSpecGeneratorTest.java index 10002f96d..076a48f8a 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/rest/OpenApiSpecGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/rest/OpenApiSpecGeneratorTest.java @@ -7,7 +7,7 @@ import org.junit.Ignore; import org.junit.Test; -import com.networknt.codegen.OpenApiGeneratorTest; +import com.networknt.codegen.OpenApiLightGeneratorTest; @Ignore public class OpenApiSpecGeneratorTest { @@ -16,7 +16,7 @@ public class OpenApiSpecGeneratorTest { @Test public void test() throws IOException { - JsonNode config = Generator.jsonMapper.readTree(OpenApiGeneratorTest.class.getResourceAsStream(configName)); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); OpenApiSpecGenerator generator = new OpenApiSpecGenerator(); diff --git a/light-rest-4j/src/test/resources/config.json b/light-rest-4j/src/test/resources/config.json index 16be9b55e..c1c6fb838 100644 --- a/light-rest-4j/src/test/resources/config.json +++ b/light-rest-4j/src/test/resources/config.json @@ -26,6 +26,7 @@ "supportH2ForTest": false, "supportClient": false, "dockerOrganization": "networknt", + "enableParamDescription": false, "generateEnvVars": { "generate": true, "skipArray": true, From c756664b179de5b874ce43ccdfa8937145cc21fd Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Wed, 17 Feb 2021 21:31:40 -0500 Subject: [PATCH 3/7] fixes #528 add Lambda generator --- .../src/main/resources/config/service.yml | 2 +- .../networknt/codegen/rest/ModelCallback.java | 8 + .../codegen/rest/OpenApiGenerator.java | 35 +- .../codegen/rest/OpenApiKotlinGenerator.java | 220 ++++++++++- .../codegen/rest/OpenApiLambdaGenerator.java | 355 +++++++++++++++++- .../codegen/rest/OpenApiLightGenerator.java | 24 +- .../codegen/rest/OpenApiSpecGenerator.java | 10 +- .../lambda/proxy/handlerYml.rocker.raw | 10 +- .../restkotlin/openapi/handlerYml.rocker.raw | 10 +- .../codegen/OpenApiKotlinGeneratorTest.java | 7 +- .../codegen/OpenApiLambdaGeneratorTest.java | 3 +- .../codegen/OpenApiLightGeneratorTest.java | 6 +- .../src/test/resources/configProxyLambda.json | 1 + 13 files changed, 639 insertions(+), 52 deletions(-) create mode 100644 light-rest-4j/src/main/java/com/networknt/codegen/rest/ModelCallback.java diff --git a/codegen-cli/src/main/resources/config/service.yml b/codegen-cli/src/main/resources/config/service.yml index 7f591f21c..149bfa902 100644 --- a/codegen-cli/src/main/resources/config/service.yml +++ b/codegen-cli/src/main/resources/config/service.yml @@ -2,7 +2,7 @@ singletons: # Generator interface implementations - com.networknt.codegen.Generator: - - com.networknt.codegen.rest.OpenApiGenerator + - com.networknt.codegen.rest.OpenApiLightGenerator - com.networknt.codegen.hybrid.HybridServerGenerator - com.networknt.codegen.hybrid.HybridServiceGenerator - com.networknt.codegen.graphql.GraphqlGenerator diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/ModelCallback.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/ModelCallback.java new file mode 100644 index 000000000..95fab71f4 --- /dev/null +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/ModelCallback.java @@ -0,0 +1,8 @@ +package com.networknt.codegen.rest; + +import java.util.List; +import java.util.Map; + +public interface ModelCallback { + void callback(String targetPath, String modelPackage, String modelFileName, String enumsIfClass, String parentClassName, String classVarName, boolean abstractIfClass, List> props, List> parentClassProps); +} diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java index 2a0bd5357..fa62fe438 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiGenerator.java @@ -275,6 +275,21 @@ default String getEnumDescription(String string) { return string; } + default String getScopes(Operation operation) { + String scopes = null; + if(operation.hasSecurityRequirements()) { + SecurityRequirement securityRequirement = operation.getSecurityRequirement(0); + if(securityRequirement != null) { + Map requirements = securityRequirement.getRequirements(); + for(SecurityParameter parameter : requirements.values()) { + List ls = parameter.getParameters(); + if(ls != null) scopes = StringUtils.join(ls, ' '); + } + } + } + return scopes; + } + default List> getOperationList(Object model, JsonNode config) { List> result = new ArrayList<>(); OpenApi3 openApi3 = null; @@ -301,6 +316,9 @@ default List> getOperationList(Object model, JsonNode config flattened.put("path", basePath + path); String normalizedPath = path.replace("{", "").replace("}", ""); flattened.put("handlerName", Utils.camelize(normalizedPath) + Utils.camelize(entryOps.getKey()) + "Handler"); + flattened.put("functionName", Utils.camelize(normalizedPath) + Utils.camelize(entryOps.getKey()) + "Function"); + flattened.put("endpoint", path + "@" + entryOps.getKey().toLowerCase()); + flattened.put("apiName", Utils.camelize(normalizedPath) + Utils.camelize(entryOps.getKey())); Operation operation = entryOps.getValue(); flattened.put("normalizedPath", UrlGenerator.generateUrl(basePath, path, entryOps.getValue().getParameters())); //eg. 200 || statusCode == 400 || statusCode == 500 @@ -313,6 +331,7 @@ default List> getOperationList(Object model, JsonNode config flattened.put("requestBodyExample", populateRequestBodyExample(operation)); Map responseExample = populateResponseExample(operation); flattened.put("responseExample", responseExample); + flattened.put("scopes", getScopes(operation)); if (config.get("enableParamDescription").booleanValue()) { //get parameters info and put into result List parameterRawList = operation.getParameters(); @@ -453,7 +472,7 @@ default Map populateResponseExample(Operation operation) { return result; } - default void loadModel(String classVarName, String parentClassName, Map value, Map schemas, boolean overwriteModel, String targetPath, String modelPackage, List modelCreators, Map references, List> parentClassProps) throws IOException { + default void loadModel(String classVarName, String parentClassName, Map value, Map schemas, boolean overwriteModel, String targetPath, String modelPackage, List modelCreators, Map references, List> parentClassProps, ModelCallback lightCallback) throws IOException { final String modelFileName = classVarName.substring(0, 1).toUpperCase() + classVarName.substring(1); final List> props = new ArrayList<>(); final List> parentProps = (parentClassProps == null) ? new ArrayList<>() : new ArrayList<>(parentClassProps); @@ -520,7 +539,7 @@ default void loadModel(String classVarName, String parentClassName, Map)schemas.get(s), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, parentProps); + loadModel(extendModelName(s, classVarName), s, (Map)schemas.get(s), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, parentProps, lightCallback); } } } @@ -582,17 +601,7 @@ default void loadModel(String classVarName, String parentClassName, Map map = new HashMap<>(1); diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiKotlinGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiKotlinGenerator.java index 624235f5c..f3937a15d 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiKotlinGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiKotlinGenerator.java @@ -2,15 +2,36 @@ import com.fasterxml.jackson.databind.JsonNode; import com.networknt.codegen.Generator; +import com.networknt.config.JsonMapper; +import com.networknt.jsonoverlay.Overlay; +import com.networknt.oas.OpenApiParser; +import com.networknt.oas.model.OpenApi3; +import com.networknt.oas.model.impl.OpenApi3Impl; +import com.networknt.utility.StringUtils; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; -public class OpenApiKotlinGenerator implements Generator { +import static java.io.File.separator; + +public class OpenApiKotlinGenerator implements OpenApiGenerator { + + public static final String FRAMEWORK="openapikotlin"; + @Override public String getFramework() { - return "openapikotlin"; + return FRAMEWORK; } /** @@ -22,10 +43,199 @@ public String getFramework() { */ @Override public void generate(String targetPath, Object model, JsonNode config) throws IOException { + // whoever is calling this needs to make sure that model is converted to Map + String rootPackage = getRootPackage(config, null); + String modelPackage = getModelPackage(config, null); + String handlerPackage = getHandlerPackage(config, null); + boolean overwriteHandler = isOverwriteHandler(config, null); + boolean overwriteHandlerTest = isOverwriteHandlerTest(config, null); + boolean overwriteModel = isOverwriteModel(config, null); + boolean generateModelOnly = isGenerateModelOnly(config, null); + boolean enableHttp = isEnableHttp(config, null); + String httpPort = getHttpPort(config, null); + boolean enableHttps = isEnableHttps(config, null); + String httpsPort = getHttpsPort(config, null); + boolean enableHttp2 = isEnableHttp2(config, null); + boolean enableRegistry = isEnableRegistry(config, null); + boolean eclipseIDE = isEclipseIDE(config, null); + boolean supportClient = isSupportClient(config, null); + boolean prometheusMetrics = isPrometheusMetrics(config, null); + String dockerOrganization = getDockerOrganization(config, null); + String version = getVersion(config, null); + String groupId = getGroupId(config, null); + String artifactId = getArtifactId(config, null); + String serviceId = groupId + "." + artifactId + "-" + version; + boolean specChangeCodeReGenOnly = isSpecChangeCodeReGenOnly(config, null); + boolean enableParamDescription = isEnableParamDescription(config, null); + boolean skipPomFile = isSkipPomFile(config, null); + boolean kafkaProducer = isKafkaProducer(config, null); + boolean kafkaConsumer = isKafkaConsumer(config, null); + boolean supportAvro = isSupportAvro(config, null); + String kafkaTopic = getKafkaTopic(config, null); + String decryptOption = getDecryptOption(config, null); + // get the list of operations for this model + List> operationList = getOperationList(model, config); + + // bypass project generation if the mode is the only one requested to be built + if(!generateModelOnly) { + // if set to true, regenerate the code only (handlers, model and the handler.yml, potentially affected by operation changes + if (!specChangeCodeReGenOnly) { + // generate configurations, project, masks, certs, etc + // There is only one port that should be exposed in Dockerfile, otherwise, the service + // discovery will be so confused. If https is enabled, expose the https port. Otherwise http port. + String expose = ""; + if(enableHttps) { + expose = httpsPort; + } else { + expose = httpPort; + } + + transfer(targetPath, "docker", "Dockerfile", templates.restkotlin.dockerfile.template(config, expose)); + transfer(targetPath, "docker", "Dockerfile-Slim", templates.restkotlin.dockerfileslim.template(config, expose)); + transfer(targetPath, "", "build.sh", templates.restkotlin.buildSh.template(dockerOrganization, serviceId)); + transfer(targetPath, "", ".gitignore", templates.restkotlin.gitignore.template()); + transfer(targetPath, "", "README.md", templates.restkotlin.README.template()); + transfer(targetPath, "", "LICENSE", templates.restkotlin.LICENSE.template()); + transfer(targetPath, "", "build.gradle.kts", templates.restkotlin.buildGradleKts.template(config)); + transfer(targetPath, "", "gradle.properties", templates.restkotlin.gradleProperties.template(config)); + transfer(targetPath, "", "settings.gradle.kts", templates.restkotlin.settingsGradleKts.template(config)); + + transferGradle(targetPath); + + // config + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "service.yml", templates.restkotlin.openapi.service.template(config)); + + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "server.yml", templates.restkotlin.server.template(serviceId, enableHttp, httpPort, enableHttps, httpsPort, enableHttp2, enableRegistry, version)); + transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", templates.restkotlin.server.template(serviceId, enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); + + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "openapi-security.yml", templates.restkotlin.openapiSecurity.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "openapi-validator.yml", templates.restkotlin.openapiValidator.template()); + if(supportClient) { + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "client.yml", templates.restkotlin.clientYml.template()); + } else { + transfer(targetPath, ("src.test.resources.config").replace(".", separator), "client.yml", templates.restkotlin.clientYml.template()); + } + + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "primary.crt", templates.restkotlin.primaryCrt.template()); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "secondary.crt", templates.restkotlin.secondaryCrt.template()); + + // mask + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "mask.yml", templates.restkotlin.maskYml.template()); + // logging + transfer(targetPath, ("src.main.resources").replace(".", separator), "logback.xml", templates.restkotlin.logback.template()); + transfer(targetPath, ("src.test.resources").replace(".", separator), "logback-test.xml", templates.restkotlin.logback.template()); + transfer(targetPath, ("src.test.resources").replace(".", separator), "junit-platform.properties", templates.restkotlin.junitPlatformProperties.template()); + + // routing handler + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "handler.yml", templates.restkotlin.openapi.handlerYml.template(serviceId, handlerPackage, operationList, prometheusMetrics)); + + // exclusion list for Config module + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "config.yml", templates.restkotlin.openapi.config.template()); + // added with #471 + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "app-status.yml", templates.restkotlin.appStatusYml.template()); + // values.yml file, transfer to suppress the warning message during start startup and encourage usage. + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "values.yml", templates.restkotlin.openapi.values.template()); + } + } + + // model + // model + OpenApi3 openApi3 = null; + try { + openApi3 = (OpenApi3)new OpenApiParser().parse((JsonNode)model, new URL("https://oas.lightapi.net/")); + } catch (MalformedURLException e) { + throw new RuntimeException("Failed to parse the model", e); + } + Map specMap = JsonMapper.string2Map(Overlay.toJson((OpenApi3Impl)openApi3).toString()); + Map components = (Map)specMap.get("components"); + if(components != null) { + Map schemas = (Map)components.get("schemas"); + if(schemas != null) { + ArrayList modelCreators = new ArrayList<>(); + final HashMap references = new HashMap<>(); + for (Map.Entry entry : schemas.entrySet()) { + loadModel(entry.getKey(), null, (Map)entry.getValue(), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, null, callback); + } + + for (Runnable r : modelCreators) { + r.run(); + } + } + } + + // exit after generating the model if the consumer needs only the model classes + if(generateModelOnly) + return; + + // handler + for(Map op : operationList){ + String className = op.get("handlerName").toString(); + @SuppressWarnings("unchecked") + List parameters = (List)op.get("parameters"); + Map responseExample = (Map)op.get("responseExample"); + String example = responseExample.get("example"); + String statusCode = responseExample.get("statusCode"); + statusCode = StringUtils.isBlank(statusCode) || statusCode.equals("default") ? "-1" : statusCode; + if (checkExist(targetPath, ("src.main.kotlin." + handlerPackage).replace(".", separator), className + ".kt") && !overwriteHandler) { + continue; + } + transfer(targetPath, ("src.main.kotlin." + handlerPackage).replace(".", separator), className + ".kt", templates.restkotlin.handler.template(handlerPackage, className, example, parameters)); + } + + // handler test cases + if (!specChangeCodeReGenOnly) { + transfer(targetPath, ("src.test.kotlin." + handlerPackage + ".").replace(".", separator), "LightTestServer.kt", templates.restkotlin.lightTestServerKt.template(handlerPackage)); + } + for(Map op : operationList){ + if(checkExist(targetPath, ("src.test.kotlin." + handlerPackage).replace(".", separator), op.get("handlerName") + "Test.kt") && !overwriteHandlerTest) { + continue; + } + transfer(targetPath, ("src.test.kotlin." + handlerPackage).replace(".", separator), op.get("handlerName") + "Test.kt", templates.restkotlin.handlerTest.template(handlerPackage, op)); + } + + // transfer binary files without touching them. + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.keystore")) { + Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.keystore"), StandardCopyOption.REPLACE_EXISTING); + } + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/server.truststore")) { + Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "server.truststore"), StandardCopyOption.REPLACE_EXISTING); + } + if(supportClient) { + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { + Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.keystore"), StandardCopyOption.REPLACE_EXISTING); + } + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { + Files.copy(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "client.truststore"), StandardCopyOption.REPLACE_EXISTING); + } + } else { + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.keystore")) { + Files.copy(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.keystore"), StandardCopyOption.REPLACE_EXISTING); + } + try (InputStream is = OpenApiGenerator.class.getResourceAsStream("/binaries/client.truststore")) { + Files.copy(is, Paths.get(targetPath, ("src.test.resources.config").replace(".", separator), "client.truststore"), StandardCopyOption.REPLACE_EXISTING); + } + } + + try (InputStream is = new ByteArrayInputStream(Generator.yamlMapper.writeValueAsBytes(model))) { + Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources.config").replace(".", separator), "openapi.yaml")); + } } - public List> getOperationList(Object model) { - return null; - } + + ModelCallback callback = new ModelCallback() { + @Override + public void callback(String targetPath, String modelPackage, String modelFileName, String enumsIfClass, String parentClassName, String classVarName, boolean abstractIfClass, List> props, List> parentClassProps) { + try { + transfer(targetPath, + ("src.main.kotlin." + modelPackage).replace(".", separator), + modelFileName + ".kt", + enumsIfClass == null + ? templates.restkotlin.pojo.template(modelPackage, modelFileName, classVarName, props) + : templates.restkotlin.enumClass.template(modelPackage, modelFileName, enumsIfClass)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + }; } diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java index 248452e96..4b66ef8fc 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java @@ -1,17 +1,33 @@ package com.networknt.codegen.rest; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.networknt.codegen.Generator; +import com.networknt.config.JsonMapper; +import com.networknt.jsonoverlay.Overlay; +import com.networknt.oas.OpenApiParser; +import com.networknt.oas.model.OpenApi3; +import com.networknt.oas.model.impl.OpenApi3Impl; +import com.networknt.utility.StringUtils; +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.util.*; + +import static java.io.File.separator; + +public class OpenApiLambdaGenerator implements OpenApiGenerator { + + public static final String FRAMEWORK="openapilambda"; -public class OpenApiLambdaGenerator implements Generator { @Override public String getFramework() { - return "openapilambda"; + return FRAMEWORK; } /** @@ -24,10 +40,253 @@ public String getFramework() { */ @Override public void generate(final String targetPath, Object model, JsonNode config) throws IOException { + // Lambda specific config + boolean packageDocker = isPackageDocker(config, null); + boolean useLightProxy = isUseLightProxy(config, null); + boolean buildMaven = isBuildMaven(config, null); + String launchType = getLaunchType(config, null); + String region = getRegion(config, null); + boolean publicVpc = isPublicVpc(config, null); + // Common config + String rootPackage = getRootPackage(config, null); + String modelPackage = getModelPackage(config, null); + String handlerPackage = getHandlerPackage(config, null); + boolean overwriteHandler = isOverwriteHandler(config, null); + boolean overwriteHandlerTest = isOverwriteHandlerTest(config, null); + boolean overwriteModel = isOverwriteModel(config, null); + boolean generateModelOnly = isGenerateModelOnly(config, null); + boolean enableHttp = isEnableHttp(config, null); + String httpPort = getHttpPort(config, null); + boolean enableHttps = isEnableHttps(config, null); + String httpsPort = getHttpsPort(config, null); + boolean enableHttp2 = isEnableHttp2(config, null); + boolean enableRegistry = isEnableRegistry(config, null); + boolean eclipseIDE = isEclipseIDE(config, null); + boolean supportClient = isSupportClient(config, null); + boolean prometheusMetrics = isPrometheusMetrics(config, null); + String dockerOrganization = getDockerOrganization(config, null); + String version = getVersion(config, null); + String groupId = getGroupId(config, null); + String artifactId = getArtifactId(config, null); + String serviceId = groupId + "." + artifactId + "-" + version; + boolean specChangeCodeReGenOnly = isSpecChangeCodeReGenOnly(config, null); + boolean enableParamDescription = isEnableParamDescription(config, null); + boolean skipPomFile = isSkipPomFile(config, null); + boolean kafkaProducer = isKafkaProducer(config, null); + boolean kafkaConsumer = isKafkaConsumer(config, null); + boolean supportAvro = isSupportAvro(config, null); + String kafkaTopic = getKafkaTopic(config, null); + String decryptOption = getDecryptOption(config, null); + + // get the list of operations for this model + List> operationList = getOperationList(model, config); + List pathList = getPathList(operationList); + transfer(targetPath, "", ".gitignore", templates.lambda.gitignore.template()); + + transfer(targetPath, "", "README.md", templates.lambda.README.template(artifactId, packageDocker, operationList)); + + if(!useLightProxy) { + // use AWS API Gateway to access Lambda functions. + transfer(targetPath, "", "template.yaml", templates.lambda.template.template(artifactId, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); + } else { + // use light-proxy for Lambda function + if("EC2".equals(launchType)) { + if(publicVpc) { + transfer(targetPath, "", "public-vpc.yaml", templates.lambda.EC2.publicVpcYaml.template()); + transfer(targetPath, "", "public-proxy.yaml", templates.lambda.EC2.publicProxyYaml.template()); + transfer(targetPath, "", "template.yaml", templates.lambda.template.template(artifactId, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); + } else { + transfer(targetPath, "", "private-vpc.yaml", templates.lambda.EC2.privateVpcYaml.template()); + transfer(targetPath, "", "private-proxy.yaml", templates.lambda.EC2.privateProxyYaml.template()); + transfer(targetPath, "", "template.yaml", templates.lambda.template.template(artifactId, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); + } + } else { + // fargate as default + if(publicVpc) { + transfer(targetPath, "", "public-vpc.yaml", templates.lambda.Fargate.publicVpcYaml.template()); + transfer(targetPath, "", "public-proxy.yaml", templates.lambda.Fargate.publicProxyYaml.template()); + transfer(targetPath, "", "template.yaml", templates.lambda.template.template(artifactId, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); + } else { + transfer(targetPath, "", "private-vpc.yaml", templates.lambda.Fargate.privateVpcYaml.template()); + transfer(targetPath, "", "private-proxy.yaml", templates.lambda.Fargate.privateProxyYaml.template()); + transfer(targetPath, "", "template.yaml", templates.lambda.template.template(artifactId, handlerPackage, packageDocker, useLightProxy, operationList, pathList)); + } + } + transfer(targetPath, "proxy", "handler.yml", + templates.lambda.proxy.handlerYml.template(serviceId, handlerPackage, operationList, prometheusMetrics)); + + transfer(targetPath, "proxy", "lambda-invoker.yml", + templates.lambda.proxy.lambdaInvokerYml.template(region, operationList)); + try (InputStream is = new ByteArrayInputStream(Generator.yamlMapper.writeValueAsBytes(model))) { + Generator.copyFile(is, Paths.get(targetPath, "proxy", "openapi.yaml")); + } + transfer(targetPath, "proxy", "server.yml", templates.lambda.proxy.server.template(serviceId, enableRegistry, version)); + + transfer(targetPath, "proxy", "openapi-security.yml", templates.rest.openapiSecurity.template()); + transfer(targetPath, "proxy", "openapi-validator.yml", templates.rest.openapiValidator.template()); + transfer(targetPath, "proxy", "client.yml", templates.rest.clientYml.template()); + + transfer(targetPath, "proxy", "primary.crt", templates.rest.primaryCrt.template()); + transfer(targetPath, "proxy", "secondary.crt", templates.rest.secondaryCrt.template()); + // transfer binary files without touching them. + try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/server.keystore")) { + Generator.copyFile(is, Paths.get(targetPath, "proxy", "server.keystore")); + } + try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/server.truststore")) { + Generator.copyFile(is, Paths.get(targetPath, "proxy", "server.truststore")); + } + try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/client.keystore")) { + Generator.copyFile(is, Paths.get(targetPath, "proxy", "client.keystore")); + } + try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/client.truststore")) { + Generator.copyFile(is, Paths.get(targetPath, "proxy", "client.truststore")); + } + // logging + transfer(targetPath, "proxy", "logback.xml", templates.rest.logback.template(rootPackage)); + // proxy.yml + transfer(targetPath, "proxy", "proxy.yml", templates.lambda.proxy.proxy.template()); + + // exclusion list for Config module + transfer(targetPath, "proxy", "config.yml", templates.rest.openapi.config.template(config)); + + transfer(targetPath, "proxy", "audit.yml", templates.rest.auditYml.template()); + transfer(targetPath, "proxy", "body.yml", templates.rest.bodyYml.template()); + transfer(targetPath, "proxy", "info.yml", templates.rest.infoYml.template()); + transfer(targetPath, "proxy", "correlation.yml", templates.rest.correlationYml.template()); + transfer(targetPath, "proxy", "metrics.yml", templates.rest.metricsYml.template()); + transfer(targetPath, "proxy", "sanitizer.yml", templates.rest.sanitizerYml.template()); + transfer(targetPath, "proxy", "traceability.yml", templates.rest.traceabilityYml.template()); + transfer(targetPath, "proxy", "health.yml", templates.rest.healthYml.template()); + // values.yml file, transfer to suppress the warning message during start startup and encourage usage. + transfer(targetPath, "proxy", "values.yml", templates.rest.openapi.values.template()); + // buildSh.rocker.raw for the docker image build + transfer(targetPath, "", "build.sh", templates.lambda.buildSh.template()); + // Dockerfile for the proxy + transfer(targetPath, "", "Dockerfile-proxy", templates.lambda.DockerfileProxy.template()); + } + + // handler + for (Map op : operationList) { + // for each operation, we need to generate a function in a separate folder. + String functionName = op.get("functionName").toString(); + + // generate event.json + transfer(targetPath, "events", "event" + functionName + ".json", templates.lambda.event.template()); + + + // generate Dockerfile if packageDocker is true + if(packageDocker) { + transfer(targetPath, functionName, "Dockerfile", templates.lambda.Dockerfile.template(handlerPackage)); + } + + if(buildMaven) { + // generate pom.xml + transfer(targetPath, functionName, "pom.xml", templates.lambda.pom.template(config, functionName)); + transferMaven(targetPath + separator + functionName); + } else { + transfer(targetPath, functionName, "build.gradle", templates.lambda.buildGradle.template(config)); + transfer(targetPath, functionName, "gradle.properties", templates.lambda.gradleProperties.template()); + transferGradle(targetPath + separator + functionName); + transfer(targetPath, functionName, "bootstrap", templates.lambda.bootstrap.template()); + transfer(targetPath, functionName, "build_graalvm.sh", templates.lambda.buildGraalvmSh.template(functionName)); + transfer(targetPath, functionName, "reflect.json", templates.lambda.reflectJson.template(handlerPackage)); + transfer(targetPath, functionName, "resource-config.json", templates.lambda.resourceJson.template()); + transfer(targetPath, functionName, "Makefile", templates.lambda.Makefile.template(functionName)); + + } + + // generate handler + String className = op.get("handlerName").toString(); + @SuppressWarnings("unchecked") + List parameters = (List)op.get("parameters"); + Map responseExample = (Map)op.get("responseExample"); + String example = responseExample.get("example"); + String statusCode = responseExample.get("statusCode"); + statusCode = StringUtils.isBlank(statusCode) || statusCode.equals("default") ? "-1" : statusCode; + + if (checkExist(targetPath + separator + functionName, ("src.main.java." + handlerPackage).replace(".", separator), "BusinessHandler.java")) { + continue; + } else { + transfer(targetPath + separator + functionName, ("src.main.java." + handlerPackage).replace(".", separator), "BusinessHandler.java", templates.lambda.BusinessHandler.template(handlerPackage, example)); + } + if (checkExist(targetPath + separator + functionName, ("src.test.java." + handlerPackage).replace(".", separator), "BusinessHandlerTest.java")) { + continue; + } else { + transfer(targetPath + separator + functionName, ("src.test.java." + handlerPackage).replace(".", separator), "BusinessHandlerTest.java", templates.lambda.BusinessHandlerTest.template(handlerPackage, op)); + } + if(useLightProxy) { + transfer(targetPath + separator + functionName, ("src.main.java." + handlerPackage).replace(".", separator), "App.java", templates.lambda.AppProxy.template(handlerPackage)); + } else { + transfer(targetPath + separator + functionName, ("src.main.java." + handlerPackage).replace(".", separator), "App.java", templates.lambda.AppGateway.template(handlerPackage)); + } + transfer(targetPath + separator + functionName, ("src.test.java." + handlerPackage).replace(".", separator), "AppTest.java", templates.lambda.AppTest.template(handlerPackage)); + + // generate model + OpenApi3 openApi3 = null; + try { + openApi3 = (OpenApi3)new OpenApiParser().parse((JsonNode)model, new URL("https://oas.lightapi.net/")); + } catch (MalformedURLException e) { + throw new RuntimeException("Failed to parse the model", e); + } + Map specMap = JsonMapper.string2Map(Overlay.toJson((OpenApi3Impl)openApi3).toString()); + Map components = (Map)specMap.get("components"); + if(components != null) { + Map schemas = (Map)components.get("schemas"); + if(schemas != null) { + ArrayList modelCreators = new ArrayList<>(); + final HashMap references = new HashMap<>(); + for (Map.Entry entry : schemas.entrySet()) { + loadModel(entry.getKey(), null, (Map)entry.getValue(), schemas, overwriteModel, targetPath + separator + functionName, modelPackage, modelCreators, references, null, callback); + } + + for (Runnable r : modelCreators) { + r.run(); + } + } + } + + if(!useLightProxy) { + try (InputStream is = new ByteArrayInputStream(Generator.yamlMapper.writeValueAsBytes(model))) { + Generator.copyFile(is, Paths.get(targetPath + separator + functionName, ("src.main.resources").replace(".", separator), "openapi.yaml")); + } + } + + // app.yml + transfer(targetPath + separator + functionName, ("src.main.resources").replace(".", separator), "app.yml", templates.lambda.appYml.template(useLightProxy)); + // logback.xml + transfer(targetPath + separator + functionName, ("src.main.resources").replace(".", separator), "logback.xml", templates.lambda.logback.template(rootPackage)); + transfer(targetPath + separator + functionName, ("src.test.resources").replace(".", separator), "logback-test.xml", templates.lambda.logback.template(rootPackage)); + // client truststore for the Prod stage. + if(!useLightProxy) { + try (InputStream is = OpenApiLambdaGenerator.class.getResourceAsStream("/binaries/client.truststore")) { + Generator.copyFile(is, Paths.get(targetPath + separator + functionName, ("src.main.resources").replace(".", separator), "prod.truststore")); + } + } + } } - public List> getOperationList(Object model) { - return null; + + private List getPathList(List> operationList) { + List pathList = new ArrayList<>(); + Set pathSet = new HashSet<>(); + OpenApiPath openApiPath = null; + for(Map op : operationList) { + String path = (String)op.get("path"); + String method = ((String)op.get("method")).toLowerCase(); + String functionName = (String)op.get("functionName"); + if(!pathSet.contains(path)) { + openApiPath = new OpenApiPath(); + openApiPath.setPath(path); + pathSet.add(path); + MethodFunction methodFunction = new MethodFunction(method, functionName); + openApiPath.addMethodFunction(methodFunction); + pathList.add(openApiPath); + } else { + MethodFunction methodFunction = new MethodFunction(method, functionName); + openApiPath.addMethodFunction(methodFunction); + } + } + return pathList; } public class OpenApiPath { @@ -78,4 +337,86 @@ public void setFunctionName(String functionName) { } } + private boolean isPackageDocker(JsonNode config, Boolean defaultValue) { + boolean packageDocker = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("packageDocker"); + if(jsonNode == null) { + ((ObjectNode)config).put("packageDocker", packageDocker); + } else { + packageDocker = jsonNode.booleanValue(); + } + return packageDocker; + } + + private boolean isUseLightProxy(JsonNode config, Boolean defaultValue) { + boolean useLightProxy = defaultValue == null ? true : defaultValue; + JsonNode jsonNode = config.get("useLightProxy"); + if(jsonNode == null) { + ((ObjectNode)config).put("useLightProxy", useLightProxy); + } else { + useLightProxy = jsonNode.booleanValue(); + } + return useLightProxy; + } + + private boolean isBuildMaven(JsonNode config, Boolean defaultValue) { + boolean buildMaven = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("buildMaven"); + if(jsonNode == null) { + ((ObjectNode)config).put("buildMaven", buildMaven); + } else { + buildMaven = jsonNode.booleanValue(); + } + return buildMaven; + } + + private boolean isPublicVpc(JsonNode config, Boolean defaultValue) { + boolean publicVpc = defaultValue == null ? false : defaultValue; + JsonNode jsonNode = config.get("publicVpc"); + if(jsonNode == null) { + ((ObjectNode)config).put("publicVpc", publicVpc); + } else { + publicVpc = jsonNode.booleanValue(); + } + return publicVpc; + } + + private String getLaunchType(JsonNode config, String defaultValue) { + String launchType = defaultValue == null ? "EC2" : defaultValue; + JsonNode jsonNode = config.get("launchType"); + if(jsonNode == null) { + ((ObjectNode)config).put("launchType", launchType); + } else { + launchType = jsonNode.asText(); + } + return launchType; + } + + private String getRegion(JsonNode config, String defaultValue) { + String region = defaultValue == null ? "us-east-1" : defaultValue; + JsonNode jsonNode = config.get("region"); + if(jsonNode == null) { + ((ObjectNode)config).put("region", region); + } else { + region = jsonNode.asText(); + } + return region; + } + + ModelCallback callback = new ModelCallback() { + @Override + public void callback(String targetPath, String modelPackage, String modelFileName, String enumsIfClass, String parentClassName, String classVarName, boolean abstractIfClass, List> props, List> parentClassProps) { + try { + transfer(targetPath, + ("src.main.java." + modelPackage).replace(".", separator), + modelFileName + ".java", + enumsIfClass == null + ? templates.rest.pojo.template(modelPackage, modelFileName, parentClassName, classVarName, abstractIfClass, props, parentClassProps) + : templates.rest.enumClass.template(modelPackage, modelFileName, enumsIfClass)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + }; + } diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java index c0c7b4892..e6160b51d 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java @@ -1,9 +1,7 @@ package com.networknt.codegen.rest; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.networknt.codegen.Generator; -import com.networknt.codegen.Utils; import com.networknt.config.JsonMapper; import com.networknt.jsonoverlay.Overlay; import com.networknt.oas.OpenApiParser; @@ -11,7 +9,6 @@ import com.networknt.oas.model.impl.OpenApi3Impl; import com.networknt.utility.StringUtils; -import javax.lang.model.SourceVersion; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -19,7 +16,6 @@ import java.net.URL; import java.nio.file.Paths; import java.util.*; -import java.util.stream.Collectors; import static java.io.File.separator; @@ -31,8 +27,6 @@ public String getFramework() { return FRAMEWORK; } - boolean enableParamDescription = false; - public OpenApiLightGenerator() { } @@ -182,7 +176,7 @@ public void generate(final String targetPath, Object model, JsonNode config) thr ArrayList modelCreators = new ArrayList<>(); final HashMap references = new HashMap<>(); for (Map.Entry entry : schemas.entrySet()) { - loadModel(entry.getKey(), null, (Map)entry.getValue(), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, null); + loadModel(entry.getKey(), null, (Map)entry.getValue(), schemas, overwriteModel, targetPath, modelPackage, modelCreators, references, null, callback); } for (Runnable r : modelCreators) { @@ -251,4 +245,20 @@ public void generate(final String targetPath, Object model, JsonNode config) thr } } + ModelCallback callback = new ModelCallback() { + @Override + public void callback(String targetPath, String modelPackage, String modelFileName, String enumsIfClass, String parentClassName, String classVarName, boolean abstractIfClass, List> props, List> parentClassProps) { + try { + transfer(targetPath, + ("src.main.java." + modelPackage).replace(".", separator), + modelFileName + ".java", + enumsIfClass == null + ? templates.rest.pojo.template(modelPackage, modelFileName, parentClassName, classVarName, abstractIfClass, props, parentClassProps) + : templates.rest.enumClass.template(modelPackage, modelFileName, enumsIfClass)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + }; + } diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpecGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpecGenerator.java index 2330a5ac3..ab00b8990 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpecGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiSpecGenerator.java @@ -2,10 +2,19 @@ import com.fasterxml.jackson.databind.JsonNode; import com.networknt.codegen.Generator; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class OpenApiSpecGenerator implements Generator { private static final Logger logger = LoggerFactory.getLogger(OpenApiSpecGenerator.class); @@ -20,7 +29,6 @@ public String getFramework() { @SuppressWarnings("rawtypes") @Override public void generate(String targetPath, Object model, JsonNode config) throws IOException { - } } diff --git a/light-rest-4j/src/main/resources/templates/lambda/proxy/handlerYml.rocker.raw b/light-rest-4j/src/main/resources/templates/lambda/proxy/handlerYml.rocker.raw index 9a7e64f50..108042e2f 100644 --- a/light-rest-4j/src/main/resources/templates/lambda/proxy/handlerYml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/lambda/proxy/handlerYml.rocker.raw @@ -1,6 +1,6 @@ @import java.util.Map @import java.util.List -@args (String serviceId, String handlerPackage, List> items, boolean prometheusMetrics, boolean healthCheck, boolean serverInfo) +@args (String serviceId, String handlerPackage, List> items, boolean prometheusMetrics) # Handler middleware chain configuration #---------------------------------------- @@ -89,18 +89,18 @@ paths: exec: - default } -@if(healthCheck){ - path: '/health/${server.serviceId:@serviceId}' + - path: '/health/${server.serviceId:@serviceId}' method: 'get' exec: - security - health -} -@if(serverInfo){ - path: '/server/info' + + - path: '/server/info' method: 'get' exec: - security - info -} + - path: '/logger' method: 'get' exec: diff --git a/light-rest-4j/src/main/resources/templates/restkotlin/openapi/handlerYml.rocker.raw b/light-rest-4j/src/main/resources/templates/restkotlin/openapi/handlerYml.rocker.raw index b947b8a06..e9e07755a 100644 --- a/light-rest-4j/src/main/resources/templates/restkotlin/openapi/handlerYml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/restkotlin/openapi/handlerYml.rocker.raw @@ -1,6 +1,6 @@ @import java.util.Map @import java.util.List -@args (String serviceId, String handlerPackage, List> items, boolean prometheusMetrics, boolean healthCheck, boolean serverInfo) +@args (String serviceId, String handlerPackage, List> items, boolean prometheusMetrics) # Handler middleware chain configuration --- @@ -79,16 +79,16 @@ paths: - default - @with (p = handlerPackage + ".") {@p}@item.get("handlerName") } -@if(healthCheck){ - path: '/health/${server.serviceId:@serviceId}' + - path: '/health/${server.serviceId:@serviceId}' method: 'get' exec: - health -} -@if(serverInfo){ - path: '/server/info' + + - path: '/server/info' method: 'get' exec: - info -} + @if(prometheusMetrics){ - path: '/prometheus' method: 'get' exec: diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java index efe6129e8..0497a7175 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java @@ -6,13 +6,11 @@ import org.junit.BeforeClass; import org.junit.Test; -import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; -import java.util.Scanner; public class OpenApiKotlinGeneratorTest { public static String targetPath = "/tmp/openapikotlin"; @@ -50,9 +48,10 @@ public void testGeneratorYaml() throws IOException { @Test public void testGetOperationList() throws IOException { - String anyModel = new Scanner(OpenApiKotlinGeneratorTest.class.getResourceAsStream(openapiYaml), "UTF-8").useDelimiter("\\A").next(); + JsonNode model = Generator.yamlMapper.readTree(OpenApiKotlinGeneratorTest.class.getResourceAsStream(openapiYaml)); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); OpenApiKotlinGenerator generator = new OpenApiKotlinGenerator(); - List list = generator.getOperationList(anyModel); + List list = generator.getOperationList(model, config); System.out.println(list); } diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLambdaGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLambdaGeneratorTest.java index 50238871d..d213fa537 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLambdaGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLambdaGeneratorTest.java @@ -68,8 +68,9 @@ public void testProxyMavenGeneratorYaml() throws IOException { @Test public void testGetOperationList() throws IOException { JsonNode model = Generator.jsonMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(openapiJson)); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLambdaGeneratorTest.class.getResourceAsStream(configProxyLambda)); OpenApiLambdaGenerator generator = new OpenApiLambdaGenerator(); - List list = generator.getOperationList(model); + List list = generator.getOperationList(model, config); System.out.println(list); } diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLightGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLightGeneratorTest.java index 36deb89fe..1c722547f 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLightGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiLightGeneratorTest.java @@ -46,10 +46,10 @@ public static void tearDown() throws IOException { @Test public void testGeneratorJson() throws IOException { - JsonNode configNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); - JsonNode modelNode = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(openapiJson)); + JsonNode config = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(configName)); + JsonNode model = Generator.jsonMapper.readTree(OpenApiLightGeneratorTest.class.getResourceAsStream(openapiJson)); OpenApiLightGenerator generator = new OpenApiLightGenerator(); - generator.generate(targetPath, modelNode, configNode); + generator.generate(targetPath, model, config); } @Test diff --git a/light-rest-4j/src/test/resources/configProxyLambda.json b/light-rest-4j/src/test/resources/configProxyLambda.json index 03e1a338c..dc51f0c95 100644 --- a/light-rest-4j/src/test/resources/configProxyLambda.json +++ b/light-rest-4j/src/test/resources/configProxyLambda.json @@ -15,5 +15,6 @@ "overwriteHandlerTest": true, "overwriteModel": true, "enableRegistry": false, + "enableParamDescription": false, "generateModelOnly": false } From 852e9108b8ee36ebbe8c278d74273d9d24af2314 Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Wed, 17 Feb 2021 22:36:59 -0500 Subject: [PATCH 4/7] fixes #528 sync the codegen with the latest petstore in example --- .../codegen/rest/OpenApiLightGenerator.java | 2 ++ .../rest/openapi/handlerYml.rocker.raw | 21 ++++++++++++++-- .../templates/rest/openapi/pom.xml.rocker.raw | 7 +++++- .../rest/openapi/service.yml.rocker.raw | 16 ++++++++++++ .../rest/portalRegistryYml.rocker.raw | 25 +++++++++++++++++++ 5 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 light-rest-4j/src/main/resources/templates/rest/portalRegistryYml.rocker.raw diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java index e6160b51d..1eafce069 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLightGenerator.java @@ -154,6 +154,8 @@ public void generate(final String targetPath, Object model, JsonNode config) thr transfer(targetPath, ("src.main.resources.config").replace(".", separator), "app-status.yml", templates.rest.appStatusYml.template()); // values.yml file, transfer to suppress the warning message during start startup and encourage usage. transfer(targetPath, ("src.main.resources.config").replace(".", separator), "values.yml", templates.rest.openapi.values.template()); + // add portal-registry.yml + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "portal-registry.yml", templates.rest.portalRegistryYml.template()); } // routing handler transfer(targetPath, ("src.main.resources.config").replace(".", separator), "handler.yml", diff --git a/light-rest-4j/src/main/resources/templates/rest/openapi/handlerYml.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/openapi/handlerYml.rocker.raw index bcc226a29..9da656a20 100644 --- a/light-rest-4j/src/main/resources/templates/rest/openapi/handlerYml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/openapi/handlerYml.rocker.raw @@ -60,6 +60,8 @@ handlers: # Framework endpoint handlers - com.networknt.health.HealthGetHandler@@health - com.networknt.info.ServerInfoGetHandler@@info + - com.networknt.logging.handler.LoggerGetHandler@@getLogger + - com.networknt.logging.handler.LoggerPostHandler@@postLogger - com.networknt.specification.SpecDisplayHandler@@spec - com.networknt.specification.SpecSwaggerUIHandler@@swaggerui # - com.networknt.metrics.prometheus.PrometheusGetHandler@@getprometheus @@ -73,10 +75,10 @@ chains: @if(prometheusMetrics){ - prometheus} else { - metrics} - traceability - correlation - - body - - audit - specification - security + - body + - audit # - dump - sanitizer - validator @@ -91,13 +93,28 @@ paths: - path: '/health/${server.serviceId:@serviceId}' method: 'get' exec: + - security - health - path: '/server/info' method: 'get' exec: + - security - info + - path: '/logger' + method: 'get' + exec: + - security + - getLogger + + - path: '/logger' + method: 'post' + exec: + - security + - body + - postLogger + @if(prometheusMetrics){ - path: '/prometheus' method: 'get' exec: diff --git a/light-rest-4j/src/main/resources/templates/rest/openapi/pom.xml.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/openapi/pom.xml.rocker.raw index 44859752f..093e39f4b 100644 --- a/light-rest-4j/src/main/resources/templates/rest/openapi/pom.xml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/openapi/pom.xml.rocker.raw @@ -168,7 +168,12 @@ com.networknt - consul + portal-registry + ${version.light-4j} + + + com.networknt + logger-config ${version.light-4j} diff --git a/light-rest-4j/src/main/resources/templates/rest/openapi/service.yml.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/openapi/service.yml.rocker.raw index a1695832e..6587418de 100644 --- a/light-rest-4j/src/main/resources/templates/rest/openapi/service.yml.rocker.raw +++ b/light-rest-4j/src/main/resources/templates/rest/openapi/service.yml.rocker.raw @@ -2,6 +2,22 @@ @args (JsonNode config) # Singleton service factory configuration/IoC injection singletons: +- com.networknt.registry.URL: + - com.networknt.registry.URLImpl: + protocol: light + host: localhost + port: 8080 + path: portal + parameters: + registryRetryPeriod: '30000' +- com.networknt.portal.registry.client.PortalRegistryClient: + - com.networknt.portal.registry.client.PortalRegistryClientImpl +- com.networknt.registry.Registry: + - com.networknt.portal.registry.PortalRegistry +- com.networknt.balance.LoadBalance: + - com.networknt.balance.RoundRobinLoadBalance +- com.networknt.cluster.Cluster: + - com.networknt.cluster.LightCluster # StartupHookProvider implementations, there are one to many and they are called in the same sequence defined. - com.networknt.server.StartupHookProvider: @if(config.get("kafkaProducer").booleanValue()){ diff --git a/light-rest-4j/src/main/resources/templates/rest/portalRegistryYml.rocker.raw b/light-rest-4j/src/main/resources/templates/rest/portalRegistryYml.rocker.raw new file mode 100644 index 000000000..b67153848 --- /dev/null +++ b/light-rest-4j/src/main/resources/templates/rest/portalRegistryYml.rocker.raw @@ -0,0 +1,25 @@ +# Portal URL for accessing controller API. Default to lightapi.net public portal and it can be point to a standalone +# light-controller instance for testing in the same Kubernetes cluster or docker-compose. +portalUrl: ${portalRegistry.portalUrl:https://localhost:8438} +# number of requests before resetting the shared connection to work around HTTP/2 limitation +maxReqPerConn: ${portalRegistry.maxReqPerConn:1000000} +# De-register the service after the amount of time with health check failed. Once a health check is failed, the +# service will be put into a critical state. After the deregisterAfter, the service will be removed from discovery. +# the value is an integer in milliseconds. 1000 means 1 second and default to 2 minutes +deregisterAfter: ${portalRegistry.deregisterAfter:120000} +# health check interval for HTTP check. Or it will be the TTL for TTL check. Every 10 seconds, an HTTP check +# request will be sent from the light-portal controller. Or if there is no heartbeat TTL request from service +# after 10 seconds, then mark the service is critical. The value is an integer in milliseconds +checkInterval: ${portalRegistry.checkInterval:10000} +# enable health check HTTP. An HTTP get request will be sent to the service to ensure that 200 response status is +# coming back. This is suitable for service that depending on the database or other infrastructure services. You should +# implement a customized health check handler that checks dependencies. i.e. if DB is down, return status 400. This +# is the recommended configuration that allows the light-portal controller to poll the health info from each service. +httpCheck: ${portalRegistry.httpCheck:true} +# enable health check TTL. When this is enabled, The light-portal controller won't actively check your service to +# ensure it is healthy, but your service will call check endpoint with a heartbeat to indicate it is alive. This +# requires that the service is built on top of light-4j, and the HTTP check is not available. For example, your service +# is behind NAT. If you are running the service within your internal network and using the SaaS lightapi.net portal, +# this is the only option as our portal controller cannot access your internal service to perform a health check. +# We recommend deploying light-portal internally if you are running services within an internal network for efficiency. +ttlCheck: ${portalRegistry.ttlCheck:false} From 9c6b63be1e73d338c1af5b6c7ae29d65ff81efdc Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Wed, 17 Feb 2021 23:07:26 -0500 Subject: [PATCH 5/7] fixes #528 compare between old and new for GraphqlGeneratorTest --- .../main/java/com/networknt/codegen/Generator.java | 12 ++++++------ .../networknt/codegen/graphql/GraphqlGenerator.java | 10 +++++----- light-graphql-4j/src/test/resources/config.json | 2 +- .../networknt/codegen/hybrid/HybridGenerator.java | 2 +- .../codegen/rest/OpenApiLambdaGenerator.java | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/codegen-core/src/main/java/com/networknt/codegen/Generator.java b/codegen-core/src/main/java/com/networknt/codegen/Generator.java index 777a1c1a7..7e800d8fd 100644 --- a/codegen-core/src/main/java/com/networknt/codegen/Generator.java +++ b/codegen-core/src/main/java/com/networknt/codegen/Generator.java @@ -361,7 +361,7 @@ default String getDockerOrganization(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("dockerOrganization", dockerOrganization); } else { - dockerOrganization = jsonNode.asText(); + dockerOrganization = jsonNode.textValue(); } return dockerOrganization; } @@ -372,7 +372,7 @@ default String getVersion(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("version", version); } else { - version = jsonNode.asText(); + version = jsonNode.textValue(); } return version; } @@ -383,7 +383,7 @@ default String getGroupId(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("groupId", groupId); } else { - groupId = jsonNode.asText(); + groupId = jsonNode.textValue(); } return groupId; } @@ -394,7 +394,7 @@ default String getArtifactId(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("artifactId", artifactId); } else { - artifactId = jsonNode.asText(); + artifactId = jsonNode.textValue(); } return artifactId; } @@ -471,7 +471,7 @@ default String getKafkaTopic(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("kafkaTopic", kafkaTopic); } else { - kafkaTopic = jsonNode.asText(); + kafkaTopic = jsonNode.textValue(); } return kafkaTopic; } @@ -482,7 +482,7 @@ default String getDecryptOption(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("decryptOption", decryptOption); } else { - decryptOption = jsonNode.asText(); + decryptOption = jsonNode.textValue(); } return decryptOption; } diff --git a/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java b/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java index ca9acac88..e9a4b506d 100644 --- a/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java +++ b/light-graphql-4j/src/main/java/com/networknt/codegen/graphql/GraphqlGenerator.java @@ -66,7 +66,7 @@ public void generate(String targetPath, Object schema, JsonNode config) throws I } transfer(targetPath, "docker", "Dockerfile", templates.graphql.dockerfile.template(config, expose)); transfer(targetPath, "docker", "Dockerfile-Slim", templates.graphql.dockerfileslim.template(config, expose)); - transfer(targetPath, "", "build.sh", templates.graphql.buildSh.template(dockerOrganization, config.get("groupId") + "." + config.get("artifactId") + "-" + config.get("version"))); + transfer(targetPath, "", "build.sh", templates.graphql.buildSh.template(dockerOrganization, serviceId)); transfer(targetPath, "", ".gitignore", templates.graphql.gitignore.template()); transfer(targetPath, "", "README.md", templates.graphql.README.template()); transfer(targetPath, "", "LICENSE", templates.graphql.LICENSE.template()); @@ -77,8 +77,8 @@ public void generate(String targetPath, Object schema, JsonNode config) throws I // config transfer(targetPath, ("src.main.resources.config").replace(".", separator), "service.yml", templates.graphql.serviceYml.template(config)); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "server.yml", templates.graphql.serverYml.template(config.get("groupId") + "." + config.get("artifactId") + "-" + config.get("version"), enableHttp, httpPort, enableHttps, httpsPort, enableHttp2, enableRegistry, version)); - transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", templates.graphql.serverYml.template(config.get("groupId") + "." + config.get("artifactId") + "-" + config.get("version"), enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "server.yml", templates.graphql.serverYml.template(serviceId, enableHttp, httpPort, enableHttps, httpsPort, enableHttp2, enableRegistry, version)); + transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", templates.graphql.serverYml.template(serviceId, enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); transfer(targetPath, ("src.main.resources.config").replace(".", separator), "graphql-security.yml", templates.graphql.securityYml.template()); transfer(targetPath, ("src.main.resources.config").replace(".", separator), "graphql-validator.yml", templates.graphql.validatorYml.template()); if(supportClient) { @@ -152,7 +152,7 @@ private String getSchemaPackage(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("schemaPackage", schemaPackage); } else { - schemaPackage = jsonNode.asText(); + schemaPackage = jsonNode.textValue(); } return schemaPackage; } @@ -163,7 +163,7 @@ private String getSchemaClass(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("schemaClass", schemaClass); } else { - schemaClass = jsonNode.asText(); + schemaClass = jsonNode.textValue(); } return schemaClass; } diff --git a/light-graphql-4j/src/test/resources/config.json b/light-graphql-4j/src/test/resources/config.json index 73d7d9fde..07f920cd0 100644 --- a/light-graphql-4j/src/test/resources/config.json +++ b/light-graphql-4j/src/test/resources/config.json @@ -10,7 +10,7 @@ "enableHttp": true, "httpsPort": 8443, "enableHttps": false, - "enableHttp2": true, + "enableHttp2": false, "enableRegistry": false, "supportDb": true, "eclipseIDE": false, diff --git a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridGenerator.java b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridGenerator.java index c458b90de..2debc07f7 100644 --- a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridGenerator.java +++ b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridGenerator.java @@ -11,7 +11,7 @@ default String getJsonPath(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("jsonPath", jsonPath); } else { - jsonPath = jsonNode.asText(); + jsonPath = jsonNode.textValue(); } return jsonPath; } diff --git a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java index 4b66ef8fc..1d35e2883 100644 --- a/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java +++ b/light-rest-4j/src/main/java/com/networknt/codegen/rest/OpenApiLambdaGenerator.java @@ -387,7 +387,7 @@ private String getLaunchType(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("launchType", launchType); } else { - launchType = jsonNode.asText(); + launchType = jsonNode.textValue(); } return launchType; } @@ -398,7 +398,7 @@ private String getRegion(JsonNode config, String defaultValue) { if(jsonNode == null) { ((ObjectNode)config).put("region", region); } else { - region = jsonNode.asText(); + region = jsonNode.textValue(); } return region; } From 4984eae26d13d232e6fa52b09d65236fd19607ae Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Wed, 17 Feb 2021 23:28:28 -0500 Subject: [PATCH 6/7] fixes #528 compare the generated projects for hybrid test cases --- .../codegen/hybrid/HybridServerGenerator.java | 4 ++-- .../codegen/hybrid/HybridServiceGenerator.java | 12 ++++++------ light-hybrid-4j/src/test/resources/serverConfig.json | 1 + .../src/test/resources/serviceConfig.json | 1 + 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServerGenerator.java b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServerGenerator.java index 5dc58d913..3e97c9dcc 100644 --- a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServerGenerator.java +++ b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServerGenerator.java @@ -80,8 +80,8 @@ public void generate(String targetPath, Object model, JsonNode config) throws IO // config transfer(targetPath, ("src.main.resources.config").replace(".", separator), "service.yml", templates.hybrid.serviceYml.template(config)); - transfer(targetPath, ("src.main.resources.config").replace(".", separator), "server.yml", templates.hybrid.serverYml.template(config.get("groupId") + "." + config.get("artifactId") + "-" + config.get("version"), enableHttp, httpPort, enableHttps, httpsPort, enableHttp2, enableRegistry, version)); - transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", templates.hybrid.serverYml.template(config.get("groupId") + "." + config.get("artifactId") + "-" + config.get("version"), enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); + transfer(targetPath, ("src.main.resources.config").replace(".", separator), "server.yml", templates.hybrid.serverYml.template(serviceId, enableHttp, httpPort, enableHttps, httpsPort, enableHttp2, enableRegistry, version)); + transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", templates.hybrid.serverYml.template(serviceId, enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); if(kafkaProducer) { transfer(targetPath, ("src.main.resources.config").replace(".", separator), "kafka-producer.yml", templates.hybrid.kafkaProducerYml.template(kafkaTopic)); diff --git a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServiceGenerator.java b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServiceGenerator.java index 63dc80ee9..b8e61b4d0 100644 --- a/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServiceGenerator.java +++ b/light-hybrid-4j/src/main/java/com/networknt/codegen/hybrid/HybridServiceGenerator.java @@ -77,7 +77,7 @@ public void generate(String targetPath, Object model, JsonNode config) throws IO // config transfer(targetPath, ("src.test.resources.config").replace(".", separator), "service.yml", templates.hybrid.serviceYml.template(config)); - transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", templates.hybrid.serverYml.template(config.get("groupId") + "." + config.get("artifactId") + "-" + config.get("version"), enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); + transfer(targetPath, ("src.test.resources.config").replace(".", separator), "server.yml", templates.hybrid.serverYml.template(serviceId, enableHttp, "49587", enableHttps, "49588", enableHttp2, enableRegistry, version)); //transfer(targetPath, ("src.test.resources.config").replace(".", separator), "secret.yml", templates.hybrid.secretYml.template()); transfer(targetPath, ("src.test.resources.config").replace(".", separator), "hybrid-security.yml", templates.hybrid.securityYml.template()); transfer(targetPath, ("src.test.resources.config").replace(".", separator), "client.yml", templates.hybrid.clientYml.template()); @@ -112,10 +112,10 @@ public void generate(String targetPath, Object model, JsonNode config) throws IO for(JsonNode item : items) { JsonNode any = item.get("example"); String example = any != null ? StringEscapeUtils.escapeJson(any.toString()).trim() : ""; - if(!overwriteHandler && checkExist(targetPath, ("src.main.java." + handlerPackage).replace(".", separator), item.get("handler") + ".java")) { + if(!overwriteHandler && checkExist(targetPath, ("src.main.java." + handlerPackage).replace(".", separator), item.get("handler").textValue() + ".java")) { continue; } - transfer(targetPath, ("src.main.java." + handlerPackage).replace(".", separator), item.get("handler") + ".java", templates.hybrid.handler.template(handlerPackage, host, service, item, example)); + transfer(targetPath, ("src.main.java." + handlerPackage).replace(".", separator), item.get("handler").textValue() + ".java", templates.hybrid.handler.template(handlerPackage, host, service, item, example)); String sId = host + "/" + service + "/" + item.get("name") + "/" + item.get("version"); Map map = new HashMap<>(); map.put("schema", item.get("schema")); @@ -131,10 +131,10 @@ public void generate(String targetPath, Object model, JsonNode config) throws IO // handler test cases transfer(targetPath, ("src.test.java." + handlerPackage + ".").replace(".", separator), "TestServer.java", templates.hybrid.testServer.template(handlerPackage)); for(JsonNode item : items) { - if(!overwriteHandlerTest && checkExist(targetPath, ("src.test.java." + handlerPackage).replace(".", separator), item.get("handler") + "Test.java")) { + if(!overwriteHandlerTest && checkExist(targetPath, ("src.test.java." + handlerPackage).replace(".", separator), item.get("handler").textValue() + "Test.java")) { continue; } - transfer(targetPath, ("src.test.java." + handlerPackage).replace(".", separator), item.get("handler") + "Test.java", templates.hybrid.handlerTest.template(handlerPackage, host, service, item)); + transfer(targetPath, ("src.test.java." + handlerPackage).replace(".", separator), item.get("handler").textValue() + "Test.java", templates.hybrid.handlerTest.template(handlerPackage, host, service, item)); } } @@ -164,7 +164,7 @@ public void generate(String targetPath, Object model, JsonNode config) throws IO templates.hybrid.rpcRouterYml.template(handlerPackage, jsonPath)); // write the generated schema into the config folder for schema validation. - try (InputStream is = new ByteArrayInputStream(Generator.jsonMapper.writeValueAsBytes(services))) { + try (InputStream is = new ByteArrayInputStream(Generator.jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(services))) { Generator.copyFile(is, Paths.get(targetPath, ("src.main.resources").replace(".", separator), "schema.json")); } } diff --git a/light-hybrid-4j/src/test/resources/serverConfig.json b/light-hybrid-4j/src/test/resources/serverConfig.json index e9adc11e6..5a7ed9342 100644 --- a/light-hybrid-4j/src/test/resources/serverConfig.json +++ b/light-hybrid-4j/src/test/resources/serverConfig.json @@ -10,6 +10,7 @@ "enableHttp": true, "httpsPort": 8443, "enableHttps": false, + "enableHttp2": false, "enableRegistry": false, "supportDb": true, "dbInfo": { diff --git a/light-hybrid-4j/src/test/resources/serviceConfig.json b/light-hybrid-4j/src/test/resources/serviceConfig.json index f59fff7fc..35ba6c4d3 100644 --- a/light-hybrid-4j/src/test/resources/serviceConfig.json +++ b/light-hybrid-4j/src/test/resources/serviceConfig.json @@ -12,6 +12,7 @@ "enableHttp": true, "httpsPort": 8443, "enableHttps": false, + "enableHttp2": false, "enableRegistry": false, "supportDb": true, "dbInfo": { From 22e251a3066cc8e0c84c7d346f929a638e47fb38 Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Thu, 18 Feb 2021 10:52:09 -0500 Subject: [PATCH 7/7] fixes #528 update the lambda config --- .../java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java | 1 - light-rest-4j/src/test/resources/configlambda.json | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java index 0497a7175..e6747701a 100644 --- a/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java +++ b/light-rest-4j/src/test/java/com/networknt/codegen/OpenApiKotlinGeneratorTest.java @@ -15,7 +15,6 @@ public class OpenApiKotlinGeneratorTest { public static String targetPath = "/tmp/openapikotlin"; public static String configName = "/config.json"; - public static String openapiJson = "/openapi.json"; public static String openapiYaml = "/openapi.yaml"; public static String openapiNoServersYaml = "/openapi-noServers.yaml"; diff --git a/light-rest-4j/src/test/resources/configlambda.json b/light-rest-4j/src/test/resources/configlambda.json index 9abfaf177..4fd88cd14 100644 --- a/light-rest-4j/src/test/resources/configlambda.json +++ b/light-rest-4j/src/test/resources/configlambda.json @@ -1,11 +1,12 @@ { - "projectName": "petstore", + "artifactId": "petstore", "version": "1.0.1", "groupId": "com.networknt", "rootPackage": "com.networknt.petstore", "handlerPackage":"com.networknt.petstore.handler", "modelPackage":"com.networknt.petstore.model", "packageDocker": false, + "useLightProxy": false, "overwriteHandler": true, "overwriteHandlerTest": true, "overwriteModel": true,