Skip to content

Commit

Permalink
[Dart] - Rework Dart client generator to be flutter-compatible (#7418)
Browse files Browse the repository at this point in the history
* copy mustache templates from dart generator

* Start with generator by copying the DartClientCodegen for now

* at least we know this is not for a browser..

* First working version for a simple swagger configuration

* remove browserClient parameter, since it doesn't make sense for flutter

* Take care of complex types to support object hierarchies

* add null safety

* add small test for options

* add flutter-petstore scripts

* generate flutter petstore output

* Add new flutter test project

* move generated client to make it usable

* use generated swagger petstore plugin

* add support for lists of objects

* add DateTime support

* fix listFromJson implementation

* fix NPEs in DateTime operations + place order in sample

* Small readme changes

* bugfixes

* Use flutter-compatible implementation as default dart implementation

* fix generated samples

* Make lists serializable, now all dart test cases are working again

* better list implementation

* use StringBuffer

* removed FlutterClientCodegen

* fix browser client

* fix dependencies

* swagger-browser-client for browserClient testcases

* fix scripts

* removed flutter scripts

* add map support and simplify code via using .toJson contract

* remove unneeded devDependencies

* Regenerated samples

* fix call to mapFromJson, it is not a constructor

* remove pointless string serialization

* regenerated dart samples
  • Loading branch information
Jörn Ahrens authored and wing328 committed Jan 25, 2018
1 parent a3c9775 commit a5e26a4
Show file tree
Hide file tree
Showing 155 changed files with 8,041 additions and 124 deletions.
11 changes: 10 additions & 1 deletion bin/dart-petstore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,21 @@ fi
# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"

ags="$@ generate -t modules/swagger-codegen/src/main/resources/dart -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l dart -o samples/client/petstore/dart/swagger -DhideGenerationTimestamp=true"
# Generate non-browserClient
ags="$@ generate -t modules/swagger-codegen/src/main/resources/dart -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l dart -o samples/client/petstore/dart/swagger -DhideGenerationTimestamp=true -DbrowserClient=false"

# then options to generate the library for vm would be:
#ags="$@ generate -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l dart -o samples/client/petstore/dart/swagger_vm -DbrowserClient=false -DpubName=swagger_vm"
java $JAVA_OPTS -jar $executable $ags

# Generate browserClient
ags="$@ generate -t modules/swagger-codegen/src/main/resources/dart -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l dart -o samples/client/petstore/dart/swagger-browser-client -DhideGenerationTimestamp=true -DbrowserClient=true"
java $JAVA_OPTS -jar $executable $ags

# Generate non-browserClient and put it to the flutter sample app
ags="$@ generate -t modules/swagger-codegen/src/main/resources/dart -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l dart -o samples/client/petstore/dart/flutter_petstore/swagger -DhideGenerationTimestamp=true -DbrowserClient=false"
java $JAVA_OPTS -jar $executable $ags

# There is a proposal to allow importing different libraries depending on the environment:
# https://github.com/munificent/dep-interface-libraries
# When this is implemented there will only be one library.
Expand Down
7 changes: 6 additions & 1 deletion bin/windows/dart-petstore.bat
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ If Not Exist %executable% (
)

REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -DloggerPath=conf/log4j.properties
set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l dart -o samples\client\petstore\dart
set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l dart -o samples\client\petstore\dart\swagger -DhideGenerationTimestamp=true -DbrowserClient=false
java %JAVA_OPTS% -jar %executable% %ags%

set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l dart -o samples\client\petstore\dart\swagger-browser-client -DhideGenerationTimestamp=true -DbrowserClient=true
java %JAVA_OPTS% -jar %executable% %ags%

set ags=generate -i modules\swagger-codegen\src\test\resources\2_0\petstore.yaml -l dart -o samples\client\petstore\dart\flutter_petstore\swagger -DhideGenerationTimestamp=true -DbrowserClient=false
java %JAVA_OPTS% -jar %executable% %ags%
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 1.20.0 and later
Dart 1.20.0 or later OR Flutter 0.0.20 or later

## Installation & Usage

Expand Down
4 changes: 2 additions & 2 deletions modules/swagger-codegen/src/main/resources/dart/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class {{classname}} {
{{#formParams}}{{#notFile}}
if ({{paramName}} != null) {
hasFields = true;
mp.fields['{{baseName}}'] = apiClient.parameterToString({{paramName}});
mp.fields['{{baseName}}'] = parameterToString({{paramName}});
}
{{/notFile}}{{#isFile}}
if ({{paramName}} != null) {
Expand All @@ -68,7 +68,7 @@ class {{classname}} {
}
else {
{{#formParams}}{{#notFile}}if ({{paramName}} != null)
formParams['{{baseName}}'] = apiClient.parameterToString({{paramName}});{{/notFile}}
formParams['{{baseName}}'] = parameterToString({{paramName}});{{/notFile}}
{{/formParams}}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ class ApiClient {
Map<String, String> _defaultHeaderMap = {};
Map<String, Authentication> _authentications = {};

final dson = new Dartson.JSON()
{{#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 @@ -61,7 +51,7 @@ class ApiClient {
return listResult[0];
{{/isEnum}}
{{^isEnum}}
return dson.map(value, new {{classname}}());
return new {{classname}}.fromJson(value);
{{/isEnum}}
{{/model}}
{{/models}}
Expand Down Expand Up @@ -100,10 +90,8 @@ class ApiClient {
String serialized = '';
if (obj == null) {
serialized = '';
} else if (obj is String) {
serialized = obj;
} else {
serialized = dson.encode(obj);
serialized = JSON.encode(obj);
}
return serialized;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Iterable<QueryParam> _convertParametersForCollectionFormat(
if (name == null || name.isEmpty || value == null) return params;
if (value is! List) {
params.add(new QueryParam(name, _parameterToString(value)));
params.add(new QueryParam(name, parameterToString(value)));
return params;
}

Expand All @@ -23,17 +23,17 @@ Iterable<QueryParam> _convertParametersForCollectionFormat(
: collectionFormat; // default: csv

if (collectionFormat == "multi") {
return values.map((v) => new QueryParam(name, _parameterToString(v)));
return values.map((v) => new QueryParam(name, parameterToString(v)));
}

String delimiter = _delimiters[collectionFormat] ?? ",";

params.add(new QueryParam(name, values.map((v)=>_parameterToString(v)).join(delimiter)));
params.add(new QueryParam(name, values.map((v) => parameterToString(v)).join(delimiter)));
return params;
}

/// Format the given parameter object into string.
String _parameterToString(dynamic value) {
String parameterToString(dynamic value) {
if (value == null) {
return '';
} else if (value is DateTime) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import 'dart:async';
import 'dart:convert';{{#browserClient}}
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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ class ApiKeyAuth implements Authentication {
headerParams[paramName] = value;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ class OAuth implements Authentication {
void applyToParams(List<QueryParam> queryParams, Map<String, String> headerParams) {
// TODO: support oauth
}
}
}
46 changes: 43 additions & 3 deletions modules/swagger-codegen/src/main/resources/dart/class.mustache
Original file line number Diff line number Diff line change
@@ -1,14 +1,54 @@
@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() {
String toString() {
return '{{classname}}[{{#vars}}{{name}}=${{name}}, {{/vars}}]';
}

{{classname}}.fromJson(Map<String, dynamic> json) {
if (json == null) return;
{{#vars}}
{{#isDateTime}}
{{name}} = json['{{name}}'] == null ? null : DateTime.parse(json['{{name}}']);
{{/isDateTime}}
{{^isDateTime}}
{{name}} =
{{#complexType}}
{{#isListContainer}}{{complexType}}.listFromJson(json['{{name}}']){{/isListContainer}}{{^isListContainer}}
{{#isMapContainer}}{{complexType}}.mapFromJson(json['{{name}}']){{/isMapContainer}}
{{^isMapContainer}}new {{complexType}}.fromJson(json['{{name}}']){{/isMapContainer}}{{/isListContainer}}
{{/complexType}}
{{^complexType}}json['{{name}}']{{/complexType}};
{{/isDateTime}}
{{/vars}}
}

Map<String, dynamic> toJson() {
return {
{{#vars}}
{{#isDateTime}}'{{name}}': {{name}} == null ? '' : {{name}}.toUtc().toIso8601String(){{^-last}},{{/-last}}{{/isDateTime}}{{^isDateTime}}'{{name}}': {{name}}{{^-last}},{{/-last}}{{/isDateTime}}
{{/vars}}
};
}

static List<{{classname}}> listFromJson(List<Map<String, dynamic>> json) {
var list = new List<{{classname}}>();
if (json != null && json.length > 0) {
json.forEach((Map<String, dynamic> value) => list.add(new {{classname}}.fromJson(value)));
}
return list;
}

static Map<String, {{classname}}> mapFromJson(Map<String, Map<String, dynamic>> json) {
var map = new Map<String, {{classname}}>();
if (json != null && json.length > 0) {
json.forEach((String key, Map<String, dynamic> value) => map[key] = new {{classname}}.fromJson(value));
}
return map;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,3 @@ version: {{pubVersion}}
description: {{pubDescription}}
dependencies:
http: '>=0.11.1 <0.12.0'
dartson: "^0.2.4"
dev_dependencies:
guinness: '^0.1.17'
browser: any
transformers:
- dartson
10 changes: 10 additions & 0 deletions samples/client/petstore/dart/flutter_petstore/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.DS_Store
.atom/
.idea
.packages
.pub/
build/
ios/.generated/
packages
pubspec.lock
.flutter-plugins
8 changes: 8 additions & 0 deletions samples/client/petstore/dart/flutter_petstore/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: 8f65fec5f5f7d7afbb0965f4a44bdb330a28fb19
channel: alpha
8 changes: 8 additions & 0 deletions samples/client/petstore/dart/flutter_petstore/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# flutter_petstore

Swagger petstore sample flutter

## Getting Started

For help getting started with Flutter, view our online
[documentation](http://flutter.io/).
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
GeneratedPluginRegistrant.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withInputStream { stream ->
localProperties.load(stream)
}
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion 25
buildToolsVersion '25.0.3'

lintOptions {
disable 'InvalidPackage'
}

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.yourcompany.flutterpetstore"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}

flutter {
source '../..'
}

dependencies {
androidTestCompile 'com.android.support:support-annotations:25.4.0'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourcompany.flutterpetstore">

<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>

<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="flutter_petstore"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.yourcompany.flutterpetstore;

import android.os.Bundle;

import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />

<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>
Loading

0 comments on commit a5e26a4

Please sign in to comment.