Skip to content

Commit fa66b2c

Browse files
authored
Merge pull request #148 from appwrite/feat-flutter-null-safety
feat-flutter-null-safety
2 parents d09b929 + fc8478c commit fa66b2c

File tree

20 files changed

+186
-62
lines changed

20 files changed

+186
-62
lines changed

src/SDK/Language/Flutter.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ public function getFiles()
3737
'template' => '/flutter/example/README.md.twig',
3838
'minify' => false,
3939
],
40+
[
41+
'scope' => 'default',
42+
'destination' => '/example/pubspec.yaml',
43+
'template' => '/flutter/example/pubspec.yaml.twig',
44+
'minify' => false,
45+
],
4046
[
4147
'scope' => 'default',
4248
'destination' => 'CHANGELOG.md',

templates/dart/lib/client.dart.twig

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Client {
2222
this.config = {};
2323

2424
assert(endPoint.startsWith(RegExp("http://|https://")), "endPoint $endPoint must start with 'http'");
25+
init();
2526
}
2627

2728

@@ -53,11 +54,9 @@ class Client {
5354
return this;
5455
}
5556

56-
Future init() async {
57-
if(!initialized) {
57+
void init() {
5858
this.http.options.baseUrl = this.endPoint;
5959
this.http.options.validateStatus = (status) => status! < 400;
60-
}
6160
}
6261

6362
Future<Response> call(HttpMethod method, {String path = '', Map<String, String> headers = const {}, Map<String, dynamic> params = const {}, ResponseType? responseType}) async {
@@ -69,8 +68,6 @@ class Client {
6968
};
7069
}
7170

72-
await this.init();
73-
7471
// Origin is hardcoded for testing
7572
Options options = Options(
7673
headers: {...this.headers!, ...headers},
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name: {{ language.params.packageName }}_example
2+
environment:
3+
sdk: '>=2.6.0 <3.0.0'

templates/flutter/lib/client.dart.twig

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ part of {{ language.params.packageName }};
33
class Client {
44
String endPoint;
55
String type = 'unknown';
6-
Map<String, String> headers;
7-
Map<String, String> config;
6+
Map<String, String>? headers;
7+
late Map<String, String> config;
88
bool selfSigned;
99
bool initialized = false;
1010
Dio http;
11-
PersistCookieJar cookieJar;
11+
late PersistCookieJar cookieJar;
1212

13-
Client({this.endPoint = '{{spec.endpoint}}', this.selfSigned = false, Dio http}) : this.http = http ?? Dio() {
13+
Client({this.endPoint = '{{spec.endpoint}}', this.selfSigned = false, Dio? http}) : this.http = http ?? Dio() {
1414
// Platform is not supported in web so if web, set type to web automatically and skip Platform check
1515
if(kIsWeb) {
1616
type = 'web';
@@ -34,6 +34,7 @@ class Client {
3434
this.config = {};
3535

3636
assert(endPoint.startsWith(RegExp("http://|https://")), "endPoint $endPoint must start with 'http'");
37+
init();
3738
}
3839

3940
Future<Directory> _getCookiePath() async {
@@ -67,31 +68,29 @@ class Client {
6768
}
6869

6970
Client addHeader(String key, String value) {
70-
headers[key] = value;
71+
headers![key] = value;
7172

7273
return this;
7374
}
7475

7576
Future init() async {
76-
if(!initialized) {
77-
// if web skip cookie implementation and origin header as those are automatically handled by browsers
78-
if(!kIsWeb) {
77+
// if web skip cookie implementation and origin header as those are automatically handled by browsers
78+
if(!kIsWeb) {
7979
final Directory cookieDir = await _getCookiePath();
80-
cookieJar = new PersistCookieJar(dir:cookieDir.path);
80+
cookieJar = new PersistCookieJar(storage: FileStorage(cookieDir.path));
8181
this.http.interceptors.add(CookieManager(cookieJar));
8282
PackageInfo packageInfo = await PackageInfo.fromPlatform();
83-
addHeader('Origin', 'appwrite-$type://${packageInfo.packageName ?? packageInfo.appName}');
84-
}else{
85-
// if web set httpClientAdapter as BrowserHttpClientAdapter with withCredentials true to make cookies work
83+
addHeader('Origin', 'appwrite-$type://${packageInfo.packageName}');
84+
} else {
85+
// if web set withCredentials true to make cookies work
8686
this.http.options.extra['withCredentials'] = true;
87-
}
88-
89-
this.http.options.baseUrl = this.endPoint;
90-
this.http.options.validateStatus = (status) => status < 400;
9187
}
88+
89+
this.http.options.baseUrl = this.endPoint;
90+
this.http.options.validateStatus = (status) => status! < 400;
9291
}
9392

94-
Future<Response> call(HttpMethod method, {String path = '', Map<String, String> headers = const {}, Map<String, dynamic> params = const {}, ResponseType responseType}) async {
93+
Future<Response> call(HttpMethod method, {String path = '', Map<String, String> headers = const {}, Map<String, dynamic> params = const {}, ResponseType? responseType}) async {
9594
if(selfSigned && !kIsWeb) {
9695
// Allow self signed requests
9796
(http.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (HttpClient client) {
@@ -100,18 +99,21 @@ class Client {
10099
};
101100
}
102101

103-
await this.init();
102+
if(!initialized) {
103+
await this.init();
104+
}
104105

105106
// Origin is hardcoded for testing
106107
Options options = Options(
107-
headers: {...this.headers, ...headers},
108+
headers: {...this.headers!, ...headers},
108109
method: method.name(),
109-
responseType: responseType
110+
responseType: responseType,
111+
listFormat: ListFormat.multiCompatible
110112
);
111113

112114
try {
113115
if(headers['content-type'] == 'multipart/form-data') {
114-
return await http.request(path, data: FormData.fromMap(params), options: options);
116+
return await http.request(path, data: FormData.fromMap(params, ListFormat.multiCompatible), options: options);
115117
}
116118

117119
if (method == HttpMethod.get) {
@@ -128,16 +130,16 @@ class Client {
128130
throw {{spec.title | caseUcfirst}}Exception(e.message);
129131
}
130132
if(responseType == ResponseType.bytes) {
131-
if(e.response.headers['content-type'].contains('application/json')) {
132-
final res = json.decode(utf8.decode(e.response.data));
133+
if(e.response!.headers['content-type']!.contains('application/json')) {
134+
final res = json.decode(utf8.decode(e.response!.data));
133135
throw {{spec.title | caseUcfirst}}Exception(res['message'],res['code'], e.response);
134136
} else {
135137
throw {{spec.title | caseUcfirst}}Exception(e.message);
136138
}
137139
}
138-
throw {{spec.title | caseUcfirst}}Exception(e.response.data['message'],e.response.data['code'], e.response.data);
140+
throw {{spec.title | caseUcfirst}}Exception(e.response!.data['message'],e.response!.data['code'], e.response!.data);
139141
} catch(e) {
140-
throw {{spec.title | caseUcfirst}}Exception(e.message);
142+
throw {{spec.title | caseUcfirst}}Exception(e.toString());
141143
}
142144
}
143145
}

templates/flutter/lib/exception.dart.twig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
part of {{ language.params.packageName }};
22

33
class {{spec.title | caseUcfirst}}Exception implements Exception {
4-
final String message;
5-
final int code;
4+
final String? message;
5+
final int? code;
66
final dynamic response;
77

88
{{spec.title | caseUcfirst}}Exception([this.message = "", this.code, this.response]);

templates/flutter/lib/package.dart.twig

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import 'dart:io';
44
import 'dart:convert';
55
import 'package:universal_html/html.dart' as html;
66
import 'package:dio/dio.dart';
7-
import 'package:meta/meta.dart';
87
import 'package:flutter/foundation.dart';
98
import 'package:flutter_web_auth/flutter_web_auth.dart';
109
import 'package:dio/adapter.dart';
@@ -14,7 +13,7 @@ import 'package:path_provider/path_provider.dart';
1413
import 'package:package_info_plus/package_info_plus.dart';
1514

1615

17-
export 'package:dio/dio.dart' show Response;
16+
export 'package:dio/dio.dart' show Response, MultipartFile;
1817

1918
part 'client.dart';
2019
part 'enums.dart';

templates/flutter/lib/services/service.dart.twig

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
part of {{ language.params.packageName }};
22

33
{% macro parameter(parameter) %}
4-
{% if parameter.name == 'orderType' %}{% if parameter.required %}@required {% endif %}{{ 'OrderType orderType = OrderType.asc' }}{% else %}
5-
{% if parameter.required %}@required {% endif %}{{ parameter.type | typeName }} {{ parameter.name | caseCamel }}{{ parameter | escapeDollarSign | paramDefault }}{% endif %}
4+
{% if parameter.name == 'orderType' %}{% if parameter.required %}required {% endif %}{{ 'OrderType orderType = OrderType.asc' }}{% else %}
5+
{% if parameter.required %}required {% endif %}{{ parameter.type | typeName }} {{ parameter.name | caseCamel }}{{ parameter | escapeDollarSign | paramDefault }}{% endif %}
66
{% endmacro %}
77
{% macro method_parameters(parameters) %}
88
{% if parameters.all|length > 0 %}{{ '{' }}{% for parameter in parameters.all %}{{ _self.parameter(parameter) }}{% if not loop.last %}, {% endif %}{% endfor %}{{ '}' }}{% endif %}
@@ -68,15 +68,15 @@ class {{ service.name | caseUcfirst }} extends Service {
6868

6969
if(kIsWeb) {
7070
html.window.location.href = url.toString();
71-
return null;
71+
return Future.value();
7272
}else{
7373

7474
return FlutterWebAuth.authenticate(
7575
url: url.toString(),
76-
callbackUrlScheme: "appwrite-callback-" + client.config['project']
76+
callbackUrlScheme: "appwrite-callback-" + client.config['project']!
7777
).then((value) async {
7878
Uri url = Uri.parse(value);
79-
Cookie cookie = new Cookie(url.queryParameters['key'], url.queryParameters['secret']);
79+
Cookie cookie = new Cookie(url.queryParameters['key']!, url.queryParameters['secret']!);
8080
cookie.domain = Uri.parse(client.endPoint).host;
8181
cookie.httpOnly = true;
8282
cookie.path = '/';

templates/flutter/pubspec.yaml.twig

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,17 @@ repository: https://github.com/{{sdk.gitUserName}}/{{sdk.gitRepoName}}
66
issue_tracker: https://github.com/appwrite/sdk-generator/issues
77
documentation: {{ spec.contactURL }}
88
environment:
9-
sdk: '>=2.6.0 <3.0.0'
9+
sdk: '>=2.12.0 <3.0.0'
1010
dependencies:
1111
flutter:
1212
sdk: flutter
13-
cookie_jar: ^1.0.1
14-
dio: ^3.0.10
15-
dio_cookie_manager: ^1.0.0
16-
flutter_web_auth: ^0.2.4
17-
meta: ^1.1.8
18-
package_info_plus: ^0.6.3
19-
path_provider: ^1.6.14
20-
universal_html: ^1.2.3
13+
cookie_jar: ^3.0.1
14+
dio: ^4.0.0
15+
dio_cookie_manager: ^2.0.0
16+
flutter_web_auth: ^0.3.0
17+
package_info_plus: ^1.0.0
18+
path_provider: ^2.0.1
19+
universal_html: ^2.0.8
2120
2221
dev_dependencies:
2322
flutter_test:

tests/SDKTest.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ class SDKTest extends TestCase
6060
'supportException' => true,
6161
],
6262

63+
'flutter' => [
64+
'class' => 'Appwrite\SDK\Language\Flutter',
65+
'build' => [
66+
'mkdir -p tests/sdks/flutter/test',
67+
'cp tests/languages/flutter/tests.dart tests/sdks/flutter/test/appwrite_test.dart',
68+
],
69+
'envs' => [
70+
'flutter-stable' => 'docker run --rm -v $(pwd):/app -w /app/tests/sdks/flutter --env PUB_CACHE=vendor cirrusci/flutter:stable sh -c "flutter pub get && flutter test test/appwrite_test.dart"',
71+
],
72+
'supportException' => true,
73+
],
74+
6375
//Skipping for now, enable it once Java SDK is in Good enough shape
6476
/* 'java' => [
6577
'class' => 'Appwrite\SDK\Language\Java',
@@ -188,7 +200,7 @@ public function testHTTPSuccess()
188200
throw new \Exception('Failed to fetch spec from Appwrite server');
189201
}
190202

191-
$whitelist = ['php', 'cli', 'node', 'ruby', 'python', 'typescript', 'deno', 'dotnet', 'dart'];
203+
$whitelist = ['php', 'cli', 'node', 'ruby', 'python', 'typescript', 'deno', 'dotnet', 'dart', 'flutter'];
192204

193205
foreach ($this->languages as $language => $options) {
194206
if (!empty($whitelist) && !in_array($language, $whitelist)) {
@@ -261,6 +273,12 @@ public function testHTTPSuccess()
261273
}
262274

263275
$this->assertIsArray($output);
276+
277+
$removed = '';
278+
do {
279+
$removed = array_shift($output);
280+
} while ($removed != 'Test Started' && sizeof($output) != 0);
281+
264282
$this->assertGreaterThan(10, count($output));
265283

266284
$this->assertEquals('GET:/v1/mock/tests/foo:passed', $output[0] ?? '');

tests/languages/cli/test.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
$output = [];
99
exec($command, $output);
1010

11+
echo "\nTest Started\n";
12+
1113
// Foo Service
1214
$command = "${baseCommand} cli foo get --x='string' --y='123' --z[]='string in array'";
1315
$output = [];

0 commit comments

Comments
 (0)