Skip to content

Commit 0466405

Browse files
author
Christian Loitsch
committed
fix: verification of requiredParams now works
other features: - remove crypto dependency. base64 encoding is now part of dart.convert. - port Java query param handling to dart. → allows us to use 'multi' collectionFormat for queryParams - use async await where possible.
1 parent d213dcd commit 0466405

File tree

7 files changed

+94
-58
lines changed

7 files changed

+94
-58
lines changed

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

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ part of api;
22

33
{{#operations}}
44

5+
56
class {{classname}} {
67
String basePath = "{{basePath}}";
78
ApiClient apiClient = ApiClient.defaultApiClient;
@@ -16,13 +17,17 @@ class {{classname}} {
1617
/// {{summary}}
1718
///
1819
/// {{notes}}
19-
{{#returnType}}Future<{{{returnType}}}> {{/returnType}}{{^returnType}}Future {{/returnType}}{{nickname}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) {
20+
{{#returnType}}Future<{{{returnType}}}> {{/returnType}}{{^returnType}}Future {{/returnType}}{{nickname}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) async {
2021
Object postBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}};
21-
{{#allParams}}
22+
2223
// verify required params are set
23-
if({{/allParams}}{{#required}} {{paramName}} == null {{#hasMore}}|| {{/hasMore}}{{/required}}{{#allParams}}) {
24-
throw new ApiException(400, "missing required params");
25-
}{{/allParams}}
24+
{{#allParams}}
25+
{{#required}}
26+
if({{paramName}} == null) {
27+
throw new ApiException(400, "Missing required param: {{paramName}}");
28+
}
29+
{{/required}}
30+
{{/allParams}}
2631

2732
// create path and map variables
2833
String path = "{{path}}".replaceAll("{format}","json"){{#pathParams}}.replaceAll("{" + "{{paramName}}" + "}", {{{paramName}}}.toString()){{/pathParams}};
@@ -32,7 +37,7 @@ class {{classname}} {
3237
Map<String, String> headerParams = {};
3338
Map<String, String> formParams = {};
3439
{{#queryParams}}if("null" != {{paramName}})
35-
queryParams["{{baseName}}"] = {{paramName}} is List ? {{paramName}}.join(',') : {{paramName}};
40+
queryParams.addAll(convertParametersForCollectionFormat({{collectionFormat}}, {{baseName}}, {{paramName}}));
3641
{{/queryParams}}
3742
{{#headerParams}}headerParams["{{baseName}}"] = {{paramName}};
3843
{{/headerParams}}
@@ -66,17 +71,23 @@ class {{classname}} {
6671
{{/formParams}}
6772
}
6873

69-
return apiClient.invokeAPI(basePath, path, '{{httpMethod}}', queryParams, postBody, headerParams, formParams, contentType, authNames).then((response) {
70-
if(response.statusCode >= 400) {
71-
throw new ApiException(response.statusCode, response.body);
72-
}
73-
else if(response.body != null){
74-
return {{#returnType}}ApiClient.deserialize(response.body, {{returnBaseType}}){{/returnType}};
75-
}
76-
else {
77-
return {{#returnType}}null{{/returnType}};
78-
}
79-
});
74+
var response = await apiClient.invokeAPI(basePath,
75+
path,
76+
'{{httpMethod}}',
77+
queryParams,
78+
postBody,
79+
headerParams,
80+
formParams,
81+
contentType,
82+
authNames);
83+
84+
if(response.statusCode >= 400) {
85+
throw new ApiException(response.statusCode, response.body);
86+
} else if(response.body != null) {
87+
return {{#returnType}}ApiClient.deserialize(response.body, {{returnBaseType}}){{/returnType}};
88+
} else {
89+
return {{#returnType}}null{{/returnType}};
90+
}
8091
}
8192
{{/operation}}
8293
}

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

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,44 @@
11
part of api;
22

3+
class QueryParam {
4+
String name;
5+
String value;
6+
7+
QueryParam(this.name, this.value);
8+
}
9+
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+
342
class ApiClient {
443
static ApiClient defaultApiClient = new ApiClient();
544
@@ -85,71 +124,60 @@ class ApiClient {
85124
return serialized;
86125
}
87126

88-
Future<Response> invokeAPI( String host,
89-
String path,
90-
String method,
91-
Map<String, String> queryParams,
92-
Object body,
93-
Map<String, String> headerParams,
94-
Map<String, String> formParams,
95-
String contentType,
96-
List<String> authNames) {
127+
// We don't use a Map<String, String> for queryParams.
128+
// If collectionFormat is 'multi' a key might appear multiple times.
129+
Future<Response> invokeAPI(String host,
130+
String path,
131+
String method,
132+
List<QueryParam> queryParams,
133+
Object body,
134+
Map<String, String> headerParams,
135+
Map<String, String> formParams,
136+
String contentType,
137+
List<String> authNames) async {
97138
98139
updateParamsForAuth(authNames, queryParams, headerParams);
99140
100141
var client = new {{#browserClient}}Browser{{/browserClient}}Client();
101142

102143
StringBuffer sb = new StringBuffer();
103-
104-
for(String key in queryParams.keys) {
105-
String value = queryParams[key];
106-
if (value != null){
107-
if(sb.toString().length == 0) {
108-
sb.write("?");
109-
} else {
110-
sb.write("&");
111-
}
112-
sb.write(key);
113-
sb.write("=");
114-
sb.write(value);
115-
}
116-
}
117-
String querystring = sb.toString();
118144

119-
String url = host + path + querystring;
145+
var ps = queryParams.where((p) => p.value != null).map((p) => '${p.key}=${p.value}');
146+
String queryString = ps.isNotEmpty ?
147+
'?' + ps.join('&') :
148+
'';
149+
150+
String url = host + path + queryString;
120151

121152
headerParams.addAll(_defaultHeaderMap);
122153
headerParams['Content-Type'] = contentType;
123154

124-
var completer = new Completer();
125-
126155
if(body is MultipartRequest) {
127156
var request = new MultipartRequest(method, Uri.parse(url));
128157
request.fields.addAll(body.fields);
129158
request.files.addAll(body.files);
130159
request.headers.addAll(body.headers);
131160
request.headers.addAll(headerParams);
132-
client.send(request).then((response) => completer.complete(Response.fromStream(response)));
161+
var response = await client.send(request);
162+
return Response.fromStream(response);
133163
} else {
134164
var msgBody = contentType == "application/x-www-form-urlencoded" ? formParams : serialize(body);
135165
switch(method) {
136-
case "GET":
137-
return client.get(url, headers: headerParams);
138166
case "POST":
139167
return client.post(url, headers: headerParams, body: msgBody);
140168
case "PUT":
141169
return client.put(url, headers: headerParams, body: msgBody);
142170
case "DELETE":
143171
return client.delete(url, headers: headerParams);
172+
default:
173+
return client.get(url, headers: headerParams);
144174
}
145175
}
146-
147-
return completer.future;
148176
}
149177

150178
/// Update query and header parameters based on authentication settings.
151179
/// @param authNames The authentications to apply
152-
void updateParamsForAuth(List<String> authNames, Map<String, String> queryParams, Map<String, String> headerParams) {
180+
void updateParamsForAuth(List<String> authNames, List<QueryParam> queryParams, Map<String, String> headerParams) {
153181
authNames.forEach((authName) {
154182
Authentication auth = _authentications[authName];
155183
if (auth == null) throw new ArgumentError("Authentication undefined: " + authName);

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'dart:html';
66
import 'package:http/browser_client.dart';{{/browserClient}}
77
import 'package:http/http.dart';
88
import 'package:dartson/dartson.dart';
9-
import 'package:crypto/crypto.dart';
109
import 'package:intl/intl.dart';
1110

1211
part 'api_client.dart';

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class ApiKeyAuth implements Authentication {
1010
ApiKeyAuth(this.location, this.paramName);
1111
1212
@override
13-
void applyToParams(Map<String, String> queryParams, Map<String, String> headerParams) {
13+
void applyToParams(List<QueryParam> queryParams, Map<String, String> headerParams) {
1414
String value;
1515
if (apiKeyPrefix != null) {
1616
value = '$apiKeyPrefix $apiKey';
@@ -19,10 +19,9 @@ class ApiKeyAuth implements Authentication {
1919
}
2020

2121
if (location == 'query' && value != null) {
22-
queryParams[paramName] = value;
22+
queryParams.add(new QueryParam(paramName, value));
2323
} else if (location == 'header' && value != null) {
2424
headerParams[paramName] = value;
2525
}
2626
}
27-
2827
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ part of api;
33
abstract class Authentication {
44
55
/// Apply authentication settings to header and query params.
6-
void applyToParams(Map<String, String> queryParams, Map<String, String> headerParams);
6+
void applyToParams(List<QueryParam> queryParams, Map<String, String> headerParams);
77
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ class HttpBasicAuth implements Authentication {
66
String password;
77
88
@override
9-
void applyToParams(Map<String, String> queryParams, Map<String, String> headerParams) {
9+
void applyToParams(List<QueryParam> queryParams, Map<String, String> headerParams) {
1010
String str = (username == null ? "" : username) + ":" + (password == null ? "" : password);
11-
headerParams["Authorization"] = "Basic " + CryptoUtils.bytesToBase64(UTF8.encode(str));
11+
headerParams["Authorization"] = "Basic " + BASE64.encode(UTF8.encode(str));
1212
}
1313

1414
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ description: {{pubDescription}}
44
dependencies:
55
http: '>=0.11.1 <0.12.0'
66
dartson: "^0.2.4"
7-
crypto: "^0.9.0"
87
intl: "^0.12.4+2"
98
109
dev_dependencies:

0 commit comments

Comments
 (0)