Skip to content

Commit bbcd7bd

Browse files
Yimin Linwing328
authored andcommitted
[Dart 2] Add support for Dart 2 (OpenAPITools#754)
* Add an option for Dart2 * add dart2 samples, update travis * fix dart installation * Upper constraints on the SDK version * Update dependencies * supportDart2 option can now be passed through --additional-properties * Update petstore tests * Update dart2-petstore.sh * Running tests on Dart VM * Fixed JSON deserialization bugs * Fixed missing initialization of postBody * Run bin/dart2-petstore.sh to regenerate libraries * Update pom.xml * Added SDK version constraints in pubspec.mustache * Run bin/dart2-petstore.sh to regenerate libraries * move dart2 test to the end
1 parent 3879b1a commit bbcd7bd

File tree

131 files changed

+10604
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

131 files changed

+10604
-2
lines changed

.travis.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ cache:
3030
- $HOME/perl5
3131
- $HOME/.cargo
3232
- $HOME/.stack
33+
- $HOME/.pub-cache
3334
- $HOME/samples/server/petstore/cpp-pistache/pistache
3435
- $HOME/.npm
3536
- $HOME/.rvm/gems/ruby-2.4.1
@@ -72,6 +73,13 @@ before_install:
7273
- sudo apt-get update -qq
7374
- sudo apt-get install -qq bats
7475
- sudo apt-get install -qq curl
76+
# install dart
77+
#- sudo apt-get update
78+
- sudo apt-get install apt-transport-https
79+
- sudo sh -c 'curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
80+
- sudo sh -c 'curl https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
81+
- sudo apt-get update
82+
- sudo apt-get install dart
7583
# install perl module
7684
#- cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib)
7785
#- cpanm Test::Exception Test::More Log::Any LWP::UserAgent JSON URI:Query Module::Runtime DateTime Module::Find Moose::Role
@@ -96,11 +104,13 @@ install:
96104
# Add Godeps dependencies to GOPATH and PATH
97105
#- eval "$(curl -sL https://raw.githubusercontent.com/travis-ci/gimme/master/gimme | GIMME_GO_VERSION=1.4 bash)"
98106
#- export GOPATH="${TRAVIS_BUILD_DIR}/Godeps/_workspace"
99-
- export PATH="${TRAVIS_BUILD_DIR}/Godeps/_workspace/bin:$HOME/.cargo/bin:$PATH"
107+
- export PATH="${TRAVIS_BUILD_DIR}/Godeps/_workspace/bin:$HOME/.cargo/bin:$PATH:/usr/lib/dart/bin"
100108
#- go version
101109
- gcc -v
102110
- echo $CC
103111
- echo $CXX
112+
- pub version
113+
- dart --version
104114

105115
script:
106116
# fail fast

bin/dart2-petstore.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/sh
2+
3+
SCRIPT="$0"
4+
echo "# START SCRIPT: $SCRIPT"
5+
6+
while [ -h "$SCRIPT" ] ; do
7+
ls=`ls -ld "$SCRIPT"`
8+
link=`expr "$ls" : '.*-> \(.*\)$'`
9+
if expr "$link" : '/.*' > /dev/null; then
10+
SCRIPT="$link"
11+
else
12+
SCRIPT=`dirname "$SCRIPT"`/"$link"
13+
fi
14+
done
15+
16+
if [ ! -d "${APP_DIR}" ]; then
17+
APP_DIR=`dirname "$SCRIPT"`/..
18+
APP_DIR=`cd "${APP_DIR}"; pwd`
19+
fi
20+
21+
executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
22+
23+
if [ ! -f "$executable" ]
24+
then
25+
mvn -B clean package
26+
fi
27+
28+
# if you've executed sbt assembly previously it will use that instead.
29+
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
30+
31+
# Generate non-browserClient
32+
ags="generate -t modules/openapi-generator/src/main/resources/dart -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g dart -o samples/client/petstore/dart2/openapi -DhideGenerationTimestamp=true -DbrowserClient=false --additional-properties supportDart2=true $@"
33+
34+
# then options to generate the library for vm would be:
35+
#ags="generate -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g dart -o samples/client/petstore/dart2/openapi_vm -DbrowserClient=false -DpubName=openapi_vm $@"
36+
java $JAVA_OPTS -jar $executable $ags
37+
38+
# Generate browserClient
39+
ags="generate -t modules/openapi-generator/src/main/resources/dart -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g dart -o samples/client/petstore/dart2/openapi-browser-client -DhideGenerationTimestamp=true -DbrowserClient=true --additional-properties supportDart2=true $@"
40+
java $JAVA_OPTS -jar $executable $ags
41+
42+
# Generate non-browserClient and put it to the flutter sample app
43+
ags="generate -t modules/openapi-generator/src/main/resources/dart -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g dart -o samples/client/petstore/dart2/flutter_petstore/openapi -DhideGenerationTimestamp=true -DbrowserClient=false --additional-properties supportDart2=true $@"
44+
java $JAVA_OPTS -jar $executable $ags
45+
46+
# There is a proposal to allow importing different libraries depending on the environment:
47+
# https://github.com/munificent/dep-interface-libraries
48+
# When this is implemented there will only be one library.
49+
50+
# The current petstore test will then work for both: the browser library and the vm library.

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/DartClientCodegen.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.openapitools.codegen.utils.ModelUtils;
2929

3030
import io.swagger.v3.oas.models.media.*;
31+
import io.swagger.v3.parser.util.SchemaTypeUtil;
3132
import org.slf4j.Logger;
3233
import org.slf4j.LoggerFactory;
3334

@@ -48,6 +49,7 @@ public class DartClientCodegen extends DefaultCodegen implements CodegenConfig {
4849
public static final String PUB_VERSION = "pubVersion";
4950
public static final String PUB_DESCRIPTION = "pubDescription";
5051
public static final String USE_ENUM_EXTENSION = "useEnumExtension";
52+
public static final String SUPPORT_DART2 = "supportDart2";
5153
protected boolean browserClient = true;
5254
protected String pubName = "openapi";
5355
protected String pubVersion = "1.0.0";
@@ -125,6 +127,7 @@ public DartClientCodegen() {
125127
cliOptions.add(new CliOption(PUB_DESCRIPTION, "Description in generated pubspec"));
126128
cliOptions.add(new CliOption(USE_ENUM_EXTENSION, "Allow the 'x-enum-values' extension for enums"));
127129
cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, "source folder for generated code"));
130+
cliOptions.add(CliOption.newBoolean(SUPPORT_DART2, "support dart2").defaultValue(Boolean.FALSE.toString()));
128131
}
129132

