diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/DartClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/DartClientCodegen.java index da10196bfda..478431a455d 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/DartClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/DartClientCodegen.java @@ -3,27 +3,35 @@ import io.swagger.codegen.CliOption; import io.swagger.codegen.CodegenConfig; import io.swagger.codegen.CodegenConstants; +import io.swagger.codegen.CodegenModel; +import io.swagger.codegen.CodegenProperty; import io.swagger.codegen.CodegenType; import io.swagger.codegen.DefaultCodegen; import io.swagger.codegen.SupportingFile; +import io.swagger.models.Model; import io.swagger.models.properties.ArrayProperty; import io.swagger.models.properties.MapProperty; import io.swagger.models.properties.Property; import java.io.File; +import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; public class DartClientCodegen extends DefaultCodegen implements CodegenConfig { public static final String BROWSER_CLIENT = "browserClient"; public static final String PUB_NAME = "pubName"; public static final String PUB_VERSION = "pubVersion"; public static final String PUB_DESCRIPTION = "pubDescription"; + public static final String USE_ENUM_EXTENSION = "useEnumExtension"; protected boolean browserClient = true; protected String pubName = "swagger"; protected String pubVersion = "1.0.0"; protected String pubDescription = "Swagger API client"; + protected boolean useEnumExtension = false; protected String sourceFolder = ""; protected String apiDocPath = "docs/"; protected String modelDocPath = "docs/"; @@ -95,6 +103,7 @@ public DartClientCodegen() { cliOptions.add(new CliOption(PUB_NAME, "Name in generated pubspec")); cliOptions.add(new CliOption(PUB_VERSION, "Version in generated pubspec")); cliOptions.add(new CliOption(PUB_DESCRIPTION, "Description in generated pubspec")); + cliOptions.add(new CliOption(USE_ENUM_EXTENSION, "Allow the 'x-enum-values' extension for enums")); cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, "source folder for generated code")); } @@ -145,6 +154,13 @@ public void processOpts() { additionalProperties.put(PUB_DESCRIPTION, pubDescription); } + if (additionalProperties.containsKey(USE_ENUM_EXTENSION)) { + this.setUseEnumExtension(convertPropertyToBooleanAndWriteBack(USE_ENUM_EXTENSION)); + } else { + // Not set, use to be passed to template. + additionalProperties.put(USE_ENUM_EXTENSION, useEnumExtension); + } + if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) { this.setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER)); } @@ -177,16 +193,11 @@ public void processOpts() { supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); - } - @Override - public String escapeReservedWord(String name) { - if(this.reservedWordsMappings().containsKey(name)) { - return this.reservedWordsMappings().get(name); - } - return "_" + name; + public String escapeReservedWord(String name) { + return name + "_"; } @Override @@ -223,8 +234,11 @@ public String toVarName(String name) { // pet_id => petId name = camelize(name, true); - // for reserved word or word starting with number, append _ - if (isReservedWord(name) || name.matches("^\\d.*")) { + if (name.matches("^\\d.*")) { + name = "n" + name; + } + + if (isReservedWord(name)) { name = escapeReservedWord(name); } @@ -300,6 +314,117 @@ public String getSwaggerType(Property p) { return toModelName(type); } + @Override + public Map postProcessModels(Map objs) { + return postProcessModelsEnum(objs); + } + + @Override + public Map postProcessModelsEnum(Map objs) { + List models = (List) objs.get("models"); + for (Object _mo : models) { + Map mo = (Map) _mo; + CodegenModel cm = (CodegenModel) mo.get("model"); + boolean succes = buildEnumFromVendorExtension(cm) || + buildEnumFromValues(cm); + for (CodegenProperty var : cm.vars) { + updateCodegenPropertyEnum(var); + } + } + return objs; + } + + /** + * Builds the set of enum members from their declared value. + * + * @return {@code true} if the enum was built + */ + private boolean buildEnumFromValues(CodegenModel cm) { + if (!cm.isEnum || cm.allowableValues == null) { + return false; + } + Map allowableValues = cm.allowableValues; + List values = (List) allowableValues.get("values"); + List> enumVars = + new ArrayList>(); + String commonPrefix = findCommonPrefixOfVars(values); + int truncateIdx = commonPrefix.length(); + for (Object value : values) { + Map enumVar = new HashMap(); + String enumName; + if (truncateIdx == 0) { + enumName = value.toString(); + } else { + enumName = value.toString().substring(truncateIdx); + if ("".equals(enumName)) { + enumName = value.toString(); + } + } + enumVar.put("name", toEnumVarName(enumName, cm.dataType)); + enumVar.put("value", toEnumValue(value.toString(), cm.dataType)); + enumVars.add(enumVar); + } + cm.allowableValues.put("enumVars", enumVars); + return true; + } + + /** + * Builds the set of enum members from a vendor extension. + * + * @return {@code true} if the enum was built + */ + private boolean buildEnumFromVendorExtension(CodegenModel cm) { + if (!cm.isEnum || cm.allowableValues == null || + !useEnumExtension || + !cm.vendorExtensions.containsKey("x-enum-values")) { + return false; + } + Object extension = cm.vendorExtensions.get("x-enum-values"); + List> values = + (List>) extension; + List> enumVars = + new ArrayList>(); + for (Map value : values) { + Map enumVar = new HashMap(); + String name = camelize((String) value.get("identifier"), true); + if (isReservedWord(name)) { + name = escapeReservedWord(name); + } + enumVar.put("name", name); + enumVar.put("value", toEnumValue( + value.get("numericValue").toString(), cm.dataType)); + if (value.containsKey("description")) { + enumVar.put("description", value.get("description").toString()); + } + enumVars.add(enumVar); + } + cm.allowableValues.put("enumVars", enumVars); + return true; + } + + @Override + public String toEnumVarName(String value, String datatype) { + if (value.length() == 0) { + return "empty"; + } + String var = value.replaceAll("\\W+", "_"); + if ("number".equalsIgnoreCase(datatype) || + "int".equalsIgnoreCase(datatype)) { + var = "Number" + var; + } + return escapeReservedWord(camelize(var, true)); + } + + @Override + public String toEnumValue(String value, String datatype) { + if ("number".equalsIgnoreCase(datatype) || + "int".equalsIgnoreCase(datatype)) { + return value; + } else { + return "\"" + escapeText(value) + "\""; + } + } + @Override public String toOperationId(String operationId) { // method name cannot use reserved keyword, e.g. return @@ -328,6 +453,10 @@ public void setPubDescription(String pubDescription) { this.pubDescription = pubDescription; } + public void setUseEnumExtension(boolean useEnumExtension) { + this.useEnumExtension = useEnumExtension; + } + public void setSourceFolder(String sourceFolder) { this.sourceFolder = sourceFolder; } diff --git a/modules/swagger-codegen/src/main/resources/dart/api_client.mustache b/modules/swagger-codegen/src/main/resources/dart/api_client.mustache index 86e698eb836..f35db7dbaed 100644 --- a/modules/swagger-codegen/src/main/resources/dart/api_client.mustache +++ b/modules/swagger-codegen/src/main/resources/dart/api_client.mustache @@ -16,7 +16,14 @@ class ApiClient { Map _authentications = {}; final dson = new Dartson.JSON() - ..addTransformer(new DateTimeParser(), DateTime); + {{#models}} + {{#model}} + {{#isEnum}} + ..addTransformer(new {{classname}}TypeTransformer(), {{classname}}) + {{/isEnum}} + {{/model}} + {{/models}} + ..addTransformer(new DateTimeParser(), DateTime); final _RegList = new RegExp(r'^List<(.*)>$'); final _RegMap = new RegExp(r'^Map$'); @@ -46,7 +53,16 @@ class ApiClient { {{#models}} {{#model}} case '{{classname}}': + {{#isEnum}} + // Enclose the value in a list so that Dartson can use a transformer + // to decode it. + final listValue = [value]; + final List listResult = dson.map(listValue, []); + return listResult[0]; + {{/isEnum}} + {{^isEnum}} return dson.map(value, new {{classname}}()); + {{/isEnum}} {{/model}} {{/models}} default: @@ -116,7 +132,7 @@ class ApiClient { headerParams['Content-Type'] = contentType; if(body is MultipartRequest) { - var request = new MultipartRequest(method, Uri.parse(url)); + var request = new MultipartRequest(method, Uri.parse(url)); request.fields.addAll(body.fields); request.files.addAll(body.files); request.headers.addAll(body.headers); @@ -141,7 +157,7 @@ class ApiClient { } /// Update query and header parameters based on authentication settings. - /// @param authNames The authentications to apply + /// @param authNames The authentications to apply void _updateParamsForAuth(List authNames, List queryParams, Map headerParams) { authNames.forEach((authName) { Authentication auth = _authentications[authName]; diff --git a/modules/swagger-codegen/src/main/resources/dart/api_helper.mustache b/modules/swagger-codegen/src/main/resources/dart/api_helper.mustache index 15798ef3466..48de5ab24b0 100644 --- a/modules/swagger-codegen/src/main/resources/dart/api_helper.mustache +++ b/modules/swagger-codegen/src/main/resources/dart/api_helper.mustache @@ -38,7 +38,15 @@ String _parameterToString(dynamic value) { return ''; } else if (value is DateTime) { return value.toUtc().toIso8601String(); + {{#models}} + {{#model}} + {{#isEnum}} + } else if (value is {{classname}}) { + return new {{classname}}TypeTransformer().encode(value).toString(); + {{/isEnum}} + {{/model}} + {{/models}} } else { return value.toString(); } -} \ No newline at end of file +} diff --git a/modules/swagger-codegen/src/main/resources/dart/apilib.mustache b/modules/swagger-codegen/src/main/resources/dart/apilib.mustache index 8f131054f25..8782ce2236d 100644 --- a/modules/swagger-codegen/src/main/resources/dart/apilib.mustache +++ b/modules/swagger-codegen/src/main/resources/dart/apilib.mustache @@ -6,6 +6,7 @@ import 'package:http/browser_client.dart';{{/browserClient}} import 'package:http/http.dart'; import 'package:dartson/dartson.dart'; import 'package:dartson/transformers/date_time.dart'; +import 'package:dartson/type_transformer.dart'; part 'api_client.dart'; part 'api_helper.dart'; @@ -21,4 +22,3 @@ part 'auth/http_basic_auth.dart'; {{/model}}{{/models}} ApiClient defaultApiClient = new ApiClient(); - diff --git a/modules/swagger-codegen/src/main/resources/dart/class.mustache b/modules/swagger-codegen/src/main/resources/dart/class.mustache new file mode 100644 index 00000000000..d92258c484f --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/dart/class.mustache @@ -0,0 +1,14 @@ +@Entity() +class {{classname}} { + {{#vars}}{{#description}}/* {{{description}}} */{{/description}} + @Property(name: '{{baseName}}') + {{{datatype}}} {{name}} = {{{defaultValue}}}; + {{#allowableValues}}{{#min}} // range from {{min}} to {{max}}{{/min}}//{{^min}}enum {{name}}Enum { {{#values}} {{.}}, {{/values}} };{{/min}}{{/allowableValues}} + {{/vars}} + {{classname}}(); + + @override + String toString() { + return '{{classname}}[{{#vars}}{{name}}=${{name}}, {{/vars}}]'; + } +} diff --git a/modules/swagger-codegen/src/main/resources/dart/enum.mustache b/modules/swagger-codegen/src/main/resources/dart/enum.mustache new file mode 100644 index 00000000000..debb73bbcec --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/dart/enum.mustache @@ -0,0 +1,36 @@ +@Entity() +class {{classname}} { + /// The underlying value of this enum member. + final {{dataType}} value; + + const {{classname}}._internal(this.value); + + {{#allowableValues}} + {{#enumVars}} + {{#description}} + /// {{description}} + {{/description}} + static const {{classname}} {{name}} = const {{classname}}._internal({{value}}); + {{/enumVars}} + {{/allowableValues}} +} + +class {{classname}}TypeTransformer extends TypeTransformer<{{classname}}> { + + @override + dynamic encode({{classname}} data) { + return data.value; + } + + @override + {{classname}} decode(dynamic data) { + switch (data) { + {{#allowableValues}} + {{#enumVars}} + case {{value}}: return {{classname}}.{{name}}; + {{/enumVars}} + {{/allowableValues}} + default: throw('Unknown enum value to decode: $data'); + } + } +} diff --git a/modules/swagger-codegen/src/main/resources/dart/model.mustache b/modules/swagger-codegen/src/main/resources/dart/model.mustache index 5fe107c7849..1fa14e179a7 100644 --- a/modules/swagger-codegen/src/main/resources/dart/model.mustache +++ b/modules/swagger-codegen/src/main/resources/dart/model.mustache @@ -1,19 +1,7 @@ part of {{pubName}}.api; -{{#models}}{{#model}} -@Entity() -class {{classname}} { - {{#vars}}{{#description}}/* {{{description}}} */{{/description}} - @Property(name: '{{baseName}}') - {{{datatype}}} {{name}} = {{{defaultValue}}}; - {{#allowableValues}}{{#min}} // range from {{min}} to {{max}}{{/min}}//{{^min}}enum {{name}}Enum { {{#values}} {{.}}, {{/values}} };{{/min}}{{/allowableValues}} - {{/vars}} - {{classname}}(); - - @override - String toString() { - return '{{classname}}[{{#vars}}{{name}}=${{name}}, {{/vars}}]'; - } - -} -{{/model}}{{/models}} +{{#models}} +{{#model}} +{{#isEnum}}{{>enum}}{{/isEnum}}{{^isEnum}}{{>class}}{{/isEnum}} +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/dart/DartClientOptionsTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/dart/DartClientOptionsTest.java index 519ac4943cc..38231d86f01 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/dart/DartClientOptionsTest.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/dart/DartClientOptionsTest.java @@ -38,7 +38,8 @@ protected void setExpectations() { times = 1; clientCodegen.setSourceFolder(DartClientOptionsProvider.SOURCE_FOLDER_VALUE); times = 1; + clientCodegen.setUseEnumExtension(Boolean.valueOf(DartClientOptionsProvider.USE_ENUM_EXTENSION)); + times = 1; }}; } } - diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/DartClientOptionsProvider.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/DartClientOptionsProvider.java index 4e2670f2653..52586119be8 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/DartClientOptionsProvider.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/DartClientOptionsProvider.java @@ -15,6 +15,7 @@ public class DartClientOptionsProvider implements OptionsProvider { public static final String PUB_VERSION_VALUE = "1.0.0-SNAPSHOT"; public static final String PUB_DESCRIPTION_VALUE = "Swagger API client dart"; public static final String SOURCE_FOLDER_VALUE = "src"; + public static final String USE_ENUM_EXTENSION = "true"; public static final String ALLOW_UNICODE_IDENTIFIERS_VALUE = "false"; @@ -33,6 +34,7 @@ public Map createOptions() { .put(DartClientCodegen.PUB_VERSION, PUB_VERSION_VALUE) .put(DartClientCodegen.PUB_DESCRIPTION, PUB_DESCRIPTION_VALUE) .put(CodegenConstants.SOURCE_FOLDER, SOURCE_FOLDER_VALUE) + .put(DartClientCodegen.USE_ENUM_EXTENSION, USE_ENUM_EXTENSION) .put(CodegenConstants.ALLOW_UNICODE_IDENTIFIERS, ALLOW_UNICODE_IDENTIFIERS_VALUE) .build(); } diff --git a/samples/client/petstore/dart/swagger/.swagger-codegen/VERSION b/samples/client/petstore/dart/swagger/.swagger-codegen/VERSION index 7fea99011a6..f9f7450d135 100644 --- a/samples/client/petstore/dart/swagger/.swagger-codegen/VERSION +++ b/samples/client/petstore/dart/swagger/.swagger-codegen/VERSION @@ -1 +1 @@ -2.2.3-SNAPSHOT \ No newline at end of file +2.3.0-SNAPSHOT \ No newline at end of file diff --git a/samples/client/petstore/dart/swagger/docs/StoreApi.md b/samples/client/petstore/dart/swagger/docs/StoreApi.md index c2e5dcdfde1..b2615f0d4f5 100644 --- a/samples/client/petstore/dart/swagger/docs/StoreApi.md +++ b/samples/client/petstore/dart/swagger/docs/StoreApi.md @@ -87,7 +87,7 @@ This endpoint does not need any parameter. ### Return type -[**Map**](Map.md) +**Map** ### Authorization diff --git a/samples/client/petstore/dart/swagger/lib/api.dart b/samples/client/petstore/dart/swagger/lib/api.dart index 9273d7f1f99..79a83321fa5 100644 --- a/samples/client/petstore/dart/swagger/lib/api.dart +++ b/samples/client/petstore/dart/swagger/lib/api.dart @@ -6,6 +6,7 @@ import 'package:http/browser_client.dart'; import 'package:http/http.dart'; import 'package:dartson/dartson.dart'; import 'package:dartson/transformers/date_time.dart'; +import 'package:dartson/type_transformer.dart'; part 'api_client.dart'; part 'api_helper.dart'; @@ -28,4 +29,3 @@ part 'model/user.dart'; ApiClient defaultApiClient = new ApiClient(); - diff --git a/samples/client/petstore/dart/swagger/lib/api_client.dart b/samples/client/petstore/dart/swagger/lib/api_client.dart index 9552cef10d8..6c292b973f0 100644 --- a/samples/client/petstore/dart/swagger/lib/api_client.dart +++ b/samples/client/petstore/dart/swagger/lib/api_client.dart @@ -16,7 +16,7 @@ class ApiClient { Map _authentications = {}; final dson = new Dartson.JSON() - ..addTransformer(new DateTimeParser(), DateTime); + ..addTransformer(new DateTimeParser(), DateTime); final _RegList = new RegExp(r'^List<(.*)>$'); final _RegMap = new RegExp(r'^Map$'); @@ -121,7 +121,7 @@ class ApiClient { headerParams['Content-Type'] = contentType; if(body is MultipartRequest) { - var request = new MultipartRequest(method, Uri.parse(url)); + var request = new MultipartRequest(method, Uri.parse(url)); request.fields.addAll(body.fields); request.files.addAll(body.files); request.headers.addAll(body.headers); @@ -146,7 +146,7 @@ class ApiClient { } /// Update query and header parameters based on authentication settings. - /// @param authNames The authentications to apply + /// @param authNames The authentications to apply void _updateParamsForAuth(List authNames, List queryParams, Map headerParams) { authNames.forEach((authName) { Authentication auth = _authentications[authName]; diff --git a/samples/client/petstore/dart/swagger/lib/api_helper.dart b/samples/client/petstore/dart/swagger/lib/api_helper.dart index 7d2c3471a9f..6da2530b1b2 100644 --- a/samples/client/petstore/dart/swagger/lib/api_helper.dart +++ b/samples/client/petstore/dart/swagger/lib/api_helper.dart @@ -41,4 +41,4 @@ String _parameterToString(dynamic value) { } else { return value.toString(); } -} \ No newline at end of file +} diff --git a/samples/client/petstore/dart/swagger/lib/model/api_response.dart b/samples/client/petstore/dart/swagger/lib/model/api_response.dart index b2ac2c2577e..7cb79fb8aef 100644 --- a/samples/client/petstore/dart/swagger/lib/model/api_response.dart +++ b/samples/client/petstore/dart/swagger/lib/model/api_response.dart @@ -1,6 +1,5 @@ part of swagger.api; - @Entity() class ApiResponse { @@ -21,6 +20,5 @@ class ApiResponse { String toString() { return 'ApiResponse[code=$code, type=$type, message=$message, ]'; } - } diff --git a/samples/client/petstore/dart/swagger/lib/model/category.dart b/samples/client/petstore/dart/swagger/lib/model/category.dart index 9ab2a96fc37..26b90273163 100644 --- a/samples/client/petstore/dart/swagger/lib/model/category.dart +++ b/samples/client/petstore/dart/swagger/lib/model/category.dart @@ -1,6 +1,5 @@ part of swagger.api; - @Entity() class Category { @@ -17,6 +16,5 @@ class Category { String toString() { return 'Category[id=$id, name=$name, ]'; } - } diff --git a/samples/client/petstore/dart/swagger/lib/model/order.dart b/samples/client/petstore/dart/swagger/lib/model/order.dart index d7ea97b3bad..84e4a972d57 100644 --- a/samples/client/petstore/dart/swagger/lib/model/order.dart +++ b/samples/client/petstore/dart/swagger/lib/model/order.dart @@ -1,6 +1,5 @@ part of swagger.api; - @Entity() class Order { @@ -33,6 +32,5 @@ class Order { String toString() { return 'Order[id=$id, petId=$petId, quantity=$quantity, shipDate=$shipDate, status=$status, complete=$complete, ]'; } - } diff --git a/samples/client/petstore/dart/swagger/lib/model/pet.dart b/samples/client/petstore/dart/swagger/lib/model/pet.dart index 03e13743763..f2b4108b892 100644 --- a/samples/client/petstore/dart/swagger/lib/model/pet.dart +++ b/samples/client/petstore/dart/swagger/lib/model/pet.dart @@ -1,6 +1,5 @@ part of swagger.api; - @Entity() class Pet { @@ -33,6 +32,5 @@ class Pet { String toString() { return 'Pet[id=$id, category=$category, name=$name, photoUrls=$photoUrls, tags=$tags, status=$status, ]'; } - } diff --git a/samples/client/petstore/dart/swagger/lib/model/tag.dart b/samples/client/petstore/dart/swagger/lib/model/tag.dart index 485de92a615..45f3668b3e7 100644 --- a/samples/client/petstore/dart/swagger/lib/model/tag.dart +++ b/samples/client/petstore/dart/swagger/lib/model/tag.dart @@ -1,6 +1,5 @@ part of swagger.api; - @Entity() class Tag { @@ -17,6 +16,5 @@ class Tag { String toString() { return 'Tag[id=$id, name=$name, ]'; } - } diff --git a/samples/client/petstore/dart/swagger/lib/model/user.dart b/samples/client/petstore/dart/swagger/lib/model/user.dart index f529d9432c1..77c02114428 100644 --- a/samples/client/petstore/dart/swagger/lib/model/user.dart +++ b/samples/client/petstore/dart/swagger/lib/model/user.dart @@ -1,6 +1,5 @@ part of swagger.api; - @Entity() class User { @@ -41,6 +40,5 @@ class User { String toString() { return 'User[id=$id, username=$username, firstName=$firstName, lastName=$lastName, email=$email, password=$password, phone=$phone, userStatus=$userStatus, ]'; } - }