Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for enums in Dart. #6516

Merged
merged 7 commits into from
Oct 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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/";
Expand Down Expand Up @@ -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"));
}

Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -300,6 +314,117 @@ public String getSwaggerType(Property p) {
return toModelName(type);
}

@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
return postProcessModelsEnum(objs);
}

@Override
public Map<String, Object> postProcessModelsEnum(Map<String, Object> objs) {
List<Object> models = (List<Object>) objs.get("models");
for (Object _mo : models) {
Map<String, Object> mo = (Map<String, Object>) _mo;
CodegenModel cm = (CodegenModel) mo.get("model");
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<String, Object> allowableValues = cm.allowableValues;
List<Object> values = (List<Object>) allowableValues.get("values");
List<Map<String, String>> enumVars =
new ArrayList<Map<String, String>>();
String commonPrefix = findCommonPrefixOfVars(values);
int truncateIdx = commonPrefix.length();
for (Object value : values) {
Map<String, String> enumVar = new HashMap<String, String>();
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<Map<String, Object>> values =
(List<Map<String, Object>>) extension;
List<Map<String, String>> enumVars =
new ArrayList<Map<String, String>>();
for (Map<String, Object> value : values) {
Map<String, String> enumVar = new HashMap<String, String>();
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
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ class ApiClient {
Map<String, Authentication> _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<String,(.*)>$');
Expand Down Expand Up @@ -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<dynamic> listResult = dson.map(listValue, []);
return listResult[0];
{{/isEnum}}
{{^isEnum}}
return dson.map(value, new {{classname}}());
{{/isEnum}}
{{/model}}
{{/models}}
default:
Expand Down Expand Up @@ -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);
Expand All @@ -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<String> authNames, List<QueryParam> queryParams, Map<String, String> headerParams) {
authNames.forEach((authName) {
Authentication auth = _authentications[authName];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -21,4 +22,3 @@ part 'auth/http_basic_auth.dart';
{{/model}}{{/models}}

ApiClient defaultApiClient = new ApiClient();

14 changes: 14 additions & 0 deletions modules/swagger-codegen/src/main/resources/dart/class.mustache
Original file line number Diff line number Diff line change
@@ -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}}]';
}
}
36 changes: 36 additions & 0 deletions modules/swagger-codegen/src/main/resources/dart/enum.mustache
Original file line number Diff line number Diff line change
@@ -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');
}
}
}
22 changes: 5 additions & 17 deletions modules/swagger-codegen/src/main/resources/dart/model.mustache
Original file line number Diff line number Diff line change
@@ -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}}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}};
}
}

Loading