@_nazmiforreal/flutter-ota 0.1.12 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/flutter-ota-linux-x64 +0 -0
- package/dart-src/packages/cli-tools/.dart_tool/package_config.json +46 -76
- package/dart-src/packages/cli-tools/.dart_tool/package_graph.json +140 -166
- package/dart-src/packages/cli-tools/README.md +1 -1
- package/dart-src/packages/cli-tools/lib/flutter_ota_kit_cli.dart +30 -17
- package/dart-src/packages/cli-tools/lib/src/backend.dart +30 -31
- package/dart-src/packages/cli-tools/lib/src/cli_base.dart +3 -1
- package/dart-src/packages/cli-tools/lib/src/commands/build.dart +44 -43
- package/dart-src/packages/cli-tools/lib/src/commands/bundle.dart +265 -239
- package/dart-src/packages/cli-tools/lib/src/commands/channel.dart +75 -66
- package/dart-src/packages/cli-tools/lib/src/commands/config_command.dart +39 -35
- package/dart-src/packages/cli-tools/lib/src/commands/console.dart +30 -27
- package/dart-src/packages/cli-tools/lib/src/commands/deploy.dart +88 -72
- package/dart-src/packages/cli-tools/lib/src/commands/doctor.dart +66 -29
- package/dart-src/packages/cli-tools/lib/src/commands/fingerprint.dart +10 -6
- package/dart-src/packages/cli-tools/lib/src/commands/init.dart +290 -181
- package/dart-src/packages/cli-tools/lib/src/commands/keys.dart +24 -23
- package/dart-src/packages/cli-tools/lib/src/commands/migrate.dart +111 -97
- package/dart-src/packages/cli-tools/lib/src/commands/pocketbase.dart +237 -0
- package/dart-src/packages/cli-tools/lib/src/commands/rollback.dart +43 -34
- package/dart-src/packages/cli-tools/lib/src/commands/storage.dart +57 -53
- package/dart-src/packages/cli-tools/lib/src/config.dart +227 -93
- package/dart-src/packages/cli-tools/lib/src/operations.dart +9 -8
- package/dart-src/packages/cli-tools/lib/src/pack.dart +26 -25
- package/dart-src/packages/cli-tools/lib/src/pocketbase/data_bootstrap.dart +39 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/hooks/bundles.pb.js +56 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/installer.dart +189 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/process_manager.dart +123 -0
- package/dart-src/packages/cli-tools/lib/src/pocketbase/schema_installer.dart +237 -0
- package/dart-src/packages/cli-tools/lib/src/runner.dart +36 -15
- package/dart-src/packages/cli-tools/lib/src/sign.dart +13 -5
- package/dart-src/packages/cli-tools/lib/src/ui/ui.dart +10 -13
- package/dart-src/packages/cli-tools/lib/src/util.dart +3 -1
- package/dart-src/packages/cli-tools/pubspec.lock +69 -116
- package/dart-src/packages/cli-tools/pubspec.yaml +25 -7
- package/dart-src/packages/cli-tools/test/backends_test.dart +62 -32
- package/dart-src/packages/cli-tools/test/build_pack_test.dart +4 -4
- package/dart-src/packages/cli-tools/test/cli_test.dart +89 -42
- package/dart-src/packages/cli-tools/test/mocks/mock_aws_s3_client.dart +8 -13
- package/dart-src/packages/cli-tools/test/mocks/mock_d1_client.dart +23 -18
- package/dart-src/packages/cli-tools/test/mocks/mock_pocketbase_client.dart +197 -0
- package/dart-src/packages/cli-tools/test/mocks/mock_postgres_client.dart +70 -46
- package/dart-src/packages/cli-tools/test/pocketbase_installer_test.dart +72 -0
- package/dart-src/packages/cli-tools/test/pocketbase_schema_test.dart +107 -0
- package/dart-src/packages/cli-tools/test/pocketbase_smoke_test.dart +59 -0
- package/dart-src/packages/core/README.md +2 -2
- package/dart-src/packages/core/example/lib/main.dart +69 -0
- package/dart-src/packages/core/lib/flutter_ota_kit_core.dart +1 -1
- package/dart-src/packages/core/lib/src/app_update_info.dart +17 -15
- package/dart-src/packages/core/lib/src/bundle.dart +102 -94
- package/dart-src/packages/core/lib/src/bundle_artifacts.dart +1 -2
- package/dart-src/packages/core/lib/src/bundle_patch_artifact.dart +15 -16
- package/dart-src/packages/core/lib/src/changed_asset.dart +24 -22
- package/dart-src/packages/core/lib/src/get_bundles_args.dart +13 -13
- package/dart-src/packages/core/lib/src/metadata.dart +9 -7
- package/dart-src/packages/core/lib/src/patch_info.dart +8 -7
- package/dart-src/packages/core/lib/src/platform.dart +4 -3
- package/dart-src/packages/core/lib/src/rollout.dart +14 -11
- package/dart-src/packages/core/lib/src/semver_range.dart +49 -39
- package/dart-src/packages/core/lib/src/semver_version.dart +8 -7
- package/dart-src/packages/core/lib/src/status.dart +4 -3
- package/dart-src/packages/core/lib/src/update_bundle_params.dart +7 -7
- package/dart-src/packages/core/lib/src/uuid.dart +1 -2
- package/dart-src/packages/core/pubspec.yaml +6 -7
- package/dart-src/packages/core/test/rollout_test.dart +15 -8
- package/dart-src/packages/core/test/semver_test.dart +5 -2
- package/dart-src/packages/core/test/types_test.dart +26 -24
- package/dart-src/packages/core/test/uuid_test.dart +3 -1
- package/dart-src/packages/core/tool/dbg.dart +1 -0
- package/dart-src/plugins/aws/CHANGELOG.md +6 -0
- package/dart-src/plugins/aws/example/lib/main.dart +60 -0
- package/dart-src/plugins/aws/lib/flutter_ota_kit_aws.dart +5 -1
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_client.dart +11 -16
- package/dart-src/plugins/aws/lib/src/aws_cloudfront_signer.dart +7 -2
- package/dart-src/plugins/aws/lib/src/aws_database.dart +14 -18
- package/dart-src/plugins/aws/lib/src/aws_s3_client.dart +23 -18
- package/dart-src/plugins/aws/lib/src/aws_ssm_client.dart +5 -4
- package/dart-src/plugins/aws/lib/src/aws_storage.dart +2 -3
- package/dart-src/plugins/aws/lib/src/aws_storage_profile.dart +10 -16
- package/dart-src/plugins/aws/lib/src/with_cloudfront_signed_url.dart +6 -9
- package/dart-src/plugins/aws/pubspec.yaml +15 -10
- package/dart-src/plugins/aws/test/aws_database_test.dart +51 -57
- package/dart-src/plugins/aws/test/mock_aws_s3_client.dart +8 -13
- package/dart-src/plugins/cloudflare/CHANGELOG.md +7 -0
- package/dart-src/plugins/cloudflare/example/lib/main.dart +62 -0
- package/dart-src/plugins/cloudflare/lib/flutter_ota_kit_cloudflare.dart +4 -2
- package/dart-src/plugins/cloudflare/lib/src/cloudflare_worker_database.dart +3 -3
- package/dart-src/plugins/cloudflare/lib/src/d1_build_where.dart +3 -2
- package/dart-src/plugins/cloudflare/lib/src/d1_bundle_mapper.dart +24 -27
- package/dart-src/plugins/cloudflare/lib/src/d1_database_plugin.dart +22 -30
- package/dart-src/plugins/cloudflare/lib/src/r2_config.dart +1 -1
- package/dart-src/plugins/cloudflare/lib/src/r2_s3_client.dart +21 -29
- package/dart-src/plugins/cloudflare/lib/src/r2_storage.dart +7 -7
- package/dart-src/plugins/cloudflare/lib/src/r2_storage_profile.dart +7 -15
- package/dart-src/plugins/cloudflare/pubspec.yaml +14 -9
- package/dart-src/plugins/cloudflare/test/cloudflare_worker_database_test.dart +46 -42
- package/dart-src/plugins/cloudflare/test/d1_database_test.dart +100 -90
- package/dart-src/plugins/cloudflare/test/mock_d1_client.dart +23 -18
- package/dart-src/plugins/cloudflare/test/mock_r2_client.dart +8 -13
- package/dart-src/plugins/cloudflare/test/r2_storage_test.dart +1 -4
- package/dart-src/plugins/plugin-core/CHANGELOG.md +16 -0
- package/dart-src/plugins/plugin-core/example/lib/main.dart +61 -0
- package/dart-src/plugins/plugin-core/lib/flutter_ota_kit_plugin_core.dart +3 -7
- package/dart-src/plugins/plugin-core/lib/src/asset_storage_layout.dart +5 -5
- package/dart-src/plugins/plugin-core/lib/src/bundle_helpers.dart +23 -0
- package/dart-src/plugins/plugin-core/lib/src/bundle_storage_layout.dart +16 -10
- package/dart-src/plugins/plugin-core/lib/src/bundle_unit_of_work.dart +31 -26
- package/dart-src/plugins/plugin-core/lib/src/calculate_pagination.dart +5 -1
- package/dart-src/plugins/plugin-core/lib/src/content_addressed_assets.dart +1 -1
- package/dart-src/plugins/plugin-core/lib/src/create_blob_database_plugin.dart +78 -71
- package/dart-src/plugins/plugin-core/lib/src/create_database_plugin.dart +56 -35
- package/dart-src/plugins/plugin-core/lib/src/create_storage_plugin.dart +10 -13
- package/dart-src/plugins/plugin-core/lib/src/paginate_bundles.dart +30 -17
- package/dart-src/plugins/plugin-core/lib/src/parse_storage_uri.dart +3 -1
- package/dart-src/plugins/plugin-core/lib/src/query_bundles.dart +5 -3
- package/dart-src/plugins/plugin-core/lib/src/request_update_bundle_state.dart +2 -4
- package/dart-src/plugins/plugin-core/lib/src/storage_helpers.dart +18 -0
- package/dart-src/plugins/plugin-core/lib/src/types.dart +7 -6
- package/dart-src/plugins/plugin-core/pubspec.yaml +12 -9
- package/dart-src/plugins/plugin-core/test/asset_storage_layout_test.dart +13 -9
- package/dart-src/plugins/plugin-core/test/bundle_unit_of_work_test.dart +28 -15
- package/dart-src/plugins/plugin-core/test/create_blob_database_plugin_test.dart +254 -122
- package/dart-src/plugins/plugin-core/test/create_database_plugin_test.dart +36 -32
- package/dart-src/plugins/plugin-core/test/create_storage_plugin_test.dart +18 -15
- package/dart-src/plugins/plugin-core/test/generate_min_bundle_id_test.dart +8 -3
- package/dart-src/plugins/plugin-core/test/get_update_info_test.dart +682 -693
- package/dart-src/plugins/plugin-core/test/helpers_test.dart +45 -21
- package/dart-src/plugins/plugin-core/test/query_bundles_test.dart +76 -13
- package/dart-src/plugins/plugin-core/test/uuidv7_test.dart +20 -9
- package/dart-src/plugins/pocketbase/CHANGELOG.md +8 -0
- package/dart-src/plugins/pocketbase/LICENSE +21 -0
- package/dart-src/plugins/pocketbase/README.md +54 -0
- package/dart-src/plugins/pocketbase/example/lib/main.dart +62 -0
- package/dart-src/plugins/pocketbase/lib/flutter_ota_kit_pocketbase.dart +28 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_bundle_mapper.dart +121 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_client.dart +349 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_config.dart +61 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_database.dart +278 -0
- package/dart-src/plugins/pocketbase/lib/src/pocketbase_storage.dart +255 -0
- package/dart-src/plugins/pocketbase/pubspec.yaml +26 -0
- package/dart-src/plugins/pocketbase/test/pb_uri_test.dart +12 -0
- package/dart-src/plugins/postgres/CHANGELOG.md +6 -0
- package/dart-src/plugins/postgres/example/lib/main.dart +67 -0
- package/dart-src/plugins/postgres/lib/flutter_ota_kit_postgres.dart +3 -6
- package/dart-src/plugins/postgres/lib/src/postgres_bundle_mapper.dart +32 -52
- package/dart-src/plugins/postgres/lib/src/postgres_config.dart +3 -1
- package/dart-src/plugins/postgres/lib/src/postgres_database.dart +28 -38
- package/dart-src/plugins/postgres/lib/src/postgres_get_update_info.dart +16 -14
- package/dart-src/plugins/postgres/lib/src/postgres_storage.dart +26 -14
- package/dart-src/plugins/postgres/lib/src/postgres_types.dart +27 -28
- package/dart-src/plugins/postgres/pubspec.yaml +14 -9
- package/dart-src/plugins/postgres/test/mock_postgres_client.dart +62 -39
- package/dart-src/plugins/postgres/test/postgres_bundle_mapper_test.dart +3 -4
- package/dart-src/plugins/postgres/test/postgres_database_test.dart +102 -73
- package/dart-src/plugins/postgres/test/postgres_storage_runtime_test.dart +60 -0
- package/dart-src/plugins/supabase/CHANGELOG.md +17 -0
- package/dart-src/plugins/supabase/README.md +2 -2
- package/dart-src/plugins/supabase/example/lib/main.dart +63 -0
- package/dart-src/plugins/supabase/lib/flutter_ota_kit_supabase.dart +2 -0
- package/dart-src/plugins/supabase/lib/src/error_message.dart +11 -0
- package/dart-src/plugins/supabase/lib/src/supabase_bundle_mapper.dart +34 -58
- package/dart-src/plugins/supabase/lib/src/supabase_client_adapter.dart +3 -13
- package/dart-src/plugins/supabase/lib/src/supabase_client_http.dart +34 -52
- package/dart-src/plugins/supabase/lib/src/supabase_database.dart +38 -30
- package/dart-src/plugins/supabase/lib/src/supabase_edge_function_storage.dart +21 -29
- package/dart-src/plugins/supabase/lib/src/supabase_signed_url_batcher.dart +7 -19
- package/dart-src/plugins/supabase/lib/src/supabase_storage.dart +18 -29
- package/dart-src/plugins/supabase/lib/src/types.dart +26 -26
- package/dart-src/plugins/supabase/pubspec.yaml +14 -9
- package/dart-src/plugins/supabase/test/mock_supabase_client.dart +57 -57
- package/dart-src/plugins/supabase/test/supabase_bundle_mapper_test.dart +30 -27
- package/dart-src/plugins/supabase/test/supabase_database_test.dart +26 -35
- package/dart-src/plugins/supabase/test/supabase_storage_test.dart +27 -34
- package/package.json +3 -3
- package/scripts/vendor.sh +1 -1
|
@@ -8,48 +8,49 @@ import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart'
|
|
|
8
8
|
UpdateStatus,
|
|
9
9
|
nilUuid;
|
|
10
10
|
import 'package:flutter_ota_kit_plugin_core/flutter_ota_kit_plugin_core.dart'
|
|
11
|
-
show
|
|
12
|
-
DatabaseBundleQueryOptions,
|
|
13
|
-
DatabaseBundleQueryWhere,
|
|
14
|
-
Paginated;
|
|
11
|
+
show DatabaseBundleQueryOptions, DatabaseBundleQueryWhere, Paginated;
|
|
15
12
|
import 'package:test/test.dart';
|
|
16
13
|
|
|
17
14
|
import 'mock_postgres_client.dart';
|
|
18
15
|
|
|
19
|
-
Bundle _bundle(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
16
|
+
Bundle _bundle(
|
|
17
|
+
String id, {
|
|
18
|
+
String channel = 'production',
|
|
19
|
+
String? fingerprintHash,
|
|
20
|
+
}) => Bundle(
|
|
21
|
+
id: id,
|
|
22
|
+
platform: Platform.android,
|
|
23
|
+
shouldForceUpdate: false,
|
|
24
|
+
enabled: true,
|
|
25
|
+
fileHash: 'file-hash-$id',
|
|
26
|
+
channel: channel,
|
|
27
|
+
storageUri: 'file:///bundle-$id',
|
|
28
|
+
targetAppVersion: '1.0.0',
|
|
29
|
+
message: null,
|
|
30
|
+
fingerprintHash: fingerprintHash,
|
|
31
|
+
metadata: null,
|
|
32
|
+
manifestStorageUri: null,
|
|
33
|
+
manifestFileHash: null,
|
|
34
|
+
assetBaseStorageUri: null,
|
|
35
|
+
patches: const [],
|
|
36
|
+
patchBaseBundleId: null,
|
|
37
|
+
patchBaseFileHash: null,
|
|
38
|
+
patchFileHash: null,
|
|
39
|
+
patchStorageUri: null,
|
|
40
|
+
rolloutCohortCount: 1000,
|
|
41
|
+
targetCohorts: null,
|
|
42
|
+
);
|
|
45
43
|
|
|
46
44
|
void main() {
|
|
47
45
|
group('postgresDatabase', () {
|
|
48
46
|
test('getChannels returns distinct channels', () async {
|
|
49
47
|
final plugin = newPlugin();
|
|
50
|
-
await plugin.appendBundle(
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
await plugin.appendBundle(
|
|
49
|
+
_bundle('018f0000-0000-7000-8000-000000000001'),
|
|
50
|
+
);
|
|
51
|
+
await plugin.appendBundle(
|
|
52
|
+
_bundle('018f0000-0000-7000-8000-000000000002', channel: 'staging'),
|
|
53
|
+
);
|
|
53
54
|
await plugin.commitBundle();
|
|
54
55
|
|
|
55
56
|
final channels = await plugin.getChannels();
|
|
@@ -59,8 +60,12 @@ void main() {
|
|
|
59
60
|
|
|
60
61
|
test('getBundles returns bundles and pagination', () async {
|
|
61
62
|
final plugin = newPlugin();
|
|
62
|
-
await plugin.appendBundle(
|
|
63
|
-
|
|
63
|
+
await plugin.appendBundle(
|
|
64
|
+
_bundle('018f0000-0000-7000-8000-000000000001'),
|
|
65
|
+
);
|
|
66
|
+
await plugin.appendBundle(
|
|
67
|
+
_bundle('018f0000-0000-7000-8000-000000000002'),
|
|
68
|
+
);
|
|
64
69
|
await plugin.commitBundle();
|
|
65
70
|
|
|
66
71
|
final result = await plugin.getBundles(
|
|
@@ -73,9 +78,12 @@ void main() {
|
|
|
73
78
|
|
|
74
79
|
test('getBundles filters by channel', () async {
|
|
75
80
|
final plugin = newPlugin();
|
|
76
|
-
await plugin.appendBundle(
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
await plugin.appendBundle(
|
|
82
|
+
_bundle('018f0000-0000-7000-8000-000000000001'),
|
|
83
|
+
);
|
|
84
|
+
await plugin.appendBundle(
|
|
85
|
+
_bundle('018f0000-0000-7000-8000-000000000002', channel: 'staging'),
|
|
86
|
+
);
|
|
79
87
|
await plugin.commitBundle();
|
|
80
88
|
|
|
81
89
|
final result = await plugin.getBundles(
|
|
@@ -88,24 +96,34 @@ void main() {
|
|
|
88
96
|
expect(result.data.first.channel, 'staging');
|
|
89
97
|
});
|
|
90
98
|
|
|
91
|
-
test(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
test(
|
|
100
|
+
'getBundleById returns null for missing, bundle for existing',
|
|
101
|
+
() async {
|
|
102
|
+
final plugin = newPlugin();
|
|
103
|
+
await plugin.appendBundle(
|
|
104
|
+
_bundle('018f0000-0000-7000-8000-000000000001'),
|
|
105
|
+
);
|
|
106
|
+
await plugin.commitBundle();
|
|
107
|
+
|
|
108
|
+
expect(await plugin.getBundleById('does-not-exist'), isNull);
|
|
109
|
+
final got = await plugin.getBundleById(
|
|
110
|
+
'018f0000-0000-7000-8000-000000000001',
|
|
111
|
+
);
|
|
112
|
+
expect(got, isNotNull);
|
|
113
|
+
expect(got!.id, '018f0000-0000-7000-8000-000000000001');
|
|
114
|
+
},
|
|
115
|
+
);
|
|
102
116
|
|
|
103
117
|
test('commitBundle append -> update -> delete reflects in store', () async {
|
|
104
118
|
final plugin = newPlugin();
|
|
105
|
-
await plugin.appendBundle(
|
|
119
|
+
await plugin.appendBundle(
|
|
120
|
+
_bundle('018f0000-0000-7000-8000-000000000001'),
|
|
121
|
+
);
|
|
106
122
|
await plugin.commitBundle();
|
|
107
123
|
|
|
108
|
-
var got = await plugin.getBundleById(
|
|
124
|
+
var got = await plugin.getBundleById(
|
|
125
|
+
'018f0000-0000-7000-8000-000000000001',
|
|
126
|
+
);
|
|
109
127
|
expect(got!.enabled, true);
|
|
110
128
|
|
|
111
129
|
await plugin.updateBundle(got.id, {'enabled': false});
|
|
@@ -121,30 +139,39 @@ void main() {
|
|
|
121
139
|
);
|
|
122
140
|
});
|
|
123
141
|
|
|
124
|
-
test(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
test(
|
|
143
|
+
'getUpdateInfo via RPC returns UpdateInfo for compatible bundle',
|
|
144
|
+
() async {
|
|
145
|
+
final plugin = newPlugin();
|
|
146
|
+
await plugin.appendBundle(
|
|
147
|
+
_bundle('018f0000-0000-7000-8000-000000000001'),
|
|
148
|
+
);
|
|
149
|
+
await plugin.commitBundle();
|
|
150
|
+
|
|
151
|
+
final info = await plugin.getUpdateInfo(
|
|
152
|
+
AppVersionGetBundlesArgs(
|
|
153
|
+
appVersion: '1.0.0',
|
|
154
|
+
bundleId: nilUuid,
|
|
155
|
+
channel: 'production',
|
|
156
|
+
minBundleId: nilUuid,
|
|
157
|
+
platform: Platform.android,
|
|
158
|
+
),
|
|
159
|
+
);
|
|
160
|
+
expect(info, isA<UpdateInfo>());
|
|
161
|
+
expect(info!.status, UpdateStatus.update);
|
|
162
|
+
expect(
|
|
163
|
+
info.storageUri,
|
|
164
|
+
'file:///bundle-018f0000-0000-7000-8000-000000000001',
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
);
|
|
143
168
|
|
|
144
169
|
test('getUpdateInfo via fingerprint returns matching bundle', () async {
|
|
145
170
|
final plugin = newPlugin();
|
|
146
|
-
final fpBundle = _bundle(
|
|
147
|
-
|
|
171
|
+
final fpBundle = _bundle(
|
|
172
|
+
'018f0000-0000-7000-8000-000000000001',
|
|
173
|
+
fingerprintHash: 'fp-abc',
|
|
174
|
+
);
|
|
148
175
|
await plugin.appendBundle(fpBundle);
|
|
149
176
|
await plugin.commitBundle();
|
|
150
177
|
|
|
@@ -163,7 +190,9 @@ void main() {
|
|
|
163
190
|
|
|
164
191
|
test('getUpdateInfo returns null when no compatible bundle', () async {
|
|
165
192
|
final plugin = newPlugin();
|
|
166
|
-
await plugin.appendBundle(
|
|
193
|
+
await plugin.appendBundle(
|
|
194
|
+
_bundle('018f0000-0000-7000-8000-000000000001'),
|
|
195
|
+
);
|
|
167
196
|
await plugin.commitBundle();
|
|
168
197
|
|
|
169
198
|
final info = await plugin.getUpdateInfo(
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import 'package:flutter_ota_kit_postgres/flutter_ota_kit_postgres.dart';
|
|
2
|
+
import 'package:test/test.dart';
|
|
3
|
+
|
|
4
|
+
void main() {
|
|
5
|
+
group('Postgres storage runtime', () {
|
|
6
|
+
test(
|
|
7
|
+
'getDownloadUrl throws a clear error when no servingBaseUrl is set',
|
|
8
|
+
() async {
|
|
9
|
+
final storage = postgresStorage(
|
|
10
|
+
PostgresStorageConfig(
|
|
11
|
+
db: PostgresConfig(
|
|
12
|
+
host: 'mock',
|
|
13
|
+
database: 'mock',
|
|
14
|
+
username: 'u',
|
|
15
|
+
password: 'p',
|
|
16
|
+
),
|
|
17
|
+
// servingBaseUrl intentionally omitted.
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
final node = storage.profiles.node!;
|
|
21
|
+
// Use a dummy storageUri just to exercise the runtime path.
|
|
22
|
+
expect(
|
|
23
|
+
() => storage.profiles.runtime!.getDownloadUrl(
|
|
24
|
+
'postgres://bundles/abc/zip',
|
|
25
|
+
),
|
|
26
|
+
throwsA(
|
|
27
|
+
isA<StateError>().having(
|
|
28
|
+
(e) => e.message,
|
|
29
|
+
'message',
|
|
30
|
+
contains('servingBaseUrl'),
|
|
31
|
+
),
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
// The node profile must still work because it goes through the DB.
|
|
35
|
+
expect(node, isNotNull);
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
test(
|
|
40
|
+
'getDownloadUrl builds an http url when servingBaseUrl is set',
|
|
41
|
+
() async {
|
|
42
|
+
final storage = postgresStorage(
|
|
43
|
+
PostgresStorageConfig(
|
|
44
|
+
db: PostgresConfig(
|
|
45
|
+
host: 'mock',
|
|
46
|
+
database: 'mock',
|
|
47
|
+
username: 'u',
|
|
48
|
+
password: 'p',
|
|
49
|
+
),
|
|
50
|
+
servingBaseUrl: 'https://cdn.example.com',
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
final url = (await storage.profiles.runtime!.getDownloadUrl(
|
|
54
|
+
'postgres://bundles/abc/zip',
|
|
55
|
+
))['fileUrl'];
|
|
56
|
+
expect(url, 'https://cdn.example.com/bundles/abc/zip');
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## 0.1.2
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
|
|
5
|
+
- **De-duplicated the error-to-message helper.** `_getErrorMessage` (supabase_storage,
|
|
6
|
+
supabase_signed_url_batcher) and `_errorToString` (supabase_edge_function_storage)
|
|
7
|
+
were byte-identical copies. They now all call one shared `errorMessage(Object?)` in
|
|
8
|
+
`lib/src/error_message.dart`. No behavior change; the internal names are gone.
|
|
9
|
+
|
|
10
|
+
## 0.1.1
|
|
11
|
+
|
|
12
|
+
- fix(supabase): the `migrate` RLS migration now creates the public-read policies
|
|
13
|
+
(`allow public read bundles`, `allow public read bundle_patches`, `public bundles read`)
|
|
14
|
+
so the anon-key app can actually fetch bundles. Previously RLS was enabled with zero
|
|
15
|
+
policies, so every fresh `flutter-ota migrate supabase` produced a DB where clients
|
|
16
|
+
received no OTA.
|
|
17
|
+
|
|
1
18
|
## 0.1.0
|
|
2
19
|
|
|
3
20
|
- Initial release of `flutter_ota_kit_supabase` as part of the flutter_ota_kit OTA toolkit.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# flutter_ota_kit_supabase
|
|
2
2
|
|
|
3
|
-
Supabase backend for [flutter_ota_kit](https://github.com/HYPER12755/
|
|
3
|
+
Supabase backend for [flutter_ota_kit](https://github.com/HYPER12755/flutter-ota-kit).
|
|
4
4
|
Implements the bundle **database** (PostgREST `bundles` table) and **storage**
|
|
5
5
|
(signed URLs) used both by the `flutter-ota` CLI and by the Flutter SDK's
|
|
6
6
|
`FlutterPatcher.configureSupabase(...)`.
|
|
@@ -46,7 +46,7 @@ FlutterPatcher.configureSupabase(SupabaseUpdateConfig(
|
|
|
46
46
|
));
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
See [doc/backends.md](https://github.com/HYPER12755/
|
|
49
|
+
See [doc/backends.md](https://github.com/HYPER12755/flutter-ota-kit/blob/main/doc/backends.md).
|
|
50
50
|
|
|
51
51
|
## License
|
|
52
52
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// flutter_ota_kit_supabase example
|
|
2
|
+
//
|
|
3
|
+
// `flutter_ota_kit_supabase` is a Supabase backend for `flutter_ota_kit`.
|
|
4
|
+
// It provides two factories — `supabaseDatabase` and `supabaseStorage` —
|
|
5
|
+
// that read/write the `bundles` table and patch files in a Supabase
|
|
6
|
+
// Storage bucket.
|
|
7
|
+
//
|
|
8
|
+
// Most apps don't import this package directly; they wire it through
|
|
9
|
+
// `flutter_ota_kit.configureSupabase(...)` in their app boot path. This
|
|
10
|
+
// file shows the low-level config types in case you're writing a custom
|
|
11
|
+
// server, a migration script, or a CLI extension.
|
|
12
|
+
//
|
|
13
|
+
// Run: `dart run example/main.dart`
|
|
14
|
+
|
|
15
|
+
import 'package:flutter_ota_kit_supabase/flutter_ota_kit_supabase.dart';
|
|
16
|
+
|
|
17
|
+
void main() {
|
|
18
|
+
// ── 1. Database config (PostgREST + service-role key). ─────────────
|
|
19
|
+
// The `supabaseServiceRoleKey` is server-side only — never ship it in
|
|
20
|
+
// a Flutter app. Use `supabaseAnonKey` + RLS policies instead.
|
|
21
|
+
const dbConfig = SupabaseServiceRoleConfig(
|
|
22
|
+
supabaseUrl: 'https://your-project.supabase.co',
|
|
23
|
+
supabaseServiceRoleKey: 'eyJ...', // server-side only
|
|
24
|
+
);
|
|
25
|
+
print('Supabase URL: ${dbConfig.supabaseUrl}');
|
|
26
|
+
|
|
27
|
+
// ── 2. Storage config (adds bucket + base path). ──────────────────
|
|
28
|
+
// The `basePath` is the prefix inside the bucket where bundles live.
|
|
29
|
+
// The default convention is `bundles/{id}/patch.zip`.
|
|
30
|
+
const storageConfig = SupabaseStorageConfig(
|
|
31
|
+
supabaseUrl: 'https://your-project.supabase.co',
|
|
32
|
+
supabaseServiceRoleKey: 'eyJ...',
|
|
33
|
+
bucketName: 'flutter-ota-bundles',
|
|
34
|
+
basePath: 'bundles',
|
|
35
|
+
);
|
|
36
|
+
print('Bucket: ${storageConfig.bucketName}');
|
|
37
|
+
print('Base path: ${storageConfig.basePath}');
|
|
38
|
+
|
|
39
|
+
// ── 3. Row-to-Bundle mapping (server-side helper). ────────────────
|
|
40
|
+
// Use this when reading bundles from PostgREST and passing them to
|
|
41
|
+
// the SDK's `PatchInfo.fromJson`. The mapper handles both camelCase
|
|
42
|
+
// and snake_case column names.
|
|
43
|
+
const row = SupabaseBundleRow(
|
|
44
|
+
id: '01a059a6-7d3c-4f3e-8a2c-9c2e3a4b5d6e',
|
|
45
|
+
channel: 'production',
|
|
46
|
+
enabled: true,
|
|
47
|
+
shouldForceUpdate: false,
|
|
48
|
+
fileHash: '7c4a8d09ca3762af61e59520943dc26494f8941b',
|
|
49
|
+
platform: 'android',
|
|
50
|
+
storageUri:
|
|
51
|
+
'supabase-storage://flutter-ota-bundles/'
|
|
52
|
+
'01a059a6/patch.zip',
|
|
53
|
+
targetAppVersion: '1.0.0',
|
|
54
|
+
rolloutCohortCount: 100,
|
|
55
|
+
);
|
|
56
|
+
final bundle = mapRowToBundle(row);
|
|
57
|
+
print('Mapped bundle: ${bundle.id} (${bundle.storageUri})');
|
|
58
|
+
|
|
59
|
+
// ── 4. URL parsing helpers. ───────────────────────────────────────
|
|
60
|
+
final uri = parseSupabaseStorageUri(bundle.storageUri);
|
|
61
|
+
print('Bucket: ${uri.bucket}');
|
|
62
|
+
print('Key: ${uri.key}');
|
|
63
|
+
}
|
|
@@ -19,3 +19,5 @@ export 'src/supabase_edge_function_database.dart'
|
|
|
19
19
|
show supabaseEdgeFunctionDatabase, SupabaseEdgeFunctionDatabaseConfig;
|
|
20
20
|
export 'src/supabase_edge_function_storage.dart'
|
|
21
21
|
show supabaseEdgeFunctionStorage, SupabaseEdgeFunctionStorageConfig;
|
|
22
|
+
export 'src/types.dart' show SupabaseBundleRow;
|
|
23
|
+
export 'src/supabase_bundle_mapper.dart' show mapRowToBundle;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// Shared error-to-message helper used across the supabase plugin
|
|
2
|
+
/// (storage, signed-url batcher, edge-function storage). Identical copies used
|
|
3
|
+
/// to live in three files; they now all call this one.
|
|
4
|
+
String errorMessage(Object? error) {
|
|
5
|
+
if (error is Error) return error.toString();
|
|
6
|
+
if (error is Exception) return error.toString();
|
|
7
|
+
if (error is Map && error['message'] is String) {
|
|
8
|
+
return error['message'] as String;
|
|
9
|
+
}
|
|
10
|
+
return error.toString();
|
|
11
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/// Faithful port of hot-updater `plugins/supabase/src/supabaseBundleMapper.ts`.
|
|
2
2
|
library;
|
|
3
3
|
|
|
4
|
-
import 'dart:convert';
|
|
5
|
-
|
|
6
4
|
import 'package:flutter_ota_kit_core/flutter_ota_kit_core.dart';
|
|
7
5
|
|
|
6
|
+
import 'package:flutter_ota_kit_plugin_core/flutter_ota_kit_plugin_core.dart'
|
|
7
|
+
show buildBundlePatchId, normalizeMetadata;
|
|
8
|
+
|
|
8
9
|
import 'types.dart';
|
|
9
10
|
|
|
10
11
|
const String bundleSelectColumns =
|
|
@@ -13,34 +14,12 @@ const String bundleSelectColumns =
|
|
|
13
14
|
'storage_uri, metadata, manifest_storage_uri, manifest_file_hash, '
|
|
14
15
|
'asset_base_storage_uri, rollout_cohort_count, target_cohorts';
|
|
15
16
|
|
|
16
|
-
/// Normalize metadata from various shapes to a plain map.
|
|
17
|
-
Map<String, Object?>? _normalizeMetadata(Object? value) {
|
|
18
|
-
if (value == null) return null;
|
|
19
|
-
|
|
20
|
-
if (value is String) {
|
|
21
|
-
try {
|
|
22
|
-
final parsed = jsonDecode(value);
|
|
23
|
-
return _normalizeMetadata(parsed);
|
|
24
|
-
} catch (_) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (value is Map) return value.cast<String, Object?>();
|
|
30
|
-
|
|
31
|
-
return null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/// Build the compound primary key for a patch row.
|
|
35
|
-
String _buildBundlePatchId(String bundleId, String baseBundleId) =>
|
|
36
|
-
'$bundleId:$baseBundleId';
|
|
37
|
-
|
|
38
17
|
/// Map a Supabase bundle row + optional patch rows to a [Bundle].
|
|
39
18
|
Bundle mapRowToBundle(
|
|
40
19
|
SupabaseBundleRow row, [
|
|
41
20
|
List<SupabaseBundlePatchRow> patchRows = const [],
|
|
42
21
|
]) {
|
|
43
|
-
final rawMetadata =
|
|
22
|
+
final rawMetadata = normalizeMetadata(row.metadata);
|
|
44
23
|
final patches = patchRows.toList()
|
|
45
24
|
..sort((a, b) {
|
|
46
25
|
final cmp = a.orderIndex.compareTo(b.orderIndex);
|
|
@@ -49,16 +28,17 @@ Bundle mapRowToBundle(
|
|
|
49
28
|
});
|
|
50
29
|
|
|
51
30
|
final patchArtifacts = patches
|
|
52
|
-
.map(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
31
|
+
.map(
|
|
32
|
+
(p) => BundlePatchArtifact(
|
|
33
|
+
baseBundleId: p.baseBundleId,
|
|
34
|
+
baseFileHash: p.baseFileHash,
|
|
35
|
+
patchFileHash: p.patchFileHash,
|
|
36
|
+
patchStorageUri: p.patchStorageUri,
|
|
37
|
+
),
|
|
38
|
+
)
|
|
58
39
|
.toList();
|
|
59
40
|
|
|
60
|
-
final primaryPatch =
|
|
61
|
-
patchArtifacts.isNotEmpty ? patchArtifacts.first : null;
|
|
41
|
+
final primaryPatch = patchArtifacts.isNotEmpty ? patchArtifacts.first : null;
|
|
62
42
|
|
|
63
43
|
return Bundle(
|
|
64
44
|
id: row.id,
|
|
@@ -72,8 +52,7 @@ Bundle mapRowToBundle(
|
|
|
72
52
|
targetAppVersion: row.targetAppVersion,
|
|
73
53
|
fingerprintHash: row.fingerprintHash,
|
|
74
54
|
storageUri: row.storageUri,
|
|
75
|
-
metadata:
|
|
76
|
-
rawMetadata != null ? BundleMetadata.fromJson(rawMetadata) : null,
|
|
55
|
+
metadata: rawMetadata != null ? BundleMetadata.fromJson(rawMetadata) : null,
|
|
77
56
|
manifestStorageUri: row.manifestStorageUri,
|
|
78
57
|
manifestFileHash: row.manifestFileHash,
|
|
79
58
|
assetBaseStorageUri: row.assetBaseStorageUri,
|
|
@@ -82,33 +61,31 @@ Bundle mapRowToBundle(
|
|
|
82
61
|
patchBaseFileHash: primaryPatch?.baseFileHash,
|
|
83
62
|
patchFileHash: primaryPatch?.patchFileHash,
|
|
84
63
|
patchStorageUri: primaryPatch?.patchStorageUri,
|
|
85
|
-
rolloutCohortCount:
|
|
86
|
-
row.rolloutCohortCount ?? defaultRolloutCohortCount,
|
|
64
|
+
rolloutCohortCount: row.rolloutCohortCount ?? defaultRolloutCohortCount,
|
|
87
65
|
targetCohorts: row.targetCohorts,
|
|
88
66
|
);
|
|
89
67
|
}
|
|
90
68
|
|
|
91
69
|
/// Map a [Bundle] to a Supabase row for upsert.
|
|
92
70
|
SupabaseBundleRow bundleToRow(Bundle bundle) => SupabaseBundleRow(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
);
|
|
71
|
+
id: bundle.id,
|
|
72
|
+
channel: bundle.channel,
|
|
73
|
+
enabled: bundle.enabled,
|
|
74
|
+
shouldForceUpdate: bundle.shouldForceUpdate,
|
|
75
|
+
fileHash: bundle.fileHash,
|
|
76
|
+
gitCommitHash: bundle.gitCommitHash,
|
|
77
|
+
message: bundle.message,
|
|
78
|
+
platform: bundle.platform.value,
|
|
79
|
+
targetAppVersion: bundle.targetAppVersion,
|
|
80
|
+
fingerprintHash: bundle.fingerprintHash,
|
|
81
|
+
storageUri: bundle.storageUri,
|
|
82
|
+
metadata: stripBundleArtifactMetadata(bundle.metadata?.toJson()),
|
|
83
|
+
manifestStorageUri: getManifestStorageUri(bundle),
|
|
84
|
+
manifestFileHash: getManifestFileHash(bundle),
|
|
85
|
+
assetBaseStorageUri: getAssetBaseStorageUri(bundle),
|
|
86
|
+
rolloutCohortCount: bundle.rolloutCohortCount ?? defaultRolloutCohortCount,
|
|
87
|
+
targetCohorts: bundle.targetCohorts,
|
|
88
|
+
);
|
|
112
89
|
|
|
113
90
|
/// Map a [Bundle] to its patch rows for upsert.
|
|
114
91
|
List<SupabaseBundlePatchRow> bundleToPatchRows(Bundle bundle) {
|
|
@@ -116,8 +93,7 @@ List<SupabaseBundlePatchRow> bundleToPatchRows(Bundle bundle) {
|
|
|
116
93
|
return [
|
|
117
94
|
for (var i = 0; i < patchArtifacts.length; i++)
|
|
118
95
|
SupabaseBundlePatchRow(
|
|
119
|
-
id:
|
|
120
|
-
bundle.id, patchArtifacts[i].baseBundleId),
|
|
96
|
+
id: buildBundlePatchId(bundle.id, patchArtifacts[i].baseBundleId),
|
|
121
97
|
bundleId: bundle.id,
|
|
122
98
|
baseBundleId: patchArtifacts[i].baseBundleId,
|
|
123
99
|
baseFileHash: patchArtifacts[i].baseFileHash,
|
|
@@ -7,18 +7,11 @@ library;
|
|
|
7
7
|
|
|
8
8
|
/// Minimal PostgREST query-builder surface used by the database plugin.
|
|
9
9
|
abstract class SupabaseQueryBuilderLike {
|
|
10
|
-
SupabaseFilterBuilderLike select(
|
|
11
|
-
String columns, {
|
|
12
|
-
bool? count,
|
|
13
|
-
bool? head,
|
|
14
|
-
});
|
|
10
|
+
SupabaseFilterBuilderLike select(String columns, {bool? count, bool? head});
|
|
15
11
|
|
|
16
12
|
SupabaseFilterBuilderLike delete();
|
|
17
13
|
|
|
18
|
-
Future<dynamic> upsert(
|
|
19
|
-
dynamic values, {
|
|
20
|
-
String? onConflict,
|
|
21
|
-
});
|
|
14
|
+
Future<dynamic> upsert(dynamic values, {String? onConflict});
|
|
22
15
|
}
|
|
23
16
|
|
|
24
17
|
/// Minimal PostgREST filter-builder surface.
|
|
@@ -47,10 +40,7 @@ abstract class SupabaseResponseLike {
|
|
|
47
40
|
|
|
48
41
|
/// Storage bucket surface used by the storage plugin.
|
|
49
42
|
abstract class SupabaseStorageBucketLike {
|
|
50
|
-
Future<SupabaseSignedUrlResult> createSignedUrl(
|
|
51
|
-
String path,
|
|
52
|
-
int expiresIn,
|
|
53
|
-
);
|
|
43
|
+
Future<SupabaseSignedUrlResult> createSignedUrl(String path, int expiresIn);
|
|
54
44
|
|
|
55
45
|
Future<SupabaseSignedUrlListResult> createSignedUrls(
|
|
56
46
|
List<String> paths,
|