Skip to content

Commit 5973a9d

Browse files
authored
mobile/api_builder (deriv-com#3)
https://trello.com/c/2XGWjthy/231-8-mobile-apibuilder-2 * Attempt to sort out our API builder * Separate out login handling * mobile/api_builder * Clear out generated files when building * Exclude autogenerated files for now * Fix exclusion * Restore cache earlier * dartanalyzer ignores the exclude directives, switch to tuneup See dart-lang/sdk#25551 * Remove duplicate cache restore * tuneup trim * Fix type handling and put the comment before the property * Handle cases where the array type is not provided * Rebuilt API * Remove .idea entirely * Ignore .idea workspace * Better handling for multiple types and arrays * Report stack trace if anything goes wrong
1 parent e12319c commit 5973a9d

File tree

320 files changed

+10052
-111
lines changed

Some content is hidden

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

320 files changed

+10052
-111
lines changed

.circleci/config.yml

+9-8
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,19 @@ jobs:
2020
name: Link JSON files for API autobuild
2121
command: ./setup.sh
2222

23+
- restore_cache:
24+
keys:
25+
- android-sdk-licenses
26+
- gradle
27+
- pub-cache
28+
2329
- run:
2430
name: pub get
2531
command: flutter pub get
2632

2733
- run:
2834
name: Build step
29-
command: flutter pub run build_runner build
35+
command: flutter pub run build_runner build --delete-conflicting-outputs
3036

3137
- run:
3238
name: Run the application tests
@@ -40,13 +46,8 @@ jobs:
4046
- run:
4147
name: Code analysis
4248
command: |
43-
dartanalyzer --options analysis_options.yaml --fatal-warnings lib
44-
45-
- restore_cache:
46-
keys:
47-
- android-sdk-licenses
48-
- gradle
49-
- pub-cache
49+
flutter pub global activate tuneup
50+
flutter pub global run tuneup check
5051
5152
- run:
5253
name: Check that the package is ready for publishing

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
.packages
55
.pub/
66

7+
.idea/
8+
79
build/
810

911
lib/api/*.json

.idea/libraries/Dart_SDK.xml

-19
This file was deleted.

.idea/libraries/Flutter_for_Android.xml

-9
This file was deleted.

.idea/modules.xml

-10
This file was deleted.

.idea/runConfigurations/example_lib_main_dart.xml

-6
This file was deleted.

.idea/workspace.xml

-45
This file was deleted.

analysis_options.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
analyzer:
2+
exclude:
3+
- lib/api/**
24
strong-mode:
35
implicit-dynamic: false
46
errors:

lib/api_builder.dart api_builder.dart

+35-9
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import 'package:recase/recase.dart';
77
import 'package:build/build.dart';
88
import 'package:dart_style/dart_style.dart';
99

10-
Builder apiBuilder(_) => APIBuilder();
10+
Builder apiBuilder(final BuilderOptions _) => APIBuilder();
1111

12+
/// This class is responsible for parsing the morass of JSON schema
13+
/// definition files for our API, and assembling them into request/response
14+
/// objects suitable for marshalling and deserialising from our websockets
15+
/// API.
1216
class APIBuilder extends Builder {
1317
static const Map<String, String> typeMap = <String, String>{
1418
'integer': 'int',
@@ -31,7 +35,7 @@ class APIBuilder extends Builder {
3135
Future<void> build(BuildStep buildStep) async {
3236
try {
3337
log.info('Reading ${buildStep.inputId} as JSON');
34-
final schemaDefinition = jsonDecode(await buildStep.readAsString(buildStep.inputId));
38+
final Map<dynamic, dynamic> schemaDefinition = jsonDecode(await buildStep.readAsString(buildStep.inputId));
3539

3640
log.info('Processing schema definition from ${buildStep.inputId}');
3741
final JsonSchema schema = JsonSchema.createSchema(schemaDefinition);
@@ -42,18 +46,39 @@ class APIBuilder extends Builder {
4246
/* Instead of trying anything too fancy here, we just provide a simple conversion from original
4347
JSON schema name - which is snake_cased - to something more Dart-suitable, and apply type
4448
mapping via "it's a string unless we have a better guess" heuristic. */
45-
log.info('Iterating over $props');
49+
props.sort();
4650
final String attrList = props.map((String k) {
4751
final String name = ReCase(k).camelCase;
4852
final JsonSchema prop = schema.properties[k];
4953
String type;
54+
// Currently we don't handle multiple types, could
55+
// treat those as `dynamic` but so far we don't have
56+
// enough of them to care too much
5057
if (prop.typeList?.isNotEmpty ?? false) {
51-
type = typeMap[prop.type?.toString() ?? 'string'];
58+
// The `.type` values are objects, not strings,
59+
// which leads to some confusing results when you
60+
// try to compare them as strings or use them as
61+
// map lookups... so we extract them out to separate
62+
// variables instead.
63+
if(prop.oneOf.isNotEmpty) {
64+
type = 'dynamic';
65+
} else {
66+
final String schemaType = prop.type?.toString() ?? 'string';
67+
if(schemaType == 'array') {
68+
// Some types aren't specified - forget_all for example
69+
final String itemType = prop.items?.type?.toString() ?? 'string';
70+
type = 'List<${typeMap[itemType]}>';
71+
} else {
72+
type = typeMap[schemaType] ?? 'String';
73+
}
74+
}
5275
} else {
5376
log.warning('The property $k on ${buildStep.inputId} does not appear to have a type: defaulting to string');
54-
type = 'string';
77+
type = 'String';
5578
}
56-
return 'final ${type ?? "unknown"} ${name ?? "unknown"};';
79+
return '''/// ${prop.description}
80+
${type ?? "unknown"} ${name ?? "unknown"};
81+
''';
5782
}).join('\n');
5883

5984
/* Some minor chicanery here to find out which API method we're supposed to be processing */
@@ -79,7 +104,7 @@ class APIBuilder extends Builder {
79104
// https://stackoverflow.com/questions/51188114/dart-build-runner-can-only-scan-read-write-files-in-the-web-directory
80105
// AssetId(buildStep.inputId.package, 'lib/api/${className}${schemaTypeMap[schemaType]}.dart'),
81106
buildStep.inputId.changeExtension('.dart'),
82-
DartFormatter().format('''/* Autogenerated from ${buildStep.inputId} */
107+
DartFormatter().format('''/// Autogenerated from ${buildStep.inputId}
83108
import 'dart:async';
84109
import 'dart:convert';
85110
import 'package:json_annotation/json_annotation.dart';
@@ -92,7 +117,7 @@ class ${fullClassName} {
92117
factory ${fullClassName}.fromJson(Map<String, dynamic> json) => _\$${fullClassName}FromJson(json);
93118
Map<String, dynamic> toJson() => _\$${fullClassName}ToJson(this);
94119
95-
/* Properties */
120+
// Properties
96121
${attrList}
97122
98123
// @override
@@ -101,8 +126,9 @@ class ${fullClassName} {
101126
static int _fromBoolean(bool v) => v ? 1 : 0;
102127
}
103128
'''));
104-
} catch (e) {
129+
} catch (e, stack) {
105130
log.severe('Failed to process ${buildStep.inputId} - $e');
131+
log.severe('Stack trace $stack');
106132
return;
107133
}
108134
}

build.yaml

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,22 @@ targets:
22
$default:
33
builders:
44
flutter_deriv_api|api:
5+
enabled: true
56
generate_for:
67
- lib/api/*_send.json
78
- lib/api/*_receive.json
89
builders:
910
api:
10-
import: 'package:flutter_deriv_api/api_builder.dart'
11+
import: './api_builder.dart'
1112
builder_factories: ["apiBuilder"]
1213
auto_apply: root_package
1314
build_extensions: {".json": [".dart"]}
15+
is_optional: False
1416
build_to: source
17+
runs_before: ["json_serializable|json_serializable"]
18+
defaults:
19+
generate_for:
20+
include:
21+
- lib/api/**
22+
#applies_builders:
23+
# - json_serializable|json_serializable

example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
44

5-
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
5+
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

example/pubspec.lock

+7
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@ packages:
158158
description: flutter
159159
source: sdk
160160
version: "0.0.0"
161+
flutter_webview_plugin:
162+
dependency: transitive
163+
description:
164+
name: flutter_webview_plugin
165+
url: "https://pub.dartlang.org"
166+
source: hosted
167+
version: "0.3.8"
161168
front_end:
162169
dependency: transitive
163170
description:

example/test/widget_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import 'package:flutter/material.dart';
99
import 'package:flutter_test/flutter_test.dart';
1010

11-
import 'package:flutter_deriv_api_example/main.dart';
11+
import '../lib/main.dart';
1212

1313
void main() {
1414
testWidgets('Verify Platform version', (WidgetTester tester) async {

flutter_deriv_api.iml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
<excludeFolder url="file://$MODULE_DIR$/.idea" />
99
<excludeFolder url="file://$MODULE_DIR$/.pub" />
1010
<excludeFolder url="file://$MODULE_DIR$/build" />
11+
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
1112
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
1213
<excludeFolder url="file://$MODULE_DIR$/example/build" />
1314
</content>
1415
<orderEntry type="sourceFolder" forTests="false" />
15-
<orderEntry type="library" name="Dart Packages" level="project" />
1616
<orderEntry type="library" name="Dart SDK" level="project" />
1717
<orderEntry type="library" name="Flutter Plugins" level="project" />
1818
</component>

lib/api/active_symbols_receive.dart

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// Autogenerated from flutter_deriv_api|lib/api/active_symbols_receive.json
2+
import 'dart:async';
3+
import 'dart:convert';
4+
import 'package:json_annotation/json_annotation.dart';
5+
6+
part 'active_symbols_receive.g.dart';
7+
8+
@JsonSerializable(nullable: false, fieldRename: FieldRename.snake)
9+
class ActiveSymbolsResponse {
10+
ActiveSymbolsResponse();
11+
factory ActiveSymbolsResponse.fromJson(Map<String, dynamic> json) =>
12+
_$ActiveSymbolsResponseFromJson(json);
13+
Map<String, dynamic> toJson() => _$ActiveSymbolsResponseToJson(this);
14+
15+
// Properties
16+
/// List of active symbols.
17+
List<Map<String, dynamic>> activeSymbols;
18+
19+
/// Echo of the request made.
20+
Map<String, dynamic> echoReq;
21+
22+
/// Action name of the request made.
23+
String msgType;
24+
25+
/// Optional field sent in request to map to response, present only when request contains req_id.
26+
int reqId;
27+
28+
// @override
29+
// String toString() => name;
30+
static bool _fromInteger(int v) => (v != 0);
31+
static int _fromBoolean(bool v) => v ? 1 : 0;
32+
}

lib/api/active_symbols_receive.g.dart

+27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)