From 7fbc8fa31e093f8c9b7b865891e51e15985f3091 Mon Sep 17 00:00:00 2001 From: wing328 Date: Mon, 26 Mar 2018 22:39:27 +0800 Subject: [PATCH] add haskell generator --- .../openapitools/codegen/DefaultCodegen.java | 10 +- .../languages/HaskellServantCodegen.java | 563 ++++++++++++++++++ .../org.openapitools.codegen.CodegenConfig | 1 + .../haskellservant/HaskellModelTest.java | 25 + .../HaskellServantOptionsTest.java | 37 ++ .../codegen/php/PhpModelTest.java | 2 +- .../HaskellServantOptionsProvider.java | 39 ++ 7 files changed, 671 insertions(+), 6 deletions(-) create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/haskellservant/HaskellModelTest.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/haskellservant/HaskellServantOptionsTest.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/options/HaskellServantOptionsProvider.java diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 72b218ddb4..460bd95015 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -1097,6 +1097,7 @@ public class DefaultCodegen implements CodegenConfig { **/ @SuppressWarnings("static-method") public String getSchemaType(Schema schema) { + // datatype is the OAI type (e.g. integer, long, etc) String datatype = null; if (StringUtils.isNotBlank(schema.get$ref())) { // object @@ -1141,7 +1142,7 @@ public class DefaultCodegen implements CodegenConfig { if (SchemaTypeUtil.INTEGER64_FORMAT.equals(schema.getFormat())) { datatype = "long"; } else { - datatype = schema.getType(); + datatype = schema.getType(); // integer } } else if (schema instanceof MapSchema) { datatype = "map"; @@ -1565,7 +1566,6 @@ public class DefaultCodegen implements CodegenConfig { } String type = getSchemaType(p); - //LOGGER.info("from property = " + p); if (p instanceof IntegerSchema || SchemaTypeUtil.INTEGER_TYPE.equals(p.getType())) { property.isNumeric = Boolean.TRUE; if (SchemaTypeUtil.INTEGER64_FORMAT.equals(p.getFormat())) { @@ -1718,6 +1718,7 @@ public class DefaultCodegen implements CodegenConfig { property.allowableValues = allowableValues; } } + property.datatype = getTypeDeclaration(p); property.dataFormat = p.getFormat(); @@ -1978,7 +1979,7 @@ public class DefaultCodegen implements CodegenConfig { Operation operation, Map schemas, OpenAPI openAPI) { - //LOGGER.info("fromOperation => operation: " + operation); + LOGGER.debug("fromOperation => operation: " + operation); CodegenOperation op = CodegenModelFactory.newInstance(CodegenModelType.OPERATION); Set imports = new HashSet(); if (operation.getExtensions() != null && !operation.getExtensions().isEmpty()) { @@ -2191,9 +2192,7 @@ public class DefaultCodegen implements CodegenConfig { // add imports to operation import tag for (String i : imports) { - LOGGER.info("debugging fromOperation imports: " + i); if (needToImport(i)) { - LOGGER.info("debugging fromOperation imports: " + i + " imported"); op.imports.add(i); } } @@ -2671,6 +2670,7 @@ public class DefaultCodegen implements CodegenConfig { setParameterExampleValue(codegenParameter); postProcessParameter(codegenParameter); + LOGGER.info("debugging codegenParameter return: " + codegenParameter); return codegenParameter; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java new file mode 100644 index 0000000000..6ddcece0c2 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/HaskellServantCodegen.java @@ -0,0 +1,563 @@ +package org.openapitools.codegen.languages; + +import org.apache.commons.lang3.StringUtils; +import org.openapitools.codegen.CliOption; +import org.openapitools.codegen.CodegenConfig; +import org.openapitools.codegen.CodegenConstants; +import org.openapitools.codegen.CodegenModel; +import org.openapitools.codegen.CodegenOperation; +import org.openapitools.codegen.CodegenParameter; +import org.openapitools.codegen.CodegenProperty; +import org.openapitools.codegen.CodegenType; +import org.openapitools.codegen.DefaultCodegen; +import org.openapitools.codegen.SupportingFile; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.media.*; + +import java.util.*; +import java.util.regex.Pattern; + +public class HaskellServantCodegen extends DefaultCodegen implements CodegenConfig { + + // source folder where to write the files + protected String sourceFolder = "src"; + protected String apiVersion = "0.0.1"; + private static final Pattern LEADING_UNDERSCORE = Pattern.compile("^_+"); + + /** + * Configures the type of generator. + * + * @return the CodegenType for this generator + */ + public CodegenType getTag() { + return CodegenType.SERVER; + } + + /** + * Configures a friendly name for the generator. This will be used by the generator + * to select the library with the -l flag. + * + * @return the friendly name for the generator + */ + public String getName() { + return "haskell"; + } + + /** + * Returns human-friendly help for the generator. Provide the consumer with help + * tips, parameters here + * + * @return A string value for the help message + */ + public String getHelp() { + return "Generates a Haskell server and client library."; + } + + public HaskellServantCodegen() { + super(); + + // override the mapping to keep the original mapping in Haskell + specialCharReplacements.put("-", "Dash"); + specialCharReplacements.put(">", "GreaterThan"); + specialCharReplacements.put("<", "LessThan"); + + // backslash and double quote need double the escapement for both Java and Haskell + specialCharReplacements.remove("\\"); + specialCharReplacements.remove("\""); + specialCharReplacements.put("\\\\", "Back_Slash"); + specialCharReplacements.put("\\\"", "Double_Quote"); + + // set the output folder here + outputFolder = "generated-code/haskell-servant"; + + /* + * Template Location. This is the location which templates will be read from. The generator + * will use the resource stream to attempt to read the templates. + */ + embeddedTemplateDir = templateDir = "haskell-servant"; + + /* + * Api Package. Optional, if needed, this can be used in templates + */ + apiPackage = "API"; + + /* + * Model Package. Optional, if needed, this can be used in templates + */ + modelPackage = "Types"; + + // Haskell keywords and reserved function names, taken mostly from https://wiki.haskell.org/Keywords + setReservedWordsLowerCase( + Arrays.asList( + // Keywords + "as", "case", "of", + "class", "data", "family", + "default", "deriving", + "do", "forall", "foreign", "hiding", + "if", "then", "else", + "import", "infix", "infixl", "infixr", + "instance", "let", "in", + "mdo", "module", "newtype", + "proc", "qualified", "rec", + "type", "where" + ) + ); + + /* + * Additional Properties. These values can be passed to the templates and + * are available in models, apis, and supporting files + */ + additionalProperties.put("apiVersion", apiVersion); + + /* + * Supporting Files. You can write single files for the generator with the + * entire object tree available. If the input file has a suffix of `.mustache + * it will be processed by the template engine. Otherwise, it will be copied + */ + supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); + supportingFiles.add(new SupportingFile("stack.mustache", "", "stack.yaml")); + supportingFiles.add(new SupportingFile("Setup.mustache", "", "Setup.hs")); + + /* + * Language Specific Primitives. These types will not trigger imports by + * the client generator + */ + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "Bool", + "String", + "Int", + "Integer", + "Float", + "Char", + "Double", + "List", + "FilePath" + ) + ); + + typeMapping.clear(); + typeMapping.put("array", "List"); + typeMapping.put("set", "Set"); + typeMapping.put("boolean", "Bool"); + typeMapping.put("string", "Text"); + typeMapping.put("integer", "Int"); + typeMapping.put("long", "Integer"); + typeMapping.put("short", "Int"); + typeMapping.put("char", "Char"); + typeMapping.put("float", "Float"); + typeMapping.put("double", "Double"); + typeMapping.put("DateTime", "Integer"); + typeMapping.put("file", "FilePath"); + typeMapping.put("number", "Double"); + typeMapping.put("any", "Value"); + typeMapping.put("UUID", "Text"); + typeMapping.put("ByteArray", "Text"); + typeMapping.put("object", "Value"); + + importMapping.clear(); + importMapping.put("Map", "qualified Data.Map as Map"); + + cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC)); + cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC)); + } + + /** + * Escapes a reserved word as defined in the `reservedWords` array. Handle escaping + * those terms here. This logic is only called if a variable matches the reserved words + * + * @return the escaped term + */ + @Override + public String escapeReservedWord(String name) { + if(this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return "_" + name; + } + + public String firstLetterToUpper(String word) { + if (word.length() == 0) { + return word; + } else if (word.length() == 1) { + return word.substring(0, 1).toUpperCase(); + } else { + return word.substring(0, 1).toUpperCase() + word.substring(1); + } + } + + public String firstLetterToLower(String word) { + if (word.length() == 0) { + return word; + } else if (word.length() == 1) { + return word.substring(0, 1).toLowerCase(); + } else { + return word.substring(0, 1).toLowerCase() + word.substring(1); + } + } + + @Override + public void preprocessOpenAPI(OpenAPI openAPI) { + // From the title, compute a reasonable name for the package and the API + String title = openAPI.getInfo().getTitle(); + + // Drop any API suffix + if(title == null) { + title = "OpenAPI"; + } else { + title = title.trim(); + if (title.toUpperCase().endsWith("API")) { + title = title.substring(0, title.length() - 3); + } + } + + String[] words = title.split(" "); + + // The package name is made by appending the lowercased words of the title interspersed with dashes + List wordsLower = new ArrayList(); + for (String word : words) { + wordsLower.add(word.toLowerCase()); + } + String cabalName = joinStrings("-", wordsLower); + + // The API name is made by appending the capitalized words of the title + List wordsCaps = new ArrayList(); + for (String word : words) { + wordsCaps.add(firstLetterToUpper(word)); + } + String apiName = joinStrings("", wordsCaps); + + // Set the filenames to write for the API + supportingFiles.add(new SupportingFile("haskell-servant-codegen.mustache", "", cabalName + ".cabal")); + supportingFiles.add(new SupportingFile("API.mustache", "lib/" + apiName, "API.hs")); + supportingFiles.add(new SupportingFile("Types.mustache", "lib/" + apiName, "Types.hs")); + + + additionalProperties.put("title", apiName); + additionalProperties.put("titleLower", firstLetterToLower(apiName)); + additionalProperties.put("package", cabalName); + + // Due to the way servant resolves types, we need a high context stack limit + additionalProperties.put("contextStackLimit", openAPI.getPaths().size() * 2 + 300); + + List> replacements = new ArrayList<>(); + Object[] replacementChars = specialCharReplacements.keySet().toArray(); + for(int i = 0; i < replacementChars.length; i++) { + String c = (String) replacementChars[i]; + Map o = new HashMap<>(); + o.put("char", c); + o.put("replacement", "'" + specialCharReplacements.get(c)); + o.put("hasMore", i != replacementChars.length - 1); + replacements.add(o); + } + additionalProperties.put("specialCharReplacements", replacements); + + super.preprocessOpenAPI(openAPI); + } + + + /** + * Optional - type declaration. This is a String which is used by the templates to instantiate your + * types. There is typically special handling for different property types + * + * @return a string value used as the `dataType` field for model templates, `returnType` for api templates + */ + @Override + public String getTypeDeclaration(Schema p) { + if (p instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) p; + Schema inner = ap.getItems(); + return "[" + getTypeDeclaration(inner) + "]"; + } else if (p instanceof MapSchema) { + MapSchema mp = (MapSchema) p; + Schema inner = (Schema) mp.getAdditionalProperties(); + return "Map.Map String " + getTypeDeclaration(inner); + } + return fixModelChars(super.getTypeDeclaration(p)); + } + + /** + * Optional - OpenAPI type conversion. This is used to map OpenAPI types in a `Schema` into + * either language specific types via `typeMapping` or into complex models if there is not a mapping. + * + * @return a string value of the type or complex model for this property + */ + @Override + public String getSchemaType(Schema p) { + String swaggerType = super.getSchemaType(p); + LOGGER.debug("debugging swager type: " + p.getType() + ", " + p.getFormat() + " => " + swaggerType); + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + return type; + //if (languageSpecificPrimitives.contains(type)) + // return toModelName(type); + } else if(typeMapping.containsValue(swaggerType)) { + // TODO what's this case for? + type = swaggerType + "_"; + } else { + type = swaggerType; + } + // it's a model + return toModelName(type); + } + + @Override + public String toInstantiationType(Schema p) { + if (p instanceof MapSchema) { + MapSchema ap = (MapSchema) p; + Schema additionalProperties2 = (Schema) ap.getAdditionalProperties(); + String type = additionalProperties2.getType(); + if (null == type) { + LOGGER.error("No Type defined for Additional Property " + additionalProperties2 + "\n" // + + "\tIn Property: " + p); + } + String inner = getSchemaType(additionalProperties2); + return "(Map.Map Text " + inner + ")"; + } else if (p instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) p; + String inner = getSchemaType(ap.getItems()); + // Return only the inner type; the wrapping with QueryList is done + // somewhere else, where we have access to the collection format. + return inner; + } else { + return null; + } + } + + + // Intersperse a separator string between a list of strings, like String.join. + private String joinStrings(String sep, List ss) { + StringBuilder sb = new StringBuilder(); + for (String s : ss) { + if (sb.length() > 0) { + sb.append(sep); + } + sb.append(s); + } + return sb.toString(); + } + + // Convert an HTTP path to a Servant route, including captured parameters. + // For example, the path /api/jobs/info/{id}/last would become: + // "api" :> "jobs" :> "info" :> Capture "id" IdType :> "last" + // IdType is provided by the capture params. + private List pathToServantRoute(String path, List pathParams) { + // Map the capture params by their names. + HashMap captureTypes = new HashMap(); + for (CodegenParameter param : pathParams) { + captureTypes.put(param.baseName, param.dataType); + } + + // Cut off the leading slash, if it is present. + if (path.startsWith("/")) { + path = path.substring(1); + } + + // Convert the path into a list of servant route components. + List pathComponents = new ArrayList(); + for (String piece : path.split("/")) { + if (piece.startsWith("{") && piece.endsWith("}")) { + String name = piece.substring(1, piece.length() - 1); + pathComponents.add("Capture \"" + name + "\" " + captureTypes.get(name)); + } else { + pathComponents.add("\"" + piece + "\""); + } + } + + // Intersperse the servant route pieces with :> to construct the final API type + return pathComponents; + } + + // Extract the arguments that are passed in the route path parameters + private List pathToClientType(String path, List pathParams) { + // Map the capture params by their names. + HashMap captureTypes = new HashMap(); + for (CodegenParameter param : pathParams) { + captureTypes.put(param.baseName, param.dataType); + } + + // Cut off the leading slash, if it is present. + if (path.startsWith("/")) { + path = path.substring(1); + } + + // Convert the path into a list of servant route components. + List type = new ArrayList(); + for (String piece : path.split("/")) { + if (piece.startsWith("{") && piece.endsWith("}")) { + String name = piece.substring(1, piece.length() - 1); + type.add(captureTypes.get(name)); + } + } + + return type; + } + + + @Override + public CodegenOperation fromOperation(String resourcePath, String httpMethod, Operation operation, Map definitions, OpenAPI openAPI) { + CodegenOperation op = super.fromOperation(resourcePath, httpMethod, operation, definitions, openAPI); + + List path = pathToServantRoute(op.path, op.pathParams); + List type = pathToClientType(op.path, op.pathParams); + + // Query parameters appended to routes + for (CodegenParameter param : op.queryParams) { + String paramType = param.dataType; + if (param.isListContainer) { + if (StringUtils.isEmpty(param.collectionFormat)) { + param.collectionFormat = "csv"; + } + paramType = makeQueryListType(paramType, param.collectionFormat); + } + path.add("QueryParam \"" + param.baseName + "\" " + paramType); + type.add("Maybe " + param.dataType); + } + + // Either body or form data parameters appended to route + // As far as I know, you cannot have two ReqBody routes. + // Is it possible to have body params AND have form params? + String bodyType = null; + if (op.getHasBodyParam()) { + for (CodegenParameter param : op.bodyParams) { + path.add("ReqBody '[JSON] " + param.dataType); + bodyType = param.dataType; + } + } else if(op.getHasFormParams()) { + // Use the FormX data type, where X is the conglomerate of all things being passed + String formName = "Form" + camelize(op.operationId); + bodyType = formName; + path.add("ReqBody '[FormUrlEncoded] " + formName); + } + + if (bodyType != null) { + type.add(bodyType); + } + + // Special headers appended to route + for (CodegenParameter param : op.headerParams) { + path.add("Header \"" + param.baseName + "\" " + param.dataType); + + String paramType = param.dataType; + if (param.isListContainer) { + if (StringUtils.isEmpty(param.collectionFormat)) { + param.collectionFormat = "csv"; + } + paramType = makeQueryListType(paramType, param.collectionFormat); + } + type.add("Maybe " + paramType); + } + + // store form parameter name in the vendor extensions + for (CodegenParameter param : op.formParams) { + param.vendorExtensions.put("x-formParamName", camelize(param.baseName)); + } + + // Add the HTTP method and return type + String returnType = op.returnType; + if (returnType == null || returnType.equals("null")) { + returnType = "()"; + } + if (returnType.indexOf(" ") >= 0) { + returnType = "(" + returnType + ")"; + } + path.add("Verb '" + op.httpMethod.toUpperCase() + " 200 '[JSON] " + returnType); + type.add("m " + returnType); + + op.vendorExtensions.put("x-routeType", joinStrings(" :> ", path)); + op.vendorExtensions.put("x-clientType", joinStrings(" -> ", type)); + op.vendorExtensions.put("x-formName", "Form" + camelize(op.operationId)); + for(CodegenParameter param : op.formParams) { + param.vendorExtensions.put("x-formPrefix", camelize(op.operationId, true)); + } + return op; + } + + private String makeQueryListType(String type, String collectionFormat) { + type = type.substring(1, type.length() - 1); + switch(collectionFormat) { + case "csv": return "(QueryList 'CommaSeparated (" + type + "))"; + case "tsv": return "(QueryList 'TabSeparated (" + type + "))"; + case "ssv": return "(QueryList 'SpaceSeparated (" + type + "))"; + case "pipes": return "(QueryList 'PipeSeparated (" + type + "))"; + case "multi": return "(QueryList 'MultiParamArray (" + type + "))"; + default: + throw new UnsupportedOperationException(); + } + } + + private String fixOperatorChars(String string) { + StringBuilder sb = new StringBuilder(); + String name = string; + //Check if it is a reserved word, in which case the underscore is added when property name is generated. + if (string.startsWith("_")) { + if (reservedWords.contains(string.substring(1, string.length()))) { + name = string.substring(1, string.length()); + } else if (reservedWordsMappings.containsValue(string)) { + name = LEADING_UNDERSCORE.matcher(string).replaceFirst(""); + } + } + for (char c : name.toCharArray()) { + String cString = String.valueOf(c); + if (specialCharReplacements.containsKey(cString)) { + sb.append("'"); + sb.append(specialCharReplacements.get(cString)); + } else { + sb.append(c); + } + } + return sb.toString(); + } + + // Remove characters from a string that do not belong in a model classname + private String fixModelChars(String string) { + return string.replace(".", "").replace("-", ""); + } + + // Override fromModel to create the appropriate model namings + @Override + public CodegenModel fromModel(String name, Schema mod, Map allDefinitions) { + CodegenModel model = super.fromModel(name, mod, allDefinitions); + + // Clean up the class name to remove invalid characters + model.classname = fixModelChars(model.classname); + if(typeMapping.containsValue(model.classname)) { + model.classname += "_"; + } + + // From the model name, compute the prefix for the fields. + String prefix = camelize(model.classname, true); + for(CodegenProperty prop : model.vars) { + prop.name = toVarName(prefix + camelize(fixOperatorChars(prop.name))); + } + + // Create newtypes for things with non-object types + String dataOrNewtype = "data"; + if(model.dataType != "object" && typeMapping.containsKey(model.dataType)) { + String newtype = typeMapping.get(model.dataType); + model.vendorExtensions.put("x-customNewtype", newtype); + } + + // Provide the prefix as a vendor extension, so that it can be used in the ToJSON and FromJSON instances. + model.vendorExtensions.put("x-prefix", prefix); + model.vendorExtensions.put("x-data", dataOrNewtype); + + return model; + } + + @Override + public String escapeQuotationMark(String input) { + // remove " to avoid code injection + return input.replace("\"", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("{-", "{_-").replace("-}", "-_}"); + } + +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index 5249c7dde1..b9ac5373d9 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -1,4 +1,5 @@ org.openapitools.codegen.languages.BashClientCodegen +org.openapitools.codegen.languages.HaskellServantCodegen org.openapitools.codegen.languages.ObjcClientCodegen org.openapitools.codegen.languages.PhpClientCodegen org.openapitools.codegen.languages.RubyClientCodegen diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/haskellservant/HaskellModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/haskellservant/HaskellModelTest.java new file mode 100644 index 0000000000..a3092d46b2 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/haskellservant/HaskellModelTest.java @@ -0,0 +1,25 @@ +package org.openapitools.codegen.haskellservant; + +import io.swagger.v3.oas.models.PathItem; +import org.openapitools.codegen.CodegenModel; +import org.openapitools.codegen.CodegenOperation; +import org.openapitools.codegen.CodegenProperty; +import org.openapitools.codegen.DefaultCodegen; +import org.openapitools.codegen.languages.HaskellServantCodegen; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.util.SchemaTypeUtil; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class HaskellModelTest { + + @Test(description = "convert a haskell model with dots") + public void modelTest() { + Assert.assertEquals(true, true); + } +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/haskellservant/HaskellServantOptionsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/haskellservant/HaskellServantOptionsTest.java new file mode 100644 index 0000000000..f3c0b32cd3 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/haskellservant/HaskellServantOptionsTest.java @@ -0,0 +1,37 @@ +package org.openapitools.codegen.haskellservant; + + +import org.openapitools.codegen.AbstractOptionsTest; +import org.openapitools.codegen.CodegenConfig; +import org.openapitools.codegen.languages.HaskellServantCodegen; +import org.openapitools.codegen.options.HaskellServantOptionsProvider; + +import mockit.Expectations; +import mockit.Tested; + +public class HaskellServantOptionsTest extends AbstractOptionsTest { + + @Tested + private HaskellServantCodegen clientCodegen; + + public HaskellServantOptionsTest() { + super(new HaskellServantOptionsProvider()); + } + + @Override + protected CodegenConfig getCodegenConfig() { + return clientCodegen; + } + + @Override + protected void setExpectations() { + new Expectations(clientCodegen) {{ + clientCodegen.setModelPackage(HaskellServantOptionsProvider.MODEL_PACKAGE_VALUE); + times = 1; + clientCodegen.setApiPackage(HaskellServantOptionsProvider.API_PACKAGE_VALUE); + times = 1; + clientCodegen.setSortParamsByRequiredFlag(Boolean.valueOf(HaskellServantOptionsProvider.SORT_PARAMS_VALUE)); + times = 1; + }}; + } +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpModelTest.java index 6d1e32063a..cf8bc0dc0a 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpModelTest.java @@ -347,7 +347,7 @@ public class PhpModelTest { } // datetime (or primitive type) not yet supported in HTTP request body - @Test(description = "returns DateTime when using `--model-name-prefix`", enabled = false) + @Test(description = "returns DateTime when using `--model-name-prefix`", enabled = false) public void dateTest() { final OpenAPI model = new OpenAPIV3Parser().read("src/test/resources/2_0/datePropertyTest.json"); final DefaultCodegen codegen = new PhpClientCodegen(); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/options/HaskellServantOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/options/HaskellServantOptionsProvider.java new file mode 100644 index 0000000000..f118e0fde6 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/options/HaskellServantOptionsProvider.java @@ -0,0 +1,39 @@ +package org.openapitools.codegen.options; + +import org.openapitools.codegen.CodegenConstants; +import org.openapitools.codegen.languages.HaskellServantCodegen; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public class HaskellServantOptionsProvider implements OptionsProvider { + public static final String MODEL_PACKAGE_VALUE = "Model"; + public static final String API_PACKAGE_VALUE = "Api"; + public static final String SORT_PARAMS_VALUE = "false"; + public static final String ENSURE_UNIQUE_PARAMS_VALUE = "true"; + public static final String ALLOW_UNICODE_IDENTIFIERS_VALUE = "false"; + public static final String PREPEND_FORM_OR_BODY_PARAMETERS_VALUE = "true"; + + @Override + public String getLanguage() { + return "haskell"; + } + + @Override + public Map createOptions() { + ImmutableMap.Builder builder = new ImmutableMap.Builder(); + return builder.put(CodegenConstants.MODEL_PACKAGE, MODEL_PACKAGE_VALUE) + .put(CodegenConstants.API_PACKAGE, API_PACKAGE_VALUE) + .put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, SORT_PARAMS_VALUE) + .put(CodegenConstants.ENSURE_UNIQUE_PARAMS, ENSURE_UNIQUE_PARAMS_VALUE) + .put(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, ALLOW_UNICODE_IDENTIFIERS_VALUE) + .put(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS, PREPEND_FORM_OR_BODY_PARAMETERS_VALUE) + .build(); + } + + @Override + public boolean isServer() { + return true; + } +}