Skip to content
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public class CodegenModel implements IJsonSchemaValidationProperties {
public String defaultValue;
public String arrayModelType;
public boolean isAlias; // Is this effectively an alias of another simple type
public boolean isString, isInteger, isLong, isNumber, isNumeric, isFloat, isDouble, isDate, isDateTime, isShort, isUnboundedInteger, isBoolean;
public boolean isString, isInteger, isLong, isNumber, isNumeric, isFloat, isDouble, isDate, isDateTime, isShort, isUnboundedInteger, isPrimitiveType, isBoolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there really no other method for this already? Im really surprised this needs to be introduced as new code @wing328 ?

private boolean additionalPropertiesIsAnyType;
public List<CodegenProperty> vars = new ArrayList<CodegenProperty>(); // all properties (without parent's properties)
public List<CodegenProperty> allVars = new ArrayList<CodegenProperty>(); // all properties (with parent's properties)
Expand Down Expand Up @@ -640,6 +640,14 @@ public void setIsUnboundedInteger(boolean isUnboundedInteger) {
this.isUnboundedInteger = isUnboundedInteger;
}

@Override
public boolean getIsPrimitiveType() { return isPrimitiveType; }

@Override
public void setIsPrimitiveType(boolean isPrimitiveType) {
this.isPrimitiveType = isPrimitiveType;
}

@Override
public CodegenProperty getAdditionalProperties() { return additionalProperties; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,14 @@ public void setIsUnboundedInteger(boolean isUnboundedInteger) {
this.isUnboundedInteger = isUnboundedInteger;
}

@Override
public boolean getIsPrimitiveType() { return isPrimitiveType; }

@Override
public void setIsPrimitiveType(boolean isPrimitiveType) {
this.isPrimitiveType = isPrimitiveType;
}

@Override
public CodegenProperty getAdditionalProperties() { return additionalProperties; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,14 @@ public void setIsUnboundedInteger(boolean isUnboundedInteger) {
this.isUnboundedInteger = isUnboundedInteger;
}

@Override
public boolean getIsPrimitiveType() { return isPrimitiveType; }

@Override
public void setIsPrimitiveType(boolean isPrimitiveType) {
this.isPrimitiveType = isPrimitiveType;
}

public Map<String, Object> getVendorExtensions() {
return vendorExtensions;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,14 @@ public void setIsUnboundedInteger(boolean isUnboundedInteger) {
this.isUnboundedInteger = isUnboundedInteger;
}

@Override
public boolean getIsPrimitiveType() { return primitiveType; }

@Override
public void setIsPrimitiveType(boolean isPrimitiveType) {
this.primitiveType = isPrimitiveType;
}

@Override
public void setIsModel(boolean isModel) {
this.isModel = isModel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ public interface IJsonSchemaValidationProperties {

void setIsUnboundedInteger(boolean isUnboundedInteger);

boolean getIsPrimitiveType();

void setIsPrimitiveType(boolean isPrimitiveType);

CodegenProperty getAdditionalProperties();

void setAdditionalProperties(CodegenProperty additionalProperties);
Expand Down Expand Up @@ -205,4 +209,4 @@ default void setTypeProperties(Schema p) {
setIsAnyType(true);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,6 @@ public AbstractDartCodegen() {
modelDocTemplateFiles.put("object_doc.mustache", ".md");
apiDocTemplateFiles.put("api_doc.mustache", ".md");

modelTestTemplateFiles.put("model_test.mustache", ".dart");
apiTestTemplateFiles.put("api_test.mustache", ".dart");

final List<String> reservedWordsList = new ArrayList<>();
try(BufferedReader reader = new BufferedReader(
new InputStreamReader(DartClientCodegen.class.getResourceAsStream("/dart/dart-keywords.txt"),
Expand Down Expand Up @@ -507,6 +504,21 @@ public Map<String, Object> postProcessModels(Map<String, Object> objs) {
@Override
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
super.postProcessModelProperty(model, property);
if (property.dataType.equals("Object") || property.baseType.equals("Object") || (property.complexType != null && property.complexType.equals("Object"))) {
String dataType = "Map<String, dynamic>";
String complexType = dataType;
if (property.isArray) {
dataType = "List<"+dataType+">";
}
if (property.required) {
dataType += "?";
complexType += "?";
}
property.setDatatypeWithEnum(dataType);
property.setDatatype(dataType);
property.setComplexType(complexType);
property.setIsPrimitiveType(true);
}
if (!model.isEnum && property.isEnum) {
// These are inner enums, enums which do not exist as models, just as properties.
// They are handled via the enum_inline template and and are generated in the
Expand All @@ -532,6 +544,12 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
@Override
public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, List<Server> servers) {
final CodegenOperation op = super.fromOperation(path, httpMethod, operation, servers);
String returnType = op.returnType;
if (returnType == null || returnType.equals("Object")) {
op.returnType = "Map<String, dynamic>";
op.returnBaseType = "Map<String, dynamic>";
op.returnTypeIsPrimitive = true;
}
for (CodegenResponse r : op.responses) {
// By default only set types are automatically added to operation imports, not sure why.
// Add all container type imports here, by default 'dart:core' imports are skipped
Expand Down Expand Up @@ -644,15 +662,20 @@ protected void updateEnumVarsWithExtensions(List<Map<String, Object>> enumVars,
}
}

boolean needsEnumEscape(String datatype) {
// TODO: is this complete?
return !("num".equalsIgnoreCase(datatype) ||
"int".equalsIgnoreCase(datatype) ||
"double".equalsIgnoreCase(datatype)||
"float".equalsIgnoreCase(datatype));
}

@Override
public String toEnumVarName(String value, String datatype) {
if (value.length() == 0) {
return "empty";
}
if (("number".equalsIgnoreCase(datatype) ||
"double".equalsIgnoreCase(datatype) ||
"int".equalsIgnoreCase(datatype)) &&
value.matches("^-?\\d.*")) {
if (!needsEnumEscape(datatype) && value.matches("^-?\\d.*")) {
// Only rename numeric values when the datatype is numeric
// AND the name is not changed by enum extensions (matches a numeric value).
boolean isNegative = value.startsWith("-");
Expand All @@ -663,8 +686,7 @@ public String toEnumVarName(String value, String datatype) {

@Override
public String toEnumValue(String value, String datatype) {
if ("number".equalsIgnoreCase(datatype) ||
"int".equalsIgnoreCase(datatype)) {
if (!needsEnumEscape(datatype)) {
return value;
} else {
return "'" + escapeText(value) + "'";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})

## Requirements

Dart 2.0 or later
Dart 2.14 or later

## Installation & Usage

Expand Down
22 changes: 5 additions & 17 deletions modules/openapi-generator/src/main/resources/dart2/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{{#operations}}

class {{{classname}}} {
{{{classname}}}([ApiClient apiClient]) : apiClient = apiClient ?? defaultApiClient;
{{{classname}}}([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;

final ApiClient apiClient;
{{#operation}}
Expand Down Expand Up @@ -49,24 +49,13 @@ class {{{classname}}} {
///
{{/-last}}
{{/allParams}}
Future<Response> {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async {
{{#hasParams}}
// Verify required params are set.
{{#allParams}}
{{#required}}
if ({{{paramName}}} == null) {
throw ApiException(HttpStatus.badRequest, 'Missing required param: {{{paramName}}}');
}
{{/required}}
{{/allParams}}

{{/hasParams}}
Future<Response> {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async {
// ignore: prefer_const_declarations
final path = r'{{{path}}}'{{#pathParams}}
.replaceAll({{=<% %>=}}'{<% baseName %>}'<%={{ }}=%>, {{{paramName}}}{{^isString}}.toString(){{/isString}}){{/pathParams}};

// ignore: prefer_final_locals
Object postBody{{#bodyParam}} = {{{paramName}}}{{/bodyParam}};
Object? postBody{{#bodyParam}} = {{{paramName}}}{{/bodyParam}};

final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
Expand Down Expand Up @@ -174,7 +163,7 @@ class {{{classname}}} {
///
{{/-last}}
{{/allParams}}
Future<{{{returnType}}}{{^returnType}}void{{/returnType}}> {{{nickname}}}({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async {
Future<{{#returnType}}{{{.}}}?{{/returnType}}{{^returnType}}void{{/returnType}}> {{{nickname}}}({{#allParams}}{{#required}}{{{dataType}}} {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}}? {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async {
final response = await {{{nickname}}}WithHttpInfo({{#allParams}}{{#required}}{{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}}{{#hasOptionalParams}} {{#allParams}}{{^required}}{{{paramName}}}: {{{paramName}}},{{^-last}} {{/-last}}{{/required}}{{/allParams}} {{/hasOptionalParams}});
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
Expand All @@ -183,7 +172,7 @@ class {{{classname}}} {
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body != null && response.statusCode != HttpStatus.noContent) {
if (response.statusCode != HttpStatus.noContent) {
{{#native_serialization}}
{{#isArray}}
final responseBody = await _decodeBodyBytes(response);
Expand Down Expand Up @@ -225,7 +214,6 @@ class {{{classname}}} {
{{/isArray}}
{{/json_serializable}}
}
return Future<{{{returnType}}}>.value();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this was added recently to satisfy stricter linter config, so pls revert #10263

{{/returnType}}
}
{{/operation}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,7 @@ class ApiClient {
Client get client => _client;

/// Requests to use a new HTTP [Client] in this class.
///
/// If the [newClient] is null, an [ArgumentError] is thrown.
set client(Client newClient) {
if (newClient == null) {
throw ArgumentError('New client instance cannot be null.');
}
_client = newClient;
}

Expand All @@ -55,7 +50,7 @@ class ApiClient {
/// or deleted.
Map<String, Authentication> get authentications => Map.unmodifiable(_authentications);

T getAuthentication<T extends Authentication>(String name) {
T? getAuthentication<T extends Authentication>(String name) {
final authentication = _authentications[name];
return authentication is T ? authentication : null;
}
Expand All @@ -66,10 +61,10 @@ class ApiClient {
String path,
String method,
List<QueryParam> queryParams,
Object body,
Object? body,
Map<String, String> headerParams,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

headerParams and formParams are nullable in other openapi generators, so I would prefer to keep it this way for language agnostic users

Map<String, String> formParams,
String nullableContentType,
String? nullableContentType,
List<String> authNames,
) async {
_updateParamsForAuth(authNames, queryParams, headerParams);
Expand Down Expand Up @@ -149,12 +144,12 @@ class ApiClient {
}
{{#native_serialization}}

Future<dynamic> deserializeAsync(String json, String targetType, {bool growable}) async =>
Future<dynamic> deserializeAsync(String json, String targetType, {bool? growable}) async =>
// ignore: deprecated_member_use_from_same_package
deserialize(json, targetType, growable: growable);

@Deprecated('Scheduled for removal in OpenAPI Generator 6.x. Use deserializeAsync() instead.')
dynamic deserialize(String json, String targetType, {bool growable}) {
dynamic deserialize(String json, String targetType, {bool? growable}) {
// Remove all spaces. Necessary for regular expressions as well.
targetType = targetType.replaceAll(' ', ''); // ignore: parameter_assignments

Expand All @@ -166,10 +161,10 @@ class ApiClient {
{{/native_serialization}}

// ignore: deprecated_member_use_from_same_package
Future<String> serializeAsync(Object value) async => serialize(value);
Future<String> serializeAsync(Object? value) async => serialize(value);

@Deprecated('Scheduled for removal in OpenAPI Generator 6.x. Use serializeAsync() instead.')
String serialize(Object value) => value == null ? '' : json.encode(value);
String serialize(Object? value) => value == null ? '' : json.encode(value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it ever make sense to serialise null object?


/// Update query and header parameters based on authentication settings.
/// @param authNames The authentications to apply
Expand All @@ -188,7 +183,7 @@ class ApiClient {
}

{{#native_serialization}}
static dynamic _deserialize(dynamic value, String targetType, {bool growable}) {
static dynamic _deserialize(dynamic value, String targetType, {bool? growable}) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to make growable false or true by default? this would avoid doing growable == true a few times below, consider it micro-optimisation. I would also make it false, since other dart apis use immutable structures

try {
switch (targetType) {
case 'String':
Expand Down Expand Up @@ -216,21 +211,21 @@ class ApiClient {
{{/model}}
{{/models}}
default:
Match match;
Match? match;
if (value is List && (match = _regList.firstMatch(targetType)) != null) {
targetType = match[1]; // ignore: parameter_assignments
targetType = match![1]!; // ignore: parameter_assignments
return value
.map<dynamic>((dynamic v) => _deserialize(v, targetType, growable: growable))
.toList(growable: growable);
.toList(growable: growable == true);
}
if (value is Set && (match = _regSet.firstMatch(targetType)) != null) {
targetType = match[1]; // ignore: parameter_assignments
targetType = match![1]!; // ignore: parameter_assignments
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, that's better than my defauling to Object? 👍

return value
.map<dynamic>((dynamic v) => _deserialize(v, targetType, growable: growable))
.toSet();
}
if (value is Map && (match = _regMap.firstMatch(targetType)) != null) {
targetType = match[1]; // ignore: parameter_assignments
targetType = match![1]!; // ignore: parameter_assignments
return Map<String, dynamic>.fromIterables(
value.keys.cast<String>(),
value.values.map<dynamic>((dynamic v) => _deserialize(v, targetType, growable: growable)),
Expand All @@ -249,8 +244,8 @@ class ApiClient {
/// Primarily intended for use in an isolate.
class DeserializationMessage {
const DeserializationMessage({
@required this.json,
@required this.targetType,
required this.json,
required this.targetType,
this.growable,
});

Expand All @@ -261,7 +256,7 @@ class DeserializationMessage {
final String targetType;

/// Whether to make deserialized lists or maps growable.
final bool growable;
final bool? growable;
}

/// Primarily intended for use in an isolate.
Expand All @@ -281,4 +276,4 @@ Future<dynamic> deserializeAsync(DeserializationMessage message) async {
{{/native_serialization}}

/// Primarily intended for use in an isolate.
Future<String> serializeAsync(Object value) async => value == null ? '' : json.encode(value);
Future<String> serializeAsync(Object? value) async => value == null ? '' : json.encode(value);
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ class ApiException implements Exception {
ApiException.withInner(this.code, this.message, this.innerException, this.stackTrace);

int code = 0;
String message;
Exception innerException;
StackTrace stackTrace;
String? message;
Copy link
Contributor

@agilob agilob Oct 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another design question, when does it make sense to raise ApiException without a message?

Exception? innerException;
StackTrace? stackTrace;

@override
String toString() {
Expand Down
Loading