Skip to content

Commit 48c2b85

Browse files
Merge pull request #251 from lohanidamodar/feat-refactored-response-model
Flutter Response Model with Refactored SDK
2 parents 1e7b9ad + 31943db commit 48c2b85

File tree

10 files changed

+159
-25
lines changed

10 files changed

+159
-25
lines changed

src/SDK/Language/Flutter.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,24 @@ public function getFiles()
235235
'template' => 'flutter/lib/src/exception.dart.twig',
236236
'minify' => false,
237237
],
238+
[
239+
'scope' => 'default',
240+
'destination' => '/lib/models.dart',
241+
'template' => 'flutter/lib/models.dart.twig',
242+
'minify' => false,
243+
],
238244
[
239245
'scope' => 'service',
240246
'destination' => '/lib/services/{{service.name | caseDash}}.dart',
241247
'template' => 'flutter/lib/services/service.dart.twig',
242248
'minify' => false,
243249
],
250+
[
251+
'scope' => 'definition',
252+
'destination' => '/lib/src/models/{{definition.name | caseSnake }}.dart',
253+
'template' => '/flutter/lib/src/models/model.dart.twig',
254+
'minify' => false,
255+
],
244256
[
245257
'scope' => 'method',
246258
'destination' => 'docs/examples/{{service.name | caseLower}}/{{method.name | caseDash}}.md',

src/SDK/SDK.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ public function __construct(Language $language, Spec $spec)
193193
$this->twig->addFilter(new TwigFilter('caseHTML', function ($value) {
194194
return $value;
195195
}, ['is_safe' => ['html']]));
196+
$this->twig->addFilter(new TwigFilter('removeDollarSign', function ($value) {
197+
return str_replace('$','',$value);
198+
}));
196199
}
197200

198201
/**
@@ -524,6 +527,7 @@ public function generate($target)
524527
'contactURL' => $this->spec->getContactURL(),
525528
'contactEmail' => $this->spec->getContactEmail(),
526529
'services' => $this->spec->getServices(),
530+
'definitions' => $this->spec->getDefinitions(),
527531
'global' => [
528532
'headers' => $this->spec->getGlobalHeaders(),
529533
'defaultHeaders' => $this->defaultHeaders,
@@ -570,6 +574,14 @@ public function generate($target)
570574
$this->render($template, $destination, $block, $params, $minify);
571575
}
572576
break;
577+
case 'definition':
578+
foreach ($this->spec->getDefinitions() as $key => $definition) {
579+
580+
$params['definition'] = $definition;
581+
582+
$this->render($template, $destination, $block, $params, $minify);
583+
}
584+
break;
573585
case 'method':
574586
foreach ($this->spec->getServices() as $key => $service) {
575587
$methods = $this->spec->getMethods($key);

src/Spec/Spec.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ abstract public function getMethods($service);
109109
*/
110110
abstract public function getGlobalHeaders();
111111