130133
@Override
@@ -189,9 +192,14 @@ public void processOpts() {
189192
additionalProperties.put("apiDocPath", apiDocPath);
190193
additionalProperties.put("modelDocPath", modelDocPath);
191194

195+
final Object isSupportDart2 = additionalProperties.get(SUPPORT_DART2);
196+
if (Boolean.TRUE.equals(isSupportDart2) || (isSupportDart2 instanceof String && Boolean.parseBoolean((String)isSupportDart2))) {
197+
embeddedTemplateDir = templateDir = "dart2";
198+
} else {
199+
supportingFiles.add(new SupportingFile("analysis_options.mustache", "", ".analysis_options"));
200+
}
192201
final String libFolder = sourceFolder + File.separator + "lib";
193202
supportingFiles.add(new SupportingFile("pubspec.mustache", "", "pubspec.yaml"));
194-
supportingFiles.add(new SupportingFile("analysis_options.mustache", "", ".analysis_options"));
195203
supportingFiles.add(new SupportingFile("api_client.mustache", libFolder, "api_client.dart"));
196204
supportingFiles.add(new SupportingFile("api_exception.mustache", libFolder, "api_exception.dart"));
197205
supportingFiles.add(new SupportingFile("api_helper.mustache", libFolder, "api_helper.dart"));
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# {{pubName}}
2+
{{#appDescription}}
3+
{{{appDescription}}}
4+
{{/appDescription}}
5+
6+
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
7+
8+
- API version: {{appVersion}}
9+
{{#artifactVersion}}
10+
- Package version: {{artifactVersion}}
11+
{{/artifactVersion}}
12+
{{^hideGenerationTimestamp}}
13+
- Build date: {{generatedDate}}
14+
{{/hideGenerationTimestamp}}
15+
- Build package: {{generatorClass}}
16+
{{#infoUrl}}
17+
For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})
18+
{{/infoUrl}}
19+
20+
## Requirements
21+
22+
Dart 1.20.0 or later OR Flutter 0.0.20 or later
23+
24+
## Installation & Usage
25+
26+
### Github
27+
If this Dart package is published to Github, please include the following in pubspec.yaml
28+
```
29+
name: {{pubName}}
30+
version: {{pubVersion}}
31+
description: {{pubDescription}}
32+
dependencies:
33+
{{pubName}}:
34+
git: https://github.com/{{gitUserId}}/{{gitRepoId}}.git
35+
version: 'any'
36+
```
37+
38+
### Local
39+
To use the package in your local drive, please include the following in pubspec.yaml
40+
```
41+
dependencies:
42+
{{pubName}}:
43+
path: /path/to/{{pubName}}
44+
```
45+
46+
## Tests
47+
48+
TODO
49+
50+
## Getting Started
51+
52+
Please follow the [installation procedure](#installation--usage) and then run the following:
53+
54+
```dart
55+
import 'package:{{pubName}}/api.dart';
56+
{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}
57+
{{#hasAuthMethods}}
58+
{{#authMethods}}
59+
{{#isBasic}}
60+
// TODO Configure HTTP basic authorization: {{{name}}}
61+
//{{pubName}}.api.Configuration.username = 'YOUR_USERNAME';
62+
//{{pubName}}.api.Configuration.password = 'YOUR_PASSWORD';
63+
{{/isBasic}}
64+
{{#isApiKey}}
65+
// TODO Configure API key authorization: {{{name}}}
66+
//{{pubName}}.api.Configuration.apiKey{'{{{keyParamName}}}'} = 'YOUR_API_KEY';
67+
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
68+
//{{pubName}}.api.Configuration.apiKeyPrefix{'{{{keyParamName}}}'} = "Bearer";
69+
{{/isApiKey}}
70+
{{#isOAuth}}
71+
// TODO Configure OAuth2 access token for authorization: {{{name}}}
72+
//{{pubName}}.api.Configuration.accessToken = 'YOUR_ACCESS_TOKEN';
73+
{{/isOAuth}}
74+
{{/authMethods}}
75+
{{/hasAuthMethods}}
76+
77+
var api_instance = new {{classname}}();
78+
{{#allParams}}
79+
var {{paramName}} = {{#isListContainer}}[{{/isListContainer}}{{#isBodyParam}}new {{dataType}}(){{/isBodyParam}}{{^isBodyParam}}{{{example}}}{{/isBodyParam}}{{#isListContainer}}]{{/isListContainer}}; // {{{dataType}}} | {{{description}}}
80+
{{/allParams}}
81+
82+
try {
83+
{{#returnType}}var result = {{/returnType}}api_instance.{{{operationId}}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});
84+
{{#returnType}}
85+
print(result);
86+
{{/returnType}}
87+
} catch (e) {
88+
print("Exception when calling {{classname}}->{{operationId}}: $e\n");
89+
}
90+
{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}}
91+
```
92+
93+
## Documentation for API Endpoints
94+
95+
All URIs are relative to *{{basePath}}*
96+
97+
Class | Method | HTTP request | Description
98+
------------ | ------------- | ------------- | -------------
99+
{{#apiInfo}}{{#apis}}{{#operations}}{{#operation}}*{{classname}}* | [**{{operationId}}**]({{apiDocPath}}/{{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{#summary}}{{summary}}{{/summary}}
100+
{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}
101+
102+
## Documentation For Models
103+
104+
{{#models}}{{#model}} - [{{{classname}}}]({{modelDocPath}}/{{{classname}}}.md)
105+
{{/model}}{{/models}}
106+
107+
## Documentation For Authorization
108+
109+
{{^authMethods}} All endpoints do not require authorization.
110+
{{/authMethods}}{{#authMethods}}{{#last}} Authentication schemes defined for the API:{{/last}}{{/authMethods}}
111+
{{#authMethods}}## {{{name}}}
112+
113+
{{#isApiKey}}- **Type**: API key
114+
- **API key parameter name**: {{{keyParamName}}}
115+
- **Location**: {{#isKeyInQuery}}URL query string{{/isKeyInQuery}}{{#isKeyInHeader}}HTTP header{{/isKeyInHeader}}
116+
{{/isApiKey}}
117+
{{#isBasic}}- **Type**: HTTP basic authentication
118+
{{/isBasic}}
119+
{{#isOAuth}}- **Type**: OAuth
120+
- **Flow**: {{{flow}}}
121+
- **Authorization URL**: {{{authorizationUrl}}}
122+
- **Scopes**: {{^scopes}}N/A{{/scopes}}
123+
{{#scopes}} - **{{{scope}}}**: {{{description}}}
124+
{{/scopes}}
125+
{{/isOAuth}}
126+
127+
{{/authMethods}}
128+
129+
## Author
130+
131+
{{#apiInfo}}{{#apis}}{{^hasMore}}{{infoEmail}}
132+
{{/hasMore}}{{/apis}}{{/apiInfo}}
133+
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
part of {{pubName}}.api;
2+
3+
{{#operations}}
4+
5+
6+
class {{classname}} {
7+
final ApiClient apiClient;
8+
9+
{{classname}}([ApiClient apiClient]) : apiClient = apiClient ?? defaultApiClient;
10+
11+
{{#operation}}
12+
/// {{summary}}
13+
///
14+
/// {{notes}}
15+
{{#returnType}}Future<{{{returnType}}}> {{/returnType}}{{^returnType}}Future {{/returnType}}{{nickname}}({{#allParams}}{{#required}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}{ {{#allParams}}{{^required}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}} }{{/hasOptionalParams}}) async {
16+
Object postBody{{#bodyParam}} = {{paramName}}{{/bodyParam}};
17+
18+
// verify required params are set
19+
{{#allParams}}
20+
{{#required}}
21+
if({{paramName}} == null) {
22+
throw new ApiException(400, "Missing required param: {{paramName}}");
23+
}
24+
{{/required}}
25+
{{/allParams}}
26+
27+
// create path and map variables
28+
String path = "{{{path}}}".replaceAll("{format}","json"){{#pathParams}}.replaceAll("{" + "{{baseName}}" + "}", {{{paramName}}}.toString()){{/pathParams}};
29+
30+
// query params
31+
List<QueryParam> queryParams = [];
32+
Map<String, String> headerParams = {};
33+
Map<String, String> formParams = {};
34+
{{#queryParams}}
35+
{{^required}}
36+
if({{paramName}} != null) {
37+
{{/required}}
38+
queryParams.addAll(_convertParametersForCollectionFormat("{{collectionFormat}}", "{{baseName}}", {{paramName}}));
39+
{{^required}}
40+
}
41+
{{/required}}
42+
{{/queryParams}}
43+
{{#headerParams}}
44+
headerParams["{{baseName}}"] = {{paramName}};
45+
{{/headerParams}}
46+
47+
List<String> contentTypes = [{{#consumes}}"{{{mediaType}}}"{{#hasMore}},{{/hasMore}}{{/consumes}}];
48+
49+
String contentType = contentTypes.length > 0 ? contentTypes[0] : "application/json";
50+
List<String> authNames = [{{#authMethods}}"{{name}}"{{#hasMore}}, {{/hasMore}}{{/authMethods}}];
51+
52+
if(contentType.startsWith("multipart/form-data")) {
53+
bool hasFields = false;
54+
MultipartRequest mp = new MultipartRequest(null, null);
55+
{{#formParams}}
56+
{{^isFile}}
57+
if ({{paramName}} != null) {
58+
hasFields = true;
59+
mp.fields['{{baseName}}'] = parameterToString({{paramName}});
60+
}
61+
{{/isFile}}
62+
{{#isFile}}
63+
if ({{paramName}} != null) {
64+
hasFields = true;
65+
mp.fields['{{baseName}}'] = {{paramName}}.field;
66+
mp.files.add({{paramName}});
67+
}
68+
{{/isFile}}
69+
{{/formParams}}
70+
if(hasFields)
71+
postBody = mp;
72+
}
73+
else {
74+
{{#formParams}}
75+
{{^isFile}}
76+
if ({{paramName}} != null)
77+
formParams['{{baseName}}'] = parameterToString({{paramName}});
78+
{{/isFile}}
79+
{{/formParams}}
80+
}
81+
82+
var response = await apiClient.invokeAPI(path,
83+
'{{httpMethod}}',
84+
queryParams,
85+
postBody,
86+
headerParams,
87+
formParams,
88+
contentType,
89+
authNames);
90+
91+
if(response.statusCode >= 400) {
92+
throw new ApiException(response.statusCode, response.body);
93+
} else if(response.body != null) {
94+
{{#isListContainer}}
95+
{{#returnType}}
96+
return (apiClient.deserialize(response.body, '{{{returnType}}}') as List).map((item) => item as {{returnBaseType}}).toList();
97+
{{/returnType}}
98+
{{/isListContainer}}
99+
{{^isListContainer}}
100+
{{#isMapContainer}}
101+
{{#returnType}}
102+
return new {{{returnType}}}.from(apiClient.deserialize(response.body, '{{{returnType}}}'));
103+
{{/returnType}};
104+
{{/isMapContainer}}
105+
{{^isMapContainer}}
106+
{{#returnType}}
107+
return apiClient.deserialize(response.body, '{{{returnType}}}') as {{{returnType}}};
108+
{{/returnType}}
109+
{{/isMapContainer}}
110+
{{/isListContainer}}
111+
} else {
112+
return{{#returnType}} null{{/returnType}};
113+
}
114+
}
115+
{{/operation}}
116+
}
117+
{{/operations}}

0 commit comments

Comments
 (0)