mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-23 12:58:55 +00:00
parent
9c07407d50
commit
462856a24d
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,19 +1,22 @@
|
||||
2.3.0 Release notes (2018-2-19)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* Sync protocol changed to version 24.
|
||||
* History schema format for server-side Realm files bumped to version 4. This means that after the server has been upgraded, it cannot be downgraded again without restoring state from backup.
|
||||
* Backup protocol version bumped to 2. No compatibility with earlier versions of the backup protocol is provided.
|
||||
* [Object Server] Sync protocol changed to version 24.
|
||||
* [Object Server] History schema format for server-side Realm files bumped to version 4. This means that after the server has been upgraded, it cannot be downgraded again without restoring state from backup.
|
||||
* [Object Server] Backup protocol version bumped to 2. No compatibility with earlier versions of the backup protocol is provided.
|
||||
* [Object Server] `Realm.subscribeToObjects()` has been removed. Use `Realm.Results.subscribe()` instead.
|
||||
|
||||
### Enhancements
|
||||
* Reduced initial download times in Realms with long transaction histories.
|
||||
* Wait for pending notifications to complete when removing a sync listener (1648).
|
||||
* [Object Server] Reduced initial download times in Realms with long transaction histories.
|
||||
* [Object Server] Wait for pending notifications to complete when removing a sync listener (1648).
|
||||
* Enabled sort and distinct in the query string. If sort or distinct are also applied outside of the query string, the conditions are stacked.
|
||||
- Example syntax: `age > 20 SORT(name ASC, age DESC) DISTINCT(name)`
|
||||
- The ordering for sorting can be one of the following case insensitive literals: `ASC`, `ASCENDING`, `DESC`, `DESCENDING`.
|
||||
- Any number of properties can appear inside the brackets in a comma separated list.
|
||||
- Any number of sort/distinct conditions can be indicated, they will be applied in the specified order.
|
||||
- Sort or distinct cannot operate independently, these conditions must be attached to at least one query filter.
|
||||
* [Object Server] Added `Realm.Results.subscribe()` to subscribe to partial synced Realms.
|
||||
* [Object Server] Added class `Realm.Sync.Subscription` to support partial synced Realms.
|
||||
|
||||
### Internal
|
||||
* Updated to Realm Core 5.2.0.
|
||||
|
@ -134,6 +134,26 @@ class Collection {
|
||||
*/
|
||||
snapshot() {}
|
||||
|
||||
/**
|
||||
* Subscribe to a subset of objects matching the query of the collection. The Realm will only be
|
||||
* partially synced. Not all queries are currently supported. Once subscribed, it is highly recommended
|
||||
* to add a listener.
|
||||
*
|
||||
* @example
|
||||
* let wines = realm.objects('Wine').filtered('vintage <= $0', maxYear).subscribe();
|
||||
* wines.addListener((collection, changes) => {
|
||||
* if (changes.partial_sync.new_state == Realm.Sync.SubscriptionState.Initialized) {
|
||||
* // update UI
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* @param {string} subscriptionName - an optional name for the subscription.
|
||||
* @returns {Realm.Sync.Subscription} - the Realm.Sync.Subscription instance.
|
||||
* @throws {Error} if the partial sync is not enabled in the configuration or the query is not supported by Realm Object Server.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
subscribe(subscriptionName) {}
|
||||
|
||||
/**
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries Array.prototype.entries}
|
||||
* @returns {Realm.Collection~Iterator<T>} of each `[index, object]` pair in the collection
|
||||
@ -400,15 +420,21 @@ class Collection {
|
||||
* The callback function is called with two arguments:
|
||||
* - `collection`: the collection instance that changed,
|
||||
* - `changes`: a dictionary with keys `insertions`, `modifications` and `deletions`,
|
||||
* each containing a list of indices that were inserted, updated or deleted respectively.
|
||||
* each containing a list of indices that were inserted, updated or deleted respectively. If
|
||||
* partial sync is enabled, an additional key `partial_sync` is added.
|
||||
* - `changes.partial_sync`: `error` indicates if an error has occurred, `old_state` is the previous
|
||||
* state, and `new_state` is the current state.
|
||||
* @throws {Error} If `callback` is not a function.
|
||||
* @example
|
||||
* wines.addListener((collection, changes) => {
|
||||
* // collection === wines
|
||||
* console.log(`${changes.insertions.length} insertions`);
|
||||
* console.log(`${changes.modifications.length} modifications`);
|
||||
* console.log(`${changes.deletions.length} deletions`);
|
||||
* console.log(`new size of collection: ${collection.length}`);
|
||||
* if (changes.partial_sync.new_state == Realm.Sync.SubscriptionState.Initialized) {
|
||||
* console.log('Our subset is ready');
|
||||
* console.log(`${changes.insertions.length} insertions`);
|
||||
* console.log(`${changes.modifications.length} modifications`);
|
||||
* console.log(`${changes.deletions.length} deletions`);
|
||||
* console.log(`new size of collection: ${collection.length}`);
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
addListener(callback) {}
|
||||
|
@ -241,17 +241,6 @@ class Realm {
|
||||
* @returns {true} if compaction succeeds.
|
||||
*/
|
||||
compact() {}
|
||||
|
||||
/**
|
||||
* If the Realm is a partially synchronized Realm, fetch and synchronize the objects
|
||||
* of a given object type that match the given query (in string format).
|
||||
*
|
||||
* **Partial synchronization is a tech preview. Its APIs are subject to change.**
|
||||
* @param {Realm~ObjectType} type - The type of Realm objects to retrieve.
|
||||
* @param {string} query - Query used to filter objects.
|
||||
* @return {Promise} - a promise that will be resolved with the Realm.Results instance when it's available.
|
||||
*/
|
||||
subscribeToObjects(className, query, callback) {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
58
docs/sync.js
58
docs/sync.js
@ -468,6 +468,64 @@ class Session {
|
||||
removeProgressNotification(progressCallback) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object encapsulating partial sync subscriptions.
|
||||
* @memberof Realm.Sync
|
||||
*/
|
||||
class Subscription {
|
||||
/**
|
||||
* Gets the current state of the subscription.
|
||||
* Can be either:
|
||||
* - Realm.Sync.SubscriptionState.Error: An error occurred while creating or processing the partial sync subscription.
|
||||
* - Realm.Sync.SubscriptionState.Creating: The subscription is being created.
|
||||
* - Realm.Sync.SubscriptionState.Pending: The subscription was created, but has not yet been processed by the sync server.
|
||||
* - Realm.Sync.SubscriptionState.Complete: The subscription has been processed by the sync server and data is being synced to the device.
|
||||
* - Realm.Sync.SubscriptionState.Invalidated: The subscription has been removed.
|
||||
* @type {number}
|
||||
*/
|
||||
get state() {}
|
||||
|
||||
/**
|
||||
* Gets the error message. `undefined` if no error.
|
||||
* @type {string}
|
||||
*/
|
||||
get error() {}
|
||||
|
||||
/**
|
||||
* Unsubscribe a partial synced `Realm.Results`. The state will change to `Realm.Sync.SubscriptionState.Invalidated`.
|
||||
* The `Realm.Results` will not produce any meaningful values. Moreover, any objects matching the query will be
|
||||
* removed if they are not matched by any other query. The object removal is done asynchronously.
|
||||
*/
|
||||
unsubscribe() {}
|
||||
|
||||
/**
|
||||
* Adds a listener `callback` which will be called when the state of the subscription changes.
|
||||
* @param {function(state)} callback - A function to be called when changes occur.
|
||||
* @throws {Error} If `callback` is not a function.
|
||||
* @example
|
||||
* let subscription = results.subscribe();
|
||||
* subscription.addListener((subscription, state) => {
|
||||
* switch (state) {
|
||||
* case Realm.Sync.SubscriptionState.Complete:
|
||||
* // results is ready to be consumed
|
||||
* break;
|
||||
* case Realm.Sync.SubscriptionState.Error:
|
||||
* console.log('An error occurred: ', subscription.error);
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
addListener(callback) {}
|
||||
|
||||
/**
|
||||
* Remove the listener `callback` from the subscription instance.
|
||||
* @param {function(collection, changes)} callback - Callback function that was previously
|
||||
* added as a listener through the {@link Subscription#addListener addListener} method.
|
||||
* @throws {Error} If `callback` is not a function.
|
||||
*/
|
||||
removeListener(callback) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Realm Worker can be used to process Sync events in multiple automatically-managed child processes.
|
||||
*
|
||||
|
@ -20,7 +20,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||
@Override
|
||||
protected boolean getUseDeveloperSupport() {
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ export const propTypes = {};
|
||||
'RESULTS',
|
||||
'USER',
|
||||
'SESSION',
|
||||
'SUBSCRIPTION',
|
||||
'UNDEFINED',
|
||||
].forEach(function(type) {
|
||||
Object.defineProperty(objectTypes, type, {
|
||||
|
@ -130,7 +130,6 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
||||
'close',
|
||||
'_waitForDownload',
|
||||
'_objectForObjectId',
|
||||
'_subscribeToObjects',
|
||||
]);
|
||||
|
||||
// Mutating methods:
|
||||
@ -147,7 +146,8 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
||||
|
||||
const Sync = {
|
||||
User,
|
||||
Session
|
||||
Session,
|
||||
Subscription,
|
||||
};
|
||||
|
||||
Object.defineProperties(Realm, {
|
||||
|
@ -30,6 +30,7 @@ createMethods(Results.prototype, objectTypes.RESULTS, [
|
||||
'filtered',
|
||||
'sorted',
|
||||
'snapshot',
|
||||
'subscribe',
|
||||
'isValid',
|
||||
'indexOf',
|
||||
'min',
|
||||
|
38
lib/browser/subscription.js
Normal file
38
lib/browser/subscription.js
Normal file
@ -0,0 +1,38 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2018 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
'use strict';
|
||||
|
||||
import { objectTypes } from './constants';
|
||||
import { getterForProperty, createMethods } from './util';
|
||||
|
||||
export default class Subscription {
|
||||
|
||||
}
|
||||
|
||||
Object.defineProperties(Subscription.prototype, {
|
||||
error: { get: getterForProperty('error') },
|
||||
state: { get: getterForProperty('state') }
|
||||
});
|
||||
|
||||
// // Non-mutating methods:
|
||||
createMethods(Subscription.prototype, objectTypes.SUBSCRIPTION, [
|
||||
'unsubscribe',
|
||||
'addListener',
|
||||
'removeListener'
|
||||
]);
|
@ -159,18 +159,13 @@ module.exports = function(realmConstructor) {
|
||||
}
|
||||
}
|
||||
|
||||
realmConstructor.prototype.subscribeToObjects = function(objectType, query) {
|
||||
const realm = this;
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
realm._subscribeToObjects(objectType, query, function(err, results) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(results);
|
||||
}
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
// Keep these value in sync with subscription_state.hpp
|
||||
realmConstructor.Sync.SubscriptionState = {
|
||||
Error: -1, // An error occurred while creating or processing the partial sync subscription.
|
||||
Creating: 2, // The subscription is being created.
|
||||
Pending: 0, // The subscription was created, but has not yet been processed by the sync server.
|
||||
Complete: 1, // The subscription has been processed by the sync server and data is being synced to the device.
|
||||
Invalidated: 3, // The subscription has been removed.
|
||||
};
|
||||
}
|
||||
|
||||
|
25
lib/index.d.ts
vendored
25
lib/index.d.ts
vendored
@ -161,6 +161,11 @@ declare namespace Realm {
|
||||
sorted(descriptor: SortDescriptor[]): Results<T>;
|
||||
sorted(descriptor: string, reverse?: boolean): Results<T>;
|
||||
|
||||
/**
|
||||
* @returns Results<T>
|
||||
*/
|
||||
subscribe(subscriptionName?: string): Realm.Sync.Subscription;
|
||||
|
||||
/**
|
||||
* @returns Results
|
||||
*/
|
||||
@ -394,6 +399,21 @@ declare namespace Realm.Sync {
|
||||
removeProgressNotification(progressCallback: ProgressNotificationCallback): void;
|
||||
}
|
||||
|
||||
type SubscriptionNotificationCallback = (state: number) => void;
|
||||
|
||||
/**
|
||||
* Subscription
|
||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.Subscription.html }
|
||||
*/
|
||||
class Subscription {
|
||||
readonly state: number;
|
||||
readonly error: string;
|
||||
|
||||
unsubscribe(): void;
|
||||
addListener(subscruptionCallback: SubscriptionNotificationCallback): void;
|
||||
removeListener(subscruptionCallback: SubscriptionNotificationCallback): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* AuthError
|
||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.AuthError.html }
|
||||
@ -600,11 +620,6 @@ declare class Realm {
|
||||
* @returns boolean
|
||||
*/
|
||||
compact(): boolean;
|
||||
|
||||
/**
|
||||
* @returns Promise<Results<T>>
|
||||
*/
|
||||
subscribeToObjects<T>(objectType: string, query: string): Promise<Realm.Results<T>>;
|
||||
}
|
||||
|
||||
declare module 'realm' {
|
||||
|
@ -78,6 +78,7 @@ LOCAL_SRC_FILES += src/object-store/src/sync/sync_user.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/sync_permission.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/impl/sync_file.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/impl/sync_metadata.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/impl/work_queue.cpp
|
||||
endif
|
||||
|
||||
LOCAL_C_INCLUDES := src
|
||||
|
@ -99,6 +99,7 @@
|
||||
"src/object-store/src/sync/impl/sync_client.hpp",
|
||||
"src/object-store/src/sync/impl/sync_file.hpp",
|
||||
"src/object-store/src/sync/impl/sync_metadata.hpp",
|
||||
"src/object-store/src/sync/impl/work_queue.hpp",
|
||||
"src/object-store/src/sync/partial_sync.hpp",
|
||||
"src/object-store/src/sync/sync_config.hpp",
|
||||
"src/object-store/src/sync/sync_manager.hpp",
|
||||
@ -147,7 +148,8 @@
|
||||
"src/object-store/src/sync/sync_session.cpp",
|
||||
"src/object-store/src/sync/sync_config.cpp",
|
||||
"src/object-store/src/sync/impl/sync_file.cpp",
|
||||
"src/object-store/src/sync/impl/sync_metadata.cpp"
|
||||
"src/object-store/src/sync/impl/sync_metadata.cpp",
|
||||
"src/object-store/src/sync/impl/work_queue.cpp"
|
||||
],
|
||||
}]
|
||||
],
|
||||
|
@ -41,6 +41,8 @@
|
||||
3FCE2A931F58BE0300D4855B /* uuid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FCE2A911F58BDFF00D4855B /* uuid.cpp */; };
|
||||
3FCE2A971F58BE2200D4855B /* sync_permission.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FCE2A951F58BE1D00D4855B /* sync_permission.cpp */; };
|
||||
420FB79F1F7FBFE900D43D0F /* partial_sync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 423737AF1F7E333400FAEDFF /* partial_sync.cpp */; };
|
||||
425A121120235A1400C2F932 /* work_queue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 425A120F20235A1400C2F932 /* work_queue.cpp */; };
|
||||
4261AF8E203C42000052450D /* work_queue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 425A120F20235A1400C2F932 /* work_queue.cpp */; };
|
||||
502B07E41E2CD201007A84ED /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 502B07E31E2CD1FA007A84ED /* object.cpp */; };
|
||||
504CF85E1EBCAE3600A9A4B6 /* network_reachability_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 504CF8541EBCAE3600A9A4B6 /* network_reachability_observer.cpp */; };
|
||||
504CF85F1EBCAE3600A9A4B6 /* system_configuration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 504CF8561EBCAE3600A9A4B6 /* system_configuration.cpp */; };
|
||||
@ -185,6 +187,8 @@
|
||||
3FCE2A991F58BE3600D4855B /* feature_checks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = feature_checks.hpp; path = "object-store/src/feature_checks.hpp"; sourceTree = SOURCE_ROOT; };
|
||||
423737AF1F7E333400FAEDFF /* partial_sync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = partial_sync.cpp; path = src/sync/partial_sync.cpp; sourceTree = "<group>"; };
|
||||
423737B01F7E333400FAEDFF /* partial_sync.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = partial_sync.hpp; path = src/sync/partial_sync.hpp; sourceTree = "<group>"; };
|
||||
425A120F20235A1400C2F932 /* work_queue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = work_queue.cpp; sourceTree = "<group>"; };
|
||||
425A121020235A1400C2F932 /* work_queue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = work_queue.hpp; sourceTree = "<group>"; };
|
||||
426FCDFF1F7DA2F9005565DC /* sync_config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_config.cpp; path = src/sync/sync_config.cpp; sourceTree = "<group>"; };
|
||||
502B07E31E2CD1FA007A84ED /* object.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = object.cpp; path = src/object.cpp; sourceTree = "<group>"; };
|
||||
502B07E51E2CD20D007A84ED /* object.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = object.hpp; path = src/object.hpp; sourceTree = "<group>"; };
|
||||
@ -474,6 +478,8 @@
|
||||
504CF8521EBCAE3600A9A4B6 /* impl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
425A120F20235A1400C2F932 /* work_queue.cpp */,
|
||||
425A121020235A1400C2F932 /* work_queue.hpp */,
|
||||
504CF8531EBCAE3600A9A4B6 /* apple */,
|
||||
504CF8581EBCAE3600A9A4B6 /* network_reachability.hpp */,
|
||||
504CF8591EBCAE3600A9A4B6 /* sync_client.hpp */,
|
||||
@ -873,6 +879,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4261AF8E203C42000052450D /* work_queue.cpp in Sources */,
|
||||
F63FF2E21C15921A00B3B8E0 /* base64.cpp in Sources */,
|
||||
022BF1021E7266DF00F382F1 /* binding_callback_thread_observer.cpp in Sources */,
|
||||
02414BA51CE6ABCF00A8669F /* collection_change_builder.cpp in Sources */,
|
||||
@ -934,6 +941,7 @@
|
||||
F63FF31E1C1642BB00B3B8E0 /* GCDWebServerRequest.m in Sources */,
|
||||
F63FF31F1C1642BB00B3B8E0 /* GCDWebServerResponse.m in Sources */,
|
||||
F63FF3271C1642BB00B3B8E0 /* GCDWebServerStreamedResponse.m in Sources */,
|
||||
425A121120235A1400C2F932 /* work_queue.cpp in Sources */,
|
||||
F63FF3231C1642BB00B3B8E0 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -1115,10 +1123,7 @@
|
||||
"-isystem",
|
||||
"$(SRCROOT)/../vendor/sync/include",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = (
|
||||
"$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios-dbg.a",
|
||||
"$(SRCROOT)/../vendor/realm-ios/librealm-ios-dbg.a",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios-dbg.a $(SRCROOT)/../vendor/realm-ios/librealm-ios-dbg.a";
|
||||
PRODUCT_NAME = RealmJS;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
@ -1140,10 +1145,7 @@
|
||||
"-isystem",
|
||||
"$(SRCROOT)/../vendor/sync/include",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = (
|
||||
"$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios.a",
|
||||
"$(SRCROOT)/../vendor/realm-ios/librealm-ios.a",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios.a $(SRCROOT)/../vendor/realm-ios/librealm-ios.a";
|
||||
PRODUCT_NAME = RealmJS;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
|
@ -23,6 +23,9 @@
|
||||
#include "js_observable.hpp"
|
||||
|
||||
#include "collection_notifications.hpp"
|
||||
#if REALM_ENABLE_SYNC
|
||||
#include "sync/subscription_state.hpp"
|
||||
#endif
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
@ -39,7 +42,7 @@ struct CollectionClass : ClassDefinition<T, Collection, ObservableClass<T>> {
|
||||
using Value = js::Value<T>;
|
||||
|
||||
std::string const name = "Collection";
|
||||
|
||||
|
||||
static inline ValueType create_collection_change_set(ContextType ctx, const CollectionChangeSet &change_set);
|
||||
};
|
||||
|
||||
@ -48,7 +51,7 @@ typename T::Value CollectionClass<T>::create_collection_change_set(ContextType c
|
||||
{
|
||||
ObjectType object = Object::create_empty(ctx);
|
||||
std::vector<ValueType> deletions, insertions, modifications;
|
||||
|
||||
|
||||
if (change_set.deletions.count() == std::numeric_limits<size_t>::max()) {
|
||||
deletions.push_back(Value::from_null(ctx));
|
||||
}
|
||||
@ -58,12 +61,12 @@ typename T::Value CollectionClass<T>::create_collection_change_set(ContextType c
|
||||
}
|
||||
}
|
||||
Object::set_property(ctx, object, "deletions", Object::create_array(ctx, deletions));
|
||||
|
||||
|
||||
for (auto index : change_set.insertions.as_indexes()) {
|
||||
insertions.push_back(Value::from_number(ctx, index));
|
||||
}
|
||||
Object::set_property(ctx, object, "insertions", Object::create_array(ctx, insertions));
|
||||
|
||||
|
||||
for (auto index : change_set.modifications.as_indexes()) {
|
||||
modifications.push_back(Value::from_number(ctx, index));
|
||||
}
|
||||
|
@ -186,9 +186,6 @@ public:
|
||||
static void compact(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_model(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void object_for_object_id(ContextType, ObjectType, Arguments, ReturnValue&);
|
||||
#if REALM_ENABLE_SYNC
|
||||
static void subscribe_to_objects(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
#endif
|
||||
|
||||
// properties
|
||||
static void get_empty(ContextType, ObjectType, ReturnValue &);
|
||||
@ -248,7 +245,6 @@ public:
|
||||
{"_objectForObjectId", wrap<object_for_object_id>},
|
||||
#if REALM_ENABLE_SYNC
|
||||
{"_waitForDownload", wrap<wait_for_download_completion>},
|
||||
{"_subscribeToObjects", wrap<subscribe_to_objects>},
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -1061,51 +1057,5 @@ void RealmClass<T>::object_for_object_id(ContextType ctx, ObjectType this_object
|
||||
#endif // REALM_ENABLE_SYNC
|
||||
}
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
template<typename T>
|
||||
void RealmClass<T>::subscribe_to_objects(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_count(3);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
std::string object_type = Value::validated_to_string(ctx, args[0]);
|
||||
std::string query = Value::validated_to_string(ctx, args[1]);
|
||||
auto callback = Value::validated_to_function(ctx, args[2]);
|
||||
|
||||
auto &schema = realm->schema();
|
||||
auto object_schema = schema.find(object_type);
|
||||
|
||||
if (object_schema == schema.end()) {
|
||||
throw std::runtime_error("Object type '" + object_type + "' not found in schema.");
|
||||
}
|
||||
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
auto cb = [=](realm::Results results, std::exception_ptr err) {
|
||||
HANDLESCOPE
|
||||
|
||||
if (err) {
|
||||
try {
|
||||
std::rethrow_exception(err);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
ValueType callback_arguments[2];
|
||||
callback_arguments[0] = Value::from_string(protected_ctx, e.what());
|
||||
callback_arguments[1] = Value::from_null(protected_ctx);
|
||||
Function<T>::callback(ctx, protected_callback, protected_this, 2, callback_arguments);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ValueType callback_arguments[2];
|
||||
callback_arguments[0] = Value::from_null(protected_ctx);
|
||||
callback_arguments[1] = ResultsClass<T>::create_instance(protected_ctx, results);
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, callback_arguments);
|
||||
};
|
||||
|
||||
partial_sync::register_query(realm, object_type, query, std::move(cb));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
@ -28,6 +28,11 @@
|
||||
|
||||
#include <realm/parser/parser.hpp>
|
||||
#include <realm/parser/query_builder.hpp>
|
||||
#include <realm/util/optional.hpp>
|
||||
#ifdef REALM_ENABLE_SYNC
|
||||
#include "js_sync.hpp"
|
||||
#include "sync/partial_sync.hpp"
|
||||
#endif
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
@ -82,6 +87,9 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
||||
static void filtered(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void sorted(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void is_valid(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
#if REALM_ENABLE_SYNC
|
||||
static void subscribe(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
#endif
|
||||
|
||||
static void index_of(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
@ -107,6 +115,9 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
||||
{"filtered", wrap<filtered>},
|
||||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
#if REALM_ENABLE_SYNC
|
||||
{"subscribe", wrap<subscribe>},
|
||||
#endif
|
||||
{"min", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Min>>},
|
||||
{"max", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Max>>},
|
||||
{"sum", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Sum>>},
|
||||
@ -254,6 +265,28 @@ void ResultsClass<T>::is_valid(ContextType ctx, ObjectType this_object, Argument
|
||||
return_value.set(get_internal<T, ResultsClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
template<typename T>
|
||||
void ResultsClass<T>::subscribe(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
auto realm = results->get_realm();
|
||||
auto sync_config = realm->config().sync_config;
|
||||
|
||||
util::Optional<std::string> subscription_name;
|
||||
if (args.count == 1) {
|
||||
subscription_name = util::Optional<std::string>(Value::validated_to_string(ctx, args[0]));
|
||||
}
|
||||
else {
|
||||
subscription_name = util::none;
|
||||
}
|
||||
|
||||
auto subscription = partial_sync::subscribe(*results, subscription_name);
|
||||
return_value.set(SubscriptionClass<T>::create_instance(ctx, std::move(subscription)));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
template<typename Fn>
|
||||
void ResultsClass<T>::index_of(ContextType ctx, Fn& fn, Arguments args, ReturnValue &return_value) {
|
||||
@ -326,13 +359,13 @@ void ResultsClass<T>::add_listener(ContextType ctx, U& collection, ObjectType th
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
auto token = collection.add_notification_callback([=](CollectionChangeSet const& change_set, std::exception_ptr exception) {
|
||||
HANDLESCOPE
|
||||
ValueType arguments[] {
|
||||
static_cast<ObjectType>(protected_this),
|
||||
CollectionClass<T>::create_collection_change_set(protected_ctx, change_set)
|
||||
};
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
HANDLESCOPE
|
||||
ValueType arguments[] {
|
||||
static_cast<ObjectType>(protected_this),
|
||||
CollectionClass<T>::create_collection_change_set(protected_ctx, change_set)
|
||||
};
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
collection.m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
||||
}
|
||||
|
||||
|
122
src/js_sync.hpp
122
src/js_sync.hpp
@ -29,6 +29,7 @@
|
||||
#include "sync/sync_config.hpp"
|
||||
#include "sync/sync_session.hpp"
|
||||
#include "sync/sync_user.hpp"
|
||||
#include "sync/partial_sync.hpp"
|
||||
#include "realm/util/logger.hpp"
|
||||
#include "realm/util/uri.hpp"
|
||||
|
||||
@ -555,6 +556,127 @@ void SessionClass<T>::override_server(ContextType ctx, ObjectType this_object, A
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class Subscription : public partial_sync::Subscription {
|
||||
public:
|
||||
Subscription(partial_sync::Subscription s) : partial_sync::Subscription(std::move(s)) {}
|
||||
Subscription(Subscription &&) = default;
|
||||
|
||||
std::vector<std::pair<Protected<typename T::Function>, partial_sync::SubscriptionNotificationToken>> m_notification_tokens;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class SubscriptionClass : public ClassDefinition<T, Subscription<T>> {
|
||||
using GlobalContextType = typename T::GlobalContext;
|
||||
using ContextType = typename T::Context;
|
||||
using FunctionType = typename T::Function;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
using String = js::String<T>;
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using Function = js::Function<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
using Arguments = js::Arguments<T>;
|
||||
|
||||
public:
|
||||
std::string const name = "Subscription";
|
||||
|
||||
static FunctionType create_constructor(ContextType);
|
||||
static ObjectType create_instance(ContextType, partial_sync::Subscription);
|
||||
|
||||
static void get_state(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_error(ContextType, ObjectType, ReturnValue &);
|
||||
|
||||
static void unsubscribe(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void add_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
{"state", {wrap<get_state>, nullptr}},
|
||||
{"error", {wrap<get_error>, nullptr}}
|
||||
};
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
{"unsubscribe", wrap<unsubscribe>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
typename T::Object SubscriptionClass<T>::create_instance(ContextType ctx, partial_sync::Subscription subscription) {
|
||||
return create_object<T, SubscriptionClass<T>>(ctx, new Subscription<T>(std::move(subscription)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::get_state(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(object);
|
||||
return_value.set(static_cast<int8_t>(subscription->state()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::get_error(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(object);
|
||||
if (auto error = subscription->error()) {
|
||||
try {
|
||||
std::rethrow_exception(error);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
return_value.set(e.what());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return_value.set_undefined();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::unsubscribe(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(this_object);
|
||||
partial_sync::unsubscribe(*subscription);
|
||||
return_value.set_undefined();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::add_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(this_object);
|
||||
|
||||
auto callback = Value::validated_to_function(ctx, args[0]);
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
auto token = subscription->add_notification_callback([=]() {
|
||||
HANDLESCOPE
|
||||
|
||||
ValueType arguments[2];
|
||||
arguments[0] = static_cast<ObjectType>(protected_this),
|
||||
arguments[1] = Value::from_number(ctx, static_cast<double>(subscription->state()));
|
||||
Function::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
|
||||
subscription->m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::remove_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(this_object);
|
||||
|
||||
auto callback = Value::validated_to_function(ctx, args[0]);
|
||||
auto protected_function = Protected<FunctionType>(ctx, callback);
|
||||
|
||||
auto& tokens = subscription->m_notification_tokens;
|
||||
auto compare = [&](auto&& token) {
|
||||
return typename Protected<FunctionType>::Comparator()(token.first, protected_function);
|
||||
};
|
||||
tokens.erase(std::remove_if(tokens.begin(), tokens.end(), compare), tokens.end());
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
class SyncClass : public ClassDefinition<T, void*> {
|
||||
using GlobalContextType = typename T::GlobalContext;
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 56f246831c6bd1bd33cf0f852d049b11e94e50e9
|
||||
Subproject commit d8324724eff33b768d02dba7cc268520ed335536
|
@ -55,7 +55,7 @@ if (global.enableSyncTests) {
|
||||
// FIXME: Permission tests currently fail in chrome debugging mode.
|
||||
if (typeof navigator === 'undefined' ||
|
||||
!/Chrome/.test(navigator.userAgent)) { // eslint-disable-line no-undef
|
||||
TESTS.PermissionTests = require('./permission-tests');
|
||||
TESTS.PermissionTests = require('./permission-tests');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -523,7 +523,7 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmConstructor() {
|
||||
/* testProgressNotificationsForRealmConstructor() {
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
@ -555,7 +555,7 @@ module.exports = {
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
},*/
|
||||
|
||||
testProgressNotificationsUnregisterForRealmConstructor() {
|
||||
if (!isNodeProccess) {
|
||||
@ -707,8 +707,7 @@ module.exports = {
|
||||
});
|
||||
},
|
||||
|
||||
/* Disabled: waiting for new implementation
|
||||
testPartialSync() {
|
||||
testPartialSyncAnonymous_SubscriptionListener() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
@ -733,14 +732,205 @@ module.exports = {
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
return realm.subscribeToObjects("Dog", "name == 'Lassy 1'").then(results => {
|
||||
TestCase.assertEqual(results.length, 1);
|
||||
TestCase.assertTrue(results[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
subscription.addListener((subscription, state) => {
|
||||
if (state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(results.length, 1);
|
||||
TestCase.assertTrue(results[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
})
|
||||
})
|
||||
},
|
||||
*/
|
||||
|
||||
testPartialSyncAnonymous_ResultsListener() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
results.addListener((collection, changes) => {
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
testPartialSyncMultipleSubscriptions() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results1 = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var results2 = realm.objects('Dog').filtered("name == 'Lassy 2'");
|
||||
var subscription1 = results1.subscribe();
|
||||
var subscription2 = results2.subscribe();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let called1 = false;
|
||||
let called2 = false;
|
||||
results1.addListener((collection, changeset) => {
|
||||
if (subscription1.state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
called1 = true;
|
||||
if (called1 && called2) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
results2.addListener((collection, changeset) => {
|
||||
if (subscription2.state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 2', "The object is not synced correctly");
|
||||
called2 = true;
|
||||
if (called1 && called2) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
testPartialSyncFailing() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: false, // <---- calling subscribe should fail
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
TestCase.assertThrows(function () { var subscription = realm.objects('Dog').filtered("name == 'Lassy 1'").subscribe(); } );
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testPartialSyncUnsubscribe() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
results.addListener((collection, changes) => {
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Complete) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Invalidated) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testClientReset() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user