112+
/**
113+
* @return array
114+
*/
115+
abstract public function getDefinitions();
116+
112117
/**
113118
* Get Attribute
114119
*

src/Spec/Swagger2.php

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,17 @@ public function getMethods($service)
146146
}
147147
}
148148

149+
$responses = $method['responses'];
150+
$responseModel = '';
151+
foreach($responses as $code => $desc) {
152+
if(isset($desc['schema']) && isset($desc['schema']['$ref'])) {
153+
$responseModel = $desc['schema']['$ref'];
154+
if(!empty($responseModel)) {
155+
$responseModel = str_replace('#/definitions/', '', $responseModel);
156+
}
157+
}
158+
}
159+
149160
$output = [
150161
'method' => $methodName,
151162
'path' => $pathName,
@@ -165,7 +176,8 @@ public function getMethods($service)
165176
'path' => [],
166177
'query' => [],
167178
'body' => [],
168-
]
179+
],
180+
'responseModel' => $responseModel,
169181
];
170182

171183
if(isset($method['consumes']) && is_array($method['consumes'])) {
@@ -274,4 +286,30 @@ public function getGlobalHeaders()
274286

275287
return $list;
276288
}
289+
290+
public function getDefinitions() {
291+
$list = [];
292+
$definition = $this->getAttribute('definitions',[]);
293+
foreach ($definition as $key => $schema) {
294+
if($key == 'any') continue;
295+
$sch = [
296+
"name" => $key,
297+
"properties"=> $schema['properties'] ?? [],
298+
"required" => $schema['required'] ?? [],
299+
"additionalProperties" => $schema['additionalProperties'] ?? []
300+
];
301+
if(isset($sch['properties'])) {
302+
foreach($sch['properties'] as $name => $def) {
303+
$sch['properties'][$name]['name'] = $name;
304+
$sch['properties'][$name]['required'] = in_array($name,$sch['required']);
305+
if(isset($def['items']['$ref'])) {
306+
//nested model
307+
$sch['properties'][$name]['sub_schema'] = str_replace('#/definitions/', '', $def['items']['$ref']);
308+
}
309+
}
310+
}
311+
$list[$key] = $sch;
312+
}
313+
return $list;
314+
}
277315
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
library {{ language.params.packageName }}.models;
2+
3+
{% for definition in spec.definitions %}
4+
part 'src/models/{{definition.name | caseSnake}}.dart';
5+
{% endfor %}

templates/flutter/lib/package.dart.twig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
library {{ language.params.packageName }};
22

33
import 'dart:async';
4+
import 'dart:typed_data';
45
import 'package:http/http.dart' as http;
56
import 'package:flutter/foundation.dart';
67
import 'src/redirect_stub.dart'
@@ -9,6 +10,7 @@ import 'src/enums.dart';
910
import 'src/client.dart';
1011
import 'src/response.dart';
1112
import 'src/service.dart';
13+
import 'models.dart' as models;
1214

1315
export 'src/response.dart';
1416
export 'src/client.dart';

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ part of {{ language.params.packageName }};
66
{% if parameters.all|length > 0 %}{{ '{' }}{% for parameter in parameters.all %}{{ _self.parameter(parameter) }}{% if not loop.last %}, {% endif %}{% endfor %}{{ '}' }}{% endif %}
77
{% endmacro %}
88
{% macro map_parameter(parameter) %}'{{ parameter.name }}': {{ parameter.name | caseCamel | escapeKeyword }},{% endmacro %}
9-
109
class {{ service.name | caseUcfirst }} extends Service {
1110
{{ service.name | caseUcfirst }}(Client client): super(client);
1211
{% for method in service.methods %}
@@ -17,7 +16,7 @@ class {{ service.name | caseUcfirst }} extends Service {
1716
{{ method.description|dartComment }}
1817
///
1918
{% endif %}
20-
{% if method.type == 'webAuth' %}Future{% else %}Future<Response>{% endif %} {{ method.name | caseCamel }}({{ _self.method_parameters(method.parameters) }}) {
19+
{% if method.type == 'webAuth' %}Future{% elseif method.type == 'location' %} Future<Uint8List> {% else %} {% if method.responseModel and method.responseModel != 'any' %}Future<models.{{method.responseModel | caseUcfirst}}>{% else %}Future{% endif %}{% endif %} {{ method.name | caseCamel }}({{ _self.method_parameters(method.parameters) }}) async {
2120
final String path = '{{ method.path }}'{% for parameter in method.parameters.path %}.replaceAll(RegExp('{{ '{' }}{{ parameter.name | caseCamel }}{{ '}' }}'), {{ parameter.name | caseCamel | escapeKeyword }}){% endfor %};
2221

2322
final Map<String, dynamic> params = {
@@ -72,15 +71,17 @@ class {{ service.name | caseUcfirst }} extends Service {
7271
params[key] = params[key].toString();
7372
}});
7473

75-
return client.call(HttpMethod.{{ method.method | caseLower }}, path: path, params: params, responseType: ResponseType.bytes);
74+
final res = await client.call(HttpMethod.{{ method.method | caseLower }}, path: path, params: params, responseType: ResponseType.bytes);
75+
return res.data;
7676
{% else %}
7777
final Map<String, String> headers = {
7878
{% for key, header in method.headers %}
7979
'{{ key }}': '{{ header }}',
8080
{% endfor %}
8181
};
8282

83-
return client.call(HttpMethod.{{ method.method | caseLower }}, path: path, params: params, headers: headers);
83+
final res = await client.call(HttpMethod.{{ method.method | caseLower }}, path: path, params: params, headers: headers);
84+
return {% if method.responseModel and method.responseModel != 'any' %}models.{{method.responseModel | caseUcfirst}}.fromMap(res.data){% else %} res.data{% endif %};
8485
{% endif %}
8586
}
8687
{% endfor %}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst}}>{% else %}{{property.sub_schema | caseUcfirst}}{% endif %}{% else %}{{property.type | typeName}}{% endif %}{% endmacro %}
2+
part of {{ language.params.packageName }}.models;
3+
4+
class {{ definition.name | caseUcfirst }} {
5+
{% for property in definition.properties %}
6+
final {% if not property.required %}{{_self.sub_schema(property)}}? {{ property.name | escapeKeyword }}{% else %}{{_self.sub_schema(property)}} {{ property.name | escapeKeyword }}{% endif %};
7+
{% endfor %}
8+
{% if definition.additionalProperties %}
9+
final Map<String, dynamic> data;
10+
{% endif %}
11+
12+
{{ definition.name | caseUcfirst}}({
13+
{% for property in definition.properties %}{% if property.required %}
14+
required {% endif %}this.{{ property.name | escapeKeyword }},
15+
{% endfor %}
16+
{% if definition.additionalProperties %}
17+
required this.data,
18+
{% endif %}
19+
});
20+
21+
factory {{ definition.name | caseUcfirst}}.fromMap(Map<String, dynamic> map) {
22+
return {{ definition.name | caseUcfirst }}(
23+
{% for property in definition.properties %}
24+
{{ property.name | escapeKeyword }}: {% if property.sub_schema %}{% if property.type == 'array' %}List<{{property.sub_schema | caseUcfirst}}>.from(map['{{property.name | escapeDollarSign }}'].map((p) => {{property.sub_schema | caseUcfirst}}.fromMap(p))){% else %}{{property.sub_schema | caseUcfirst}}.fromMap(map['{{property.name | escapeDollarSign }}']){% endif %}{% else %}map['{{property.name | escapeDollarSign }}']{% if property.type == "number" %}.toDouble(){% endif %}{% endif %},
25+
{% endfor %}
26+
{% if definition.additionalProperties %}
27+
data: map,
28+
{% endif %}
29+
);
30+
}
31+
32+
Map<String, dynamic> toMap() {
33+
return {
34+
{% for property in definition.properties %}
35+
"{{ property.name | escapeDollarSign }}": {% if property.sub_schema %}{% if property.type == 'array' %}{{property.name | escapeKeyword}}.map((p) => p.toMap()){% else %}{{property.name | escapeKeyword}}.toMap(){% endif %}{% else %}{{property.name | escapeKeyword }}{% endif %},
36+
{% endfor %}
37+
{% if definition.additionalProperties %}
38+
"data": data,
39+
{% endif %}
40+
};
41+
}
42+
{% if definition.additionalProperties %}
43+
44+
T convertTo<T>(T Function(Map) fromJson) => fromJson(data);
45+
{% endif %}
46+
{% for property in definition.properties %}
47+
{% if property.sub_schema %}
48+
{% for def in spec.definitions %}
49+
{% if def.name == property.sub_schema and def.additionalProperties and property.type == 'array' %}
50+
51+
List<T> convertTo<T>(T Function(Map) fromJson) =>
52+
{{property.name}}.map((d) => d.convertTo<T>(fromJson)).toList();
53+
{% endif %}
54+
{% endfor %}
55+
{% endif %}
56+
{% endfor %}
57+
}

templates/flutter/pubspec.yaml.twig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ dependencies:
1414
device_info_plus: ^2.1.0
1515
flutter_web_auth: ^0.3.1
1616
http: ^0.13.3
17-
package_info_plus: ^1.0.4
18-
path_provider: ^2.0.2
17+
package_info_plus: ^1.0.6
18+
path_provider: ^2.0.3
1919
web_socket_channel: ^2.1.0
2020
2121
dev_dependencies:

tests/languages/flutter/tests.dart

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import '../lib/packageName.dart';
2+
import '../lib/models.dart';
23

34
void main() async {
45
Client client = Client();
@@ -8,64 +9,65 @@ void main() async {
89

910
client.setSelfSigned();
1011
client.setProject('console');
11-
client.setEndPointRealtime("wss://demo.appwrite.io/v1"); // change this later to appwrite.io
12+
client.setEndPointRealtime(
13+
"wss://demo.appwrite.io/v1"); // change this later to appwrite.io
1214

1315
Realtime realtime = Realtime(client);
1416
final rtsub = realtime.subscribe(["tests"]);
1517

16-
await Future.delayed(Duration(seconds: 4));
18+
await Future.delayed(Duration(seconds: 5));
1719
client.addHeader('Origin', 'http://localhost');
1820
// Foo Tests
1921
print('\nTest Started');
2022

21-
Response response;
23+
Mock response;
2224
response = await foo.get(x: 'string', y: 123, z: ['string in array']);
23-
print(response.data['result']);
25+
print(response.result);
2426

2527
response = await foo.post(x: 'string', y: 123, z: ['string in array']);
26-
print(response.data['result']);
28+
print(response.result);
2729

2830
response = await foo.put(x: 'string', y: 123, z: ['string in array']);
29-
print(response.data['result']);
31+
print(response.result);
3032

3133
response = await foo.patch(x: 'string', y: 123, z: ['string in array']);
32-
print(response.data['result']);
34+
print(response.result);
3335

3436
response = await foo.delete(x: 'string', y: 123, z: ['string in array']);
35-
print(response.data['result']);
37+
print(response.result);
3638

3739
// Bar Tests
3840

3941
response =
4042
await bar.get(xrequired: 'string', xdefault: 123, z: ['string in array']);
41-
print(response.data['result']);
43+
print(response.result);
4244

4345
response = await bar
4446
.post(xrequired: 'string', xdefault: 123, z: ['string in array']);
45-
print(response.data['result']);
47+
print(response.result);
4648

4749
response =
4850
await bar.put(xrequired: 'string', xdefault: 123, z: ['string in array']);
49-
print(response.data['result']);
51+
print(response.result);
5052

5153
response = await bar
5254
.patch(xrequired: 'string', xdefault: 123, z: ['string in array']);
53-
print(response.data['result']);
55+
print(response.result);
5456

5557
response = await bar
5658
.delete(xrequired: 'string', xdefault: 123, z: ['string in array']);
57-
print(response.data['result']);
59+
print(response.result);
5860

5961
// General Tests
6062

61-
response = await general.redirect();
62-
print(response.data['result']);
63+
final res = await general.redirect();
64+
print(res['result']);
6365

6466
final file = await MultipartFile.fromPath('file', '../../resources/file.png',
6567
filename: 'file.png');
6668
response = await general.upload(
6769
x: 'string', y: 123, z: ['string in array'], file: file);
68-
print(response.data['result']);
70+
print(response.result);
6971

7072
try {
7173
await general.error400();
@@ -93,8 +95,8 @@ void main() async {
9395
await Future.delayed(Duration(seconds: 5));
9496

9597
// response = await general.setCookie();
96-
// print(response.data['result']);
98+
// print(response.result);
9799

98100
// response = await general.getCookie();
99-
// print(response.data['result']);
101+
// print(response.result);
100102
}

0 commit comments

Comments
 (0)