Skip to content

Commit 9a65a5f

Browse files
author
Christian Loitsch
committed
feat: improve / fix deserialization by parsing type String
* added api_helper.dart for a helper function. * defaultApiClient is now a variable instead of a static field inside ApiClient * a lot of functions inside ApiClient are no longer static. * optional params are now named params (needed to introduce a justIgnoreFlag as hack) * queryParams now support the multi format and are therefore no longer a Map<String, String>, but a List<QueryParam> * renamed apiException.mustache to api_exception.mustache to conform with other file names. * removed unused import: 'dart:html' * removed 'package:crypto/crypto.dart' dependency. 'dart:convert' has a base64 converter now. * use null-aware operator for apiClient assignment in xxxApi constructors. * enable testStoreApi (which returned a Future nobody waited for) * fix types in tests. Some ids were passed as Strings instead of ints. * adapt tests to use the optional named arguments (for optional query args) * generate random ids in tests. Otherwise insertion will always succeed if the test has been called once.
1 parent 0466405 commit 9a65a5f

23 files changed

+1029
-631
lines changed

modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/DartClientCodegen.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ public void processOpts() {
141141
final String libFolder = sourceFolder + File.separator + "lib";
142142
supportingFiles.add(new SupportingFile("pubspec.mustache", "", "pubspec.yaml"));
143143
supportingFiles.add(new SupportingFile("api_client.mustache", libFolder, "api_client.dart"));
144-
supportingFiles.add(new SupportingFile("apiException.mustache", libFolder, "api_exception.dart"));
144+
supportingFiles.add(new SupportingFile("api_exception.mustache", libFolder, "api_exception.dart"));
145+
supportingFiles.add(new SupportingFile("api_helper.mustache", libFolder, "api_helper.dart"));
145146
supportingFiles.add(new SupportingFile("apilib.mustache", libFolder, "api.dart"));
146147

147148
final String authFolder = sourceFolder + File.separator + "lib" + File.separator + "auth";

modules/swagger-codegen/src/main/resources/dart/api.mustache

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ part of api;
55

66
class {{classname}} {
77
String basePath = "{{basePath}}";
8-
ApiClient apiClient = ApiClient.defaultApiClient;
8+
final ApiClient apiClient;
99
10-
{{classname}}([ApiClient apiClient]) {
11-
if (apiClient != null) {
12-
this.apiClient = apiClient;
13-
}
14-
}
10+
{{classname}}([ApiClient apiClient]) : apiClient = apiClient ?? defaultApiClient;
1511

1612
{{#operation}}
1713
/// {{summary}}
1814
///
1915
/// {{notes}}
20-
{{#returnType}}Future<{{{returnType}}}> {{/returnType}}{{^returnType}}Future {{/returnType}}{{nickname}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) async {
16+
{{#returnType}}Future<{{{returnType}}}> {{/returnType}}{{^returnType}}Future {{/returnType}}{{nickname}}({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}, {{/required}}{{/allParams}} { {{#allParams}}{{^required}} {{{dataType}}} {{paramName}}, {{/required}}{{/allParams}} bool justIgnoreThisFlag: true}) async {
17+
if (!justIgnoreThisFlag) {
18+
print('Why??? Just trust me, I only need this variable inside the mustache codegen template.');
19+
// This code may be removed as soon as dart accepts trailing spaces (has already been implemented).
20+
}
2121
Object postBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}};
2222

2323
// verify required params are set
@@ -33,11 +33,13 @@ class {{classname}} {
3333
String path = "{{path}}".replaceAll("{format}","json"){{#pathParams}}.replaceAll("{" + "{{paramName}}" + "}", {{{paramName}}}.toString()){{/pathParams}};
3434

3535
// query params
36-
Map<String, String> queryParams = {};
36+
List<QueryParam> queryParams = [];
3737
Map<String, String> headerParams = {};
3838
Map<String, String> formParams = {};
39-
{{#queryParams}}if("null" != {{paramName}})
40-
queryParams.addAll(convertParametersForCollectionFormat({{collectionFormat}}, {{baseName}}, {{paramName}}));
39+
{{#queryParams}}
40+
if("null" != {{paramName}}) {
41+
queryParams.addAll(_convertParametersForCollectionFormat("{{collectionFormat}}", "{{baseName}}", {{paramName}}));
42+
}
4143
{{/queryParams}}
4244
{{#headerParams}}headerParams["{{baseName}}"] = {{paramName}};
4345
{{/headerParams}}
@@ -84,7 +86,7 @@ class {{classname}} {
8486
if(response.statusCode >= 400) {
8587
throw new ApiException(response.statusCode, response.body);
8688
} else if(response.body != null) {
87-
return {{#returnType}}ApiClient.deserialize(response.body, {{returnBaseType}}){{/returnType}};
89+
return {{#returnType}} apiClient.deserialize(response.body, '{{{returnType}}}') {{/returnType}};
8890
} else {
8991
return {{#returnType}}null{{/returnType}};
9092
}

modules/swagger-codegen/src/main/resources/dart/api_client.mustache

Lines changed: 54 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,19 @@ class QueryParam {
77
QueryParam(this.name, this.value);
88
}
99

10-
const _delimiters = const {'csv': ',', 'ssv': ' ', 'tsv': '\t', 'pipes': '|'};
11-
12-
// port from Java version
13-
List<QueryParam> convertParametersForCollectionFormat(
14-
String collectionFormat, String name, dynamic value) {
15-
var params = {};
16-
17-
// preconditions
18-
if (name == null || name.isEmpty || value == null) return params;
19-
20-
if (value is! List) {
21-
params.add(new QueryParam(name, value as String));
22-
return params;
23-
}
24-
25-
List<String> values = value as List<String>;
26-
27-
// get the collection format
28-
collectionFormat = (collectionFormat == null || collectionFormat.isEmpty)
29-
? "csv"
30-
: collectionFormat; // default: csv
31-
32-
if (collectionFormat == "multi") {
33-
return values.map((v) => new QueryParam(name, v));
34-
}
35-
36-
String delimiter = _delimiters[collectionFormat] ?? ",";
37-
38-
params.add(new QueryParam(name, values.join(delimiter)));
39-
return params;
40-
}
41-
4210
class ApiClient {
43-
static ApiClient defaultApiClient = new ApiClient();
11+
12+
var client = new {{#browserClient}}Browser{{/browserClient}}Client();
4413

4514
Map<String, String> _defaultHeaderMap = {};
4615
Map<String, Authentication> _authentications = {};
47-
static final dson = new Dartson.JSON();
16+
17+
final dson = new Dartson.JSON();
4818
final DateFormat _dateFormatter = new DateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
4919

20+
final _RegList = new RegExp(r'^List<(.*)>$');
21+
final _RegMap = new RegExp(r'^Map<String,(.*)>$');
22+
5023
ApiClient() {
5124
// Setup authentications (key: authentication name, value: authentication).{{#authMethods}}{{#isBasic}}
5225
_authentications['{{name}}'] = new HttpBasicAuth();{{/isBasic}}{{#isApiKey}}
@@ -76,43 +49,57 @@ class ApiClient {
7649
}
7750
}
7851

79-
static dynamic deserialize(String json, dynamic clazz) {
80-
var result = json;
81-
52+
dynamic _deserialize(dynamic value, String targetType) {
8253
try {
83-
var decodedJson = JSON.decode(json);
84-
85-
if(decodedJson is List) {
86-
result = [];
87-
for(var obj in decodedJson) {
88-
result.add(_createEntity(obj, clazz));
89-
}
90-
} else {
91-
result = _createEntity(json, clazz);
54+
switch (targetType) {
55+
case 'String':
56+
return '$value';
57+
case 'int':
58+
return value is int ? value : int.parse('$value');
59+
case 'bool':
60+
return value is bool ? value : '$value'.toLowerCase() == 'true';
61+
case 'double':
62+
return value is double ? value : double.parse('$value');
63+
{{#models}}
64+
{{#model}}
65+
case '{{classname}}':
66+
return dson.map(value, new {{classname}}());
67+
{{/model}}
68+
{{/models}}
69+
default:
70+
{
71+
Match match;
72+
if (value is List &&
73+
(match = _RegList.firstMatch(targetType)) != null) {
74+
var valueL = value as List;
75+
var newTargetType = match[1];
76+
return valueL.map((v) => _deserialize(v, newTargetType)).toList();
77+
} else if (value is Map &&
78+
(match = _RegMap.firstMatch(targetType)) != null) {
79+
var valueM = value as Map;
80+
var newTargetType = match[1];
81+
return new Map.fromIterables(valueM.keys,
82+
valueM.values.map((v) => _deserialize(v, newTargetType)));
83+
}
84+
}
9285
}
93-
} on FormatException {
94-
// Just return the passed in value
86+
} catch(e) {
87+
// Just throw the ApiException below
9588
}
96-
97-
return result;
89+
throw new ApiException(500, 'Could not find a suitable class for deserialization');
9890
}
9991

100-
static dynamic _createEntity(dynamic json, dynamic clazz) {
101-
bool isMap = json is Map;
102-
103-
switch(clazz) {
104-
{{#models}}
105-
{{#model}}
106-
case {{classname}}:
107-
return isMap ? dson.map(json, new {{classname}}()) : dson.decode(json, new {{classname}}());
108-
{{/model}}
109-
{{/models}}
110-
default:
111-
throw new ApiException(500, 'Could not find a suitable class for deserialization');
112-
}
92+
dynamic deserialize(String json, String targetType) {
93+
// Remove all spaces. Necessary for reg expressions as well.
94+
targetType = targetType.replaceAll(' ', '');
95+
96+
if (targetType == 'String') return json;
97+
98+
var decodedJson = JSON.decode(json);
99+
return _deserialize(decodedJson, targetType);
113100
}
114101

115-
static String serialize(Object obj) {
102+
String serialize(Object obj) {
116103
String serialized = '';
117104
if (obj == null) {
118105
serialized = '';
@@ -136,13 +123,9 @@ class ApiClient {
136123
String contentType,
137124
List<String> authNames) async {
138125
139-
updateParamsForAuth(authNames, queryParams, headerParams);
126+
_updateParamsForAuth(authNames, queryParams, headerParams);
140127
141-
var client = new {{#browserClient}}Browser{{/browserClient}}Client();
142-
143-
StringBuffer sb = new StringBuffer();
144-
145-
var ps = queryParams.where((p) => p.value != null).map((p) => '${p.key}=${p.value}');
128+
var ps = queryParams.where((p) => p.value != null).map((p) => '${p.name}=${p.value}');
146129
String queryString = ps.isNotEmpty ?
147130
'?' + ps.join('&') :
148131
'';
@@ -177,12 +160,11 @@ class ApiClient {
177160

178161
/// Update query and header parameters based on authentication settings.
179162
/// @param authNames The authentications to apply
180-
void updateParamsForAuth(List<String> authNames, List<QueryParam> queryParams, Map<String, String> headerParams) {
163+
void _updateParamsForAuth(List<String> authNames, List<QueryParam> queryParams, Map<String, String> headerParams) {
181164
authNames.forEach((authName) {
182165
Authentication auth = _authentications[authName];
183166
if (auth == null) throw new ArgumentError("Authentication undefined: " + authName);
184167
auth.applyToParams(queryParams, headerParams);
185168
});
186169
}
187-
188170
}
File renamed without changes.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
part of api;
2+
3+
const _delimiters = const {'csv': ',', 'ssv': ' ', 'tsv': '\t', 'pipes': '|'};
4+
5+
// port from Java version
6+
List<QueryParam> _convertParametersForCollectionFormat(
7+
String collectionFormat, String name, dynamic value) {
8+
var params = [];
9+
10+
// preconditions
11+
if (name == null || name.isEmpty || value == null) return params;
12+
13+
if (value is! List) {
14+
params.add(new QueryParam(name, value as String));
15+
return params;
16+
}
17+
18+
List<String> values = value as List<String>;
19+
20+
// get the collection format
21+
collectionFormat = (collectionFormat == null || collectionFormat.isEmpty)
22+
? "csv"
23+
: collectionFormat; // default: csv
24+
25+
if (collectionFormat == "multi") {
26+
return values.map((v) => new QueryParam(name, v));
27+
}
28+
29+
String delimiter = _delimiters[collectionFormat] ?? ",";
30+
31+
params.add(new QueryParam(name, values.join(delimiter)));
32+
return params;
33+
}

modules/swagger-codegen/src/main/resources/dart/apilib.mustache

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ library api;
22

33
import 'dart:async';
44
import 'dart:convert';{{#browserClient}}
5-
import 'dart:html';
65
import 'package:http/browser_client.dart';{{/browserClient}}
76
import 'package:http/http.dart';
87
import 'package:dartson/dartson.dart';
98
import 'package:intl/intl.dart';
109

1110
part 'api_client.dart';
11+
part 'api_helper.dart';
1212
part 'api_exception.dart';
1313
part 'auth/authentication.dart';
1414
part 'auth/api_key_auth.dart';
@@ -19,3 +19,6 @@ part 'auth/http_basic_auth.dart';
1919
{{/apis}}{{/apiInfo}}
2020
{{#models}}{{#model}}part 'model/{{classFilename}}.dart';
2121
{{/model}}{{/models}}
22+
23+
ApiClient defaultApiClient = new ApiClient();
24+

modules/swagger-codegen/src/main/resources/dart/auth/oauth.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ part of api;
33
class OAuth implements Authentication {
44
55
@override
6-
void applyToParams(Map<String, String> queryParams, Map<String, String> headerParams) {
6+
void applyToParams(List<QueryParam> queryParams, Map<String, String> headerParams) {
77
// TODO: support oauth
88
}
99
}

samples/client/petstore/dart/lib/api.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ library api;
22

33
import 'dart:async';
44
import 'dart:convert';
5-
import 'dart:html';
65
import 'package:http/browser_client.dart';
76
import 'package:http/http.dart';
87
import 'package:dartson/dartson.dart';
9-
import 'package:crypto/crypto.dart';
108
import 'package:intl/intl.dart';
119

1210
part 'api_client.dart';
11+
part 'api_helper.dart';
1312
part 'api_exception.dart';
1413
part 'auth/authentication.dart';
1514
part 'auth/api_key_auth.dart';
@@ -27,3 +26,6 @@ part 'model/pet.dart';
2726
part 'model/tag.dart';
2827
part 'model/user.dart';
2928

29+
30+
ApiClient defaultApiClient = new ApiClient();
31+

0 commit comments

Comments
 (0)