diff --git a/packages/realm_dart/lib/src/cli/atlas_apps/baas_client.dart b/packages/realm_dart/lib/src/cli/atlas_apps/baas_client.dart index 7d340f97f..5ab2d880d 100644 --- a/packages/realm_dart/lib/src/cli/atlas_apps/baas_client.dart +++ b/packages/realm_dart/lib/src/cli/atlas_apps/baas_client.dart @@ -159,7 +159,9 @@ class BaasClient { static final String defaultAppName = AppName.flexible.name; - final String _adminApiUrl; + String get _adminApiUrl => '$baseUrl/api/admin/v3.0'; + String get _privateApiUrl => '$baseUrl/api/private/v1.0'; + final String? _clusterName; final Map _headers; final String _appSuffix; @@ -170,8 +172,7 @@ class BaasClient { late String publicRSAKey = ''; BaasClient._(this.baseUrl, String differentiator, [this._clusterName]) - : _adminApiUrl = '$baseUrl/api/admin/v3.0', - _headers = {'Accept': 'application/json'}, + : _headers = {'Accept': 'application/json'}, _appSuffix = '-${shortenDifferentiator(differentiator)}${_clusterName == null ? '' : '-$_clusterName'}'; /// A client that imports apps in a MongoDB Atlas docker image. See https://github.com/realm/ci/tree/master/realm/docker/mongodb-realm @@ -211,7 +212,7 @@ class BaasClient { final authHelper = BaasAuthHelper(apiKey); final existing = (await _getContainers(authHelper, differentiator: differentiator)).firstOrNull; if (existing != null) { - print('Using existing BaaS container at ${existing.httpUrl}'); + print('Using existing BaaS container at ${existing.httpUrl}, id: ${existing.id}'); return (existing.httpUrl, existing.id); } @@ -227,7 +228,7 @@ class BaasClient { httpUrl = await _waitForContainer(authHelper, id); } - print('Deployed BaaS instance at $httpUrl'); + print('Deployed BaaS instance at $httpUrl, id: $id'); return (httpUrl, id); } @@ -333,18 +334,25 @@ class BaasClient { try { final response = await _get('groups/$_groupId/apps/$appId/sync/progress'); - final progressInfo = response['progress'] as Map; - for (final key in progressInfo.keys) { - final error = progressInfo[key]['error'] as String?; - if (error != null) { - print(error); - return false; - } + final acceptingClients = response['accepting_clients'] as bool? ?? false; + if (acceptingClients) { + return true; + } + + final progressInfo = response['progress'] as Map?; + if (progressInfo != null) { + for (final key in (progressInfo?.keys ?? {})) { + final error = progressInfo[key]['error'] as String?; + if (error != null) { + print(error); + return false; + } - final namespaceComplete = progressInfo[key]['complete'] as bool; + final namespaceComplete = progressInfo[key]['complete'] as bool; - if (!namespaceComplete) { - return false; + if (!namespaceComplete) { + return false; + } } } @@ -538,6 +546,10 @@ class BaasClient { }'''); } + print('Enable qbsv2 for ${app.name}'); + + await _setFeatureFlag(app, 'allow_qbs_v2'); + print('Creating database db_$uniqueName'); final mongoServiceId = await _createMongoDBService( @@ -631,6 +643,17 @@ class BaasClient { return result['key'] as String; } + Future _setFeatureFlag(BaasApp app, String feature, {bool enabled = true}) async { + final action = enabled ? 'enable' : 'disable'; + await _post( + 'groups/$_groupId/apps/$app/features', + '''{ + "action": "$action", + "feature_flags": [ "$feature" ] + }''', + apiRootPath: _privateApiUrl); + } + Future _authenticate(String provider, String credentials) async { dynamic response = await _post('auth/providers/$provider/login', credentials); @@ -766,12 +789,12 @@ class BaasClient { return additionalHeaders; } - Uri _getUri(String relativePath) { - return Uri.parse('$_adminApiUrl/$relativePath'); + Uri _getUri(String relativePath, {String? apiRootPath}) { + return Uri.parse('${apiRootPath ?? _adminApiUrl}/$relativePath'); } - Future _post(String relativePath, String payload) async { - var response = await http.post(_getUri(relativePath), headers: _getHeaders({'Content-Type': 'application/json'}), body: payload); + Future _post(String relativePath, String payload, {String? apiRootPath}) async { + var response = await http.post(_getUri(relativePath, apiRootPath: apiRootPath), headers: _getHeaders({'Content-Type': 'application/json'}), body: payload); return _decodeResponse(response, payload); } diff --git a/packages/realm_dart/lib/src/subscription.dart b/packages/realm_dart/lib/src/subscription.dart index ff814a198..ec16e2e8f 100644 --- a/packages/realm_dart/lib/src/subscription.dart +++ b/packages/realm_dart/lib/src/subscription.dart @@ -125,6 +125,11 @@ abstract interface class SubscriptionSet with Iterable { Future _waitForStateChange(SubscriptionSetState state, [CancellationToken? cancellationToken]) async { final result = await _handle.waitForStateChange(state, cancellationToken); + if (_handle.released) { + // We can get here if the Realm was closed before the wait has completed + throw CancelledException(cancellationReason: 'The Realm owning the subscription set was closed.'); + } + _handle.refresh(); return result; } diff --git a/packages/realm_dart/test/sync_migration_test.dart b/packages/realm_dart/test/sync_migration_test.dart index f8d6ed488..30412fce6 100644 --- a/packages/realm_dart/test/sync_migration_test.dart +++ b/packages/realm_dart/test/sync_migration_test.dart @@ -5,7 +5,6 @@ import 'dart:typed_data'; import 'package:realm_dart/realm.dart'; import 'package:realm_dart/src/configuration.dart'; -import 'package:test/test.dart' hide test, throws; import 'test.dart'; @@ -159,7 +158,7 @@ void main() { expect(objv2.stringValue, isNull); expect(objv2.uuidValue, isNull); expect(objv2.binaryValue, isNull); - }, appName: AppName.staticSchema, skip: true); + }, appName: AppName.staticSchema); baasTest('Can remove field', (appConfig) async { final differentiator = ObjectId(); @@ -237,7 +236,7 @@ void main() { expect(objv2.stringValue, isNull); expect(objv2.uuidValue, isNull); expect(objv2.binaryValue, isNull); - }, appName: AppName.staticSchema, skip:true); + }, appName: AppName.staticSchema); baasTest('Realm can be migrated skipping versions (0->2)', (appConfig) async { final differentiator = ObjectId(); @@ -262,5 +261,5 @@ void main() { expect(objv2.stringValue, isNull); expect(objv2.uuidValue, isNull); expect(objv2.binaryValue, isNull); - }, appName: AppName.staticSchema, skip: true); + }, appName: AppName.staticSchema); }