mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-12 15:24:18 +00:00
Merge branch '2.3.x' of github.com:realm/realm-js into jas/backlink-queries
This commit is contained in:
commit
8e63737008
90
CHANGELOG.md
90
CHANGELOG.md
@ -1,32 +1,65 @@
|
|||||||
2.3.0 Release notes (2018-2-19)
|
2.3.0 Release notes (2018-2-19)
|
||||||
=============================================================
|
=============================================================
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
* Sync protocol changed to version 24.
|
* [Sync] 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.
|
* [Sync] 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.
|
* [Sync] `Realm.subscribeToObjects()` has been removed. Use `Realm.Results.subscribe()` instead.
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
* Reduced initial download times in Realms with long transaction histories.
|
* [Sync] Reduced initial download times in Realms with long transaction histories.
|
||||||
* Wait for pending notifications to complete when removing a sync listener (1648).
|
* [Sync] 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.
|
* 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)`
|
- 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`.
|
- 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 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.
|
- 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.
|
- Sort or distinct cannot operate independently, these conditions must be attached to at least one query filter.
|
||||||
|
* [Sync] Added `Realm.Results.subscribe()` to subscribe to partial synced Realms.
|
||||||
|
* [Sync] Added class `Realm.Sync.Subscription` to support partial synced Realms.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* Updated to Realm Core 5.2.0.
|
* Updated to Realm Core 5.2.0.
|
||||||
* Updated to Realm Sync 3.0.0-beta.5.
|
* Updated to Realm Sync 3.0.0-beta.9.
|
||||||
* Tested against Realm Object Server 3.0.0-alpha.2.
|
* Tested against Realm Object Server 3.0.0-alpha.2.
|
||||||
|
|
||||||
|
|
||||||
|
2.2.10 Release notes (2018-2-20)
|
||||||
|
=============================================================
|
||||||
|
### Breaking changes
|
||||||
|
* None.
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
* None.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
* [Sync] A use-after-free bug was fixed which could cause arrays of primitives to behave unexpectedly.
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
* Updated to Realm Sync 2.2.12.
|
||||||
|
|
||||||
|
|
||||||
|
2.2.9 Release notes (2018-2-19)
|
||||||
|
=============================================================
|
||||||
|
### Breaking changes
|
||||||
|
* None.
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
* None.
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
* Improved root certificate checking.
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
* Updated to Realm Sync 2.2.11.
|
||||||
|
|
||||||
|
|
||||||
2.2.8 Release notes (2018-2-13)
|
2.2.8 Release notes (2018-2-13)
|
||||||
=============================================================
|
=============================================================
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
* [Object Server] For OpenSSL, the sync client includes a fixed list of certificates in its SSL certificate verification besides the default trust store in the case where the user is not specifying its own trust certificates or callback.
|
* [Sync] For OpenSSL, the sync client includes a fixed list of certificates in its SSL certificate verification besides the default trust store in the case where the user is not specifying its own trust certificates or callback.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* None.
|
* None.
|
||||||
@ -41,15 +74,16 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
* [Object Server] Wait for pending notifications to complete when removing a sync listener (#1648).
|
* [Sync] Wait for pending notifications to complete when removing a sync listener (#1648).
|
||||||
* Add schema name to missing primary key error message
|
* Add schema name to missing primary key error message
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed a bug causing use-after-free crashes in Global Notifier (realm-js-private #405).
|
* [Sync] Fixed a bug causing use-after-free crashes in Global Notifier (realm-js-private #405).
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* None.
|
* None.
|
||||||
|
|
||||||
|
|
||||||
2.2.6 Release notes (2018-1-26)
|
2.2.6 Release notes (2018-1-26)
|
||||||
=============================================================
|
=============================================================
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
@ -59,7 +93,7 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed a bug where arguments were not transferred when debugging.
|
* [Sync] Fixed a bug where arguments were not transferred when debugging.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* None.
|
* None.
|
||||||
@ -73,7 +107,7 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed a typing error leading to `_getExistingUser` wasn't defined in the Chrome debugging support library (#1625).
|
* [Sync] Fixed a typing error leading to `_getExistingUser` wasn't defined in the Chrome debugging support library (#1625).
|
||||||
* Fixed a bug in the TypeScript definition of `PermissionCondition` (#1574).
|
* Fixed a bug in the TypeScript definition of `PermissionCondition` (#1574).
|
||||||
* [Electron] Fixed a `dlopen` error related to OpenSSL that prevented using realm-js on Linux (#1636).
|
* [Electron] Fixed a `dlopen` error related to OpenSSL that prevented using realm-js on Linux (#1636).
|
||||||
|
|
||||||
@ -89,8 +123,8 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed a bug where errors in `refreshAdminToken` wasn't catched (#1627).
|
* [Sync] Fixed a bug where errors in `refreshAdminToken` wasn't catched (#1627).
|
||||||
* [Object Server] Added `_getExitingUser` to the Chrome debugging support library.
|
* [Sync] Added `_getExitingUser` to the Chrome debugging support library.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* None.
|
* None.
|
||||||
@ -104,8 +138,8 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed a bug in upload progress reporting.
|
* [Sync] Fixed a bug in upload progress reporting.
|
||||||
* [Object Server] Fixed a bug where any errors which occurred when trying to sync the admin Realm were ignored, which made attempting to add a listener with an invalid admin user silently do nothing.
|
* [Sync] Fixed a bug where any errors which occurred when trying to sync the admin Realm were ignored, which made attempting to add a listener with an invalid admin user silently do nothing.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* None.
|
* None.
|
||||||
@ -119,7 +153,7 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Added missing `Realm.Sync` listener functions.
|
* [Sync] Added missing `Realm.Sync` listener functions.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* None.
|
* None.
|
||||||
@ -134,7 +168,7 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed a bug preventing opening Realms with an admin token without a working ROS directory service (#1615).
|
* [Sync] Fixed a bug preventing opening Realms with an admin token without a working ROS directory service (#1615).
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* None.
|
* None.
|
||||||
@ -147,8 +181,8 @@
|
|||||||
### Enhancements
|
### Enhancements
|
||||||
* Added new query features to support a subset of `NSPredicates` for example `LIKE` for string matches, `@count` and `@sum` in lists. See documentation for more details.
|
* Added new query features to support a subset of `NSPredicates` for example `LIKE` for string matches, `@count` and `@sum` in lists. See documentation for more details.
|
||||||
* Potential performance enhancements in cases of many writes between queries.
|
* Potential performance enhancements in cases of many writes between queries.
|
||||||
* [Object Server] Added method `Realm.Sync.User.authenticate` to unify authentication of users.
|
* [Sync] Added method `Realm.Sync.User.authenticate` to unify authentication of users.
|
||||||
* [Object Server] Added JWT authenfication (#1548).
|
* [Sync] Added JWT authenfication (#1548).
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* Fix a bug where `Realm.open` could unexpectedly raise a "Realm at path ... already opened with different schema version" error.
|
* Fix a bug where `Realm.open` could unexpectedly raise a "Realm at path ... already opened with different schema version" error.
|
||||||
@ -169,10 +203,10 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed a bug where long reconnection happens when a proxy in front of the sync worker returns one of those.
|
* [Sync] Fixed a bug where long reconnection happens when a proxy in front of the sync worker returns one of those.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* [Object Server] Updated to Realm Object Server v2.2.0 for testing.
|
* [Sync] Updated to Realm Object Server v2.2.0 for testing.
|
||||||
* Updated to Realm Sync 2.1.10 (see "Bug fixes").
|
* Updated to Realm Sync 2.1.10 (see "Bug fixes").
|
||||||
|
|
||||||
|
|
||||||
@ -200,11 +234,11 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] When authentication fails due to a misbehaving server, a proper error is thrown.
|
* [Sync] When authentication fails due to a misbehaving server, a proper error is thrown.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* [Object Server] Strings can now be assigned to Date columns. When that happens the JavaScript Date constructor will be invoked to parse the string.
|
* [Sync] Strings can now be assigned to Date columns. When that happens the JavaScript Date constructor will be invoked to parse the string.
|
||||||
* [Object Server] Base64 strings can now be assigned to Data columns.
|
* [Sync] Base64 strings can now be assigned to Data columns.
|
||||||
|
|
||||||
2.0.12 Release notes (2017-12-1)
|
2.0.12 Release notes (2017-12-1)
|
||||||
=============================================================
|
=============================================================
|
||||||
@ -230,8 +264,8 @@
|
|||||||
* None
|
* None
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed a bug where deleted-then-recreated objects with identical primary keys to become empty.
|
* [Sync] Fixed a bug where deleted-then-recreated objects with identical primary keys to become empty.
|
||||||
* [Object Server] Fixed a bug in outward partial sync is changed to ensure convergence of partial sync in the case where the client creates a primary key object, that is already present on the server, and subscribes to it in the same transaction.
|
* [Sync] Fixed a bug in outward partial sync is changed to ensure convergence of partial sync in the case where the client creates a primary key object, that is already present on the server, and subscribes to it in the same transaction.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* Updated to Realm Sync 2.1.7 (see under "Bug fixes").
|
* Updated to Realm Sync 2.1.7 (see under "Bug fixes").
|
||||||
@ -270,10 +304,10 @@
|
|||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
* [Object Server] Improving performance of processing large changesets.
|
* [Sync] Improving performance of processing large changesets.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Changesets over 16MB in size are now handled correctly.
|
* [Sync] Changesets over 16MB in size are now handled correctly.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* Updated to Realm Sync 2.1.6.
|
* Updated to Realm Sync 2.1.6.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
PACKAGE_NAME=realm-js
|
PACKAGE_NAME=realm-js
|
||||||
VERSION=2.3.0-alpha.11
|
VERSION=2.3.0-alpha.13
|
||||||
REALM_CORE_VERSION=5.2.0
|
REALM_CORE_VERSION=5.2.0
|
||||||
REALM_SYNC_VERSION=3.0.0-beta.6
|
REALM_SYNC_VERSION=3.0.0-beta.9
|
||||||
REALM_OBJECT_SERVER_VERSION=3.0.0-alpha.2
|
REALM_OBJECT_SERVER_VERSION=3.0.0-alpha.2
|
||||||
|
@ -134,6 +134,26 @@ class Collection {
|
|||||||
*/
|
*/
|
||||||
snapshot() {}
|
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}
|
* @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
|
* @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:
|
* The callback function is called with two arguments:
|
||||||
* - `collection`: the collection instance that changed,
|
* - `collection`: the collection instance that changed,
|
||||||
* - `changes`: a dictionary with keys `insertions`, `modifications` and `deletions`,
|
* - `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.
|
* @throws {Error} If `callback` is not a function.
|
||||||
* @example
|
* @example
|
||||||
* wines.addListener((collection, changes) => {
|
* wines.addListener((collection, changes) => {
|
||||||
* // collection === wines
|
* // collection === wines
|
||||||
* console.log(`${changes.insertions.length} insertions`);
|
* if (changes.partial_sync.new_state == Realm.Sync.SubscriptionState.Initialized) {
|
||||||
* console.log(`${changes.modifications.length} modifications`);
|
* console.log('Our subset is ready');
|
||||||
* console.log(`${changes.deletions.length} deletions`);
|
* console.log(`${changes.insertions.length} insertions`);
|
||||||
* console.log(`new size of collection: ${collection.length}`);
|
* console.log(`${changes.modifications.length} modifications`);
|
||||||
|
* console.log(`${changes.deletions.length} deletions`);
|
||||||
|
* console.log(`new size of collection: ${collection.length}`);
|
||||||
|
* }
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
addListener(callback) {}
|
addListener(callback) {}
|
||||||
|
@ -241,17 +241,6 @@ class Realm {
|
|||||||
* @returns {true} if compaction succeeds.
|
* @returns {true} if compaction succeeds.
|
||||||
*/
|
*/
|
||||||
compact() {}
|
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) {}
|
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.
|
* 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) {
|
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||||
@Override
|
@Override
|
||||||
protected boolean getUseDeveloperSupport() {
|
public boolean getUseDeveloperSupport() {
|
||||||
return BuildConfig.DEBUG;
|
return BuildConfig.DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ export const propTypes = {};
|
|||||||
'RESULTS',
|
'RESULTS',
|
||||||
'USER',
|
'USER',
|
||||||
'SESSION',
|
'SESSION',
|
||||||
|
'SUBSCRIPTION',
|
||||||
'UNDEFINED',
|
'UNDEFINED',
|
||||||
].forEach(function(type) {
|
].forEach(function(type) {
|
||||||
Object.defineProperty(objectTypes, type, {
|
Object.defineProperty(objectTypes, type, {
|
||||||
|
@ -130,7 +130,6 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
|||||||
'close',
|
'close',
|
||||||
'_waitForDownload',
|
'_waitForDownload',
|
||||||
'_objectForObjectId',
|
'_objectForObjectId',
|
||||||
'_subscribeToObjects',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Mutating methods:
|
// Mutating methods:
|
||||||
@ -147,7 +146,8 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
|||||||
|
|
||||||
const Sync = {
|
const Sync = {
|
||||||
User,
|
User,
|
||||||
Session
|
Session,
|
||||||
|
Subscription,
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.defineProperties(Realm, {
|
Object.defineProperties(Realm, {
|
||||||
|
@ -30,6 +30,7 @@ createMethods(Results.prototype, objectTypes.RESULTS, [
|
|||||||
'filtered',
|
'filtered',
|
||||||
'sorted',
|
'sorted',
|
||||||
'snapshot',
|
'snapshot',
|
||||||
|
'subscribe',
|
||||||
'isValid',
|
'isValid',
|
||||||
'indexOf',
|
'indexOf',
|
||||||
'min',
|
'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) {
|
// Keep these value in sync with subscription_state.hpp
|
||||||
const realm = this;
|
realmConstructor.Sync.SubscriptionState = {
|
||||||
let promise = new Promise((resolve, reject) => {
|
Error: -1, // An error occurred while creating or processing the partial sync subscription.
|
||||||
realm._subscribeToObjects(objectType, query, function(err, results) {
|
Creating: 2, // The subscription is being created.
|
||||||
if (err) {
|
Pending: 0, // The subscription was created, but has not yet been processed by the sync server.
|
||||||
reject(err);
|
Complete: 1, // The subscription has been processed by the sync server and data is being synced to the device.
|
||||||
} else {
|
Invalidated: 3, // The subscription has been removed.
|
||||||
resolve(results);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return promise;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
29
lib/index.d.ts
vendored
29
lib/index.d.ts
vendored
@ -161,6 +161,11 @@ declare namespace Realm {
|
|||||||
sorted(descriptor: SortDescriptor[]): Results<T>;
|
sorted(descriptor: SortDescriptor[]): Results<T>;
|
||||||
sorted(descriptor: string, reverse?: boolean): Results<T>;
|
sorted(descriptor: string, reverse?: boolean): Results<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Results<T>
|
||||||
|
*/
|
||||||
|
subscribe(subscriptionName?: string): Realm.Sync.Subscription;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns Results
|
* @returns Results
|
||||||
*/
|
*/
|
||||||
@ -394,6 +399,21 @@ declare namespace Realm.Sync {
|
|||||||
removeProgressNotification(progressCallback: ProgressNotificationCallback): void;
|
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
|
* AuthError
|
||||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.AuthError.html }
|
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.AuthError.html }
|
||||||
@ -545,9 +565,9 @@ declare class Realm {
|
|||||||
/**
|
/**
|
||||||
* @param {string|Realm.ObjectSchema|Function} type
|
* @param {string|Realm.ObjectSchema|Function} type
|
||||||
* @param {number|string} key
|
* @param {number|string} key
|
||||||
* @returns T
|
* @returns {T | undefined}
|
||||||
*/
|
*/
|
||||||
objectForPrimaryKey<T>(type: string | Realm.ObjectSchema | Function, key: number | string): T | null;
|
objectForPrimaryKey<T>(type: string | Realm.ObjectSchema | Function, key: number | string): T | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string|Realm.ObjectType|Function} type
|
* @param {string|Realm.ObjectType|Function} type
|
||||||
@ -600,11 +620,6 @@ declare class Realm {
|
|||||||
* @returns boolean
|
* @returns boolean
|
||||||
*/
|
*/
|
||||||
compact(): boolean;
|
compact(): boolean;
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns Promise<Results<T>>
|
|
||||||
*/
|
|
||||||
subscribeToObjects<T>(objectType: string, query: string): Promise<Realm.Results<T>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'realm' {
|
declare module 'realm' {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "realm",
|
"name": "realm",
|
||||||
"description": "Realm is a mobile database: an alternative to SQLite and key-value stores",
|
"description": "Realm is a mobile database: an alternative to SQLite and key-value stores",
|
||||||
"version": "2.3.0-alpha.11",
|
"version": "2.3.0-alpha.13",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"homepage": "https://realm.io",
|
"homepage": "https://realm.io",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
@ -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/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_file.cpp
|
||||||
LOCAL_SRC_FILES += src/object-store/src/sync/impl/sync_metadata.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
|
endif
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := src
|
LOCAL_C_INCLUDES := src
|
||||||
|
@ -99,6 +99,7 @@
|
|||||||
"src/object-store/src/sync/impl/sync_client.hpp",
|
"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_file.hpp",
|
||||||
"src/object-store/src/sync/impl/sync_metadata.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/partial_sync.hpp",
|
||||||
"src/object-store/src/sync/sync_config.hpp",
|
"src/object-store/src/sync/sync_config.hpp",
|
||||||
"src/object-store/src/sync/sync_manager.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_session.cpp",
|
||||||
"src/object-store/src/sync/sync_config.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_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 */; };
|
3FCE2A931F58BE0300D4855B /* uuid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FCE2A911F58BDFF00D4855B /* uuid.cpp */; };
|
||||||
3FCE2A971F58BE2200D4855B /* sync_permission.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FCE2A951F58BE1D00D4855B /* sync_permission.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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
502B07E51E2CD20D007A84ED /* object.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = object.hpp; path = src/object.hpp; sourceTree = "<group>"; };
|
||||||
@ -474,6 +478,8 @@
|
|||||||
504CF8521EBCAE3600A9A4B6 /* impl */ = {
|
504CF8521EBCAE3600A9A4B6 /* impl */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
425A120F20235A1400C2F932 /* work_queue.cpp */,
|
||||||
|
425A121020235A1400C2F932 /* work_queue.hpp */,
|
||||||
504CF8531EBCAE3600A9A4B6 /* apple */,
|
504CF8531EBCAE3600A9A4B6 /* apple */,
|
||||||
504CF8581EBCAE3600A9A4B6 /* network_reachability.hpp */,
|
504CF8581EBCAE3600A9A4B6 /* network_reachability.hpp */,
|
||||||
504CF8591EBCAE3600A9A4B6 /* sync_client.hpp */,
|
504CF8591EBCAE3600A9A4B6 /* sync_client.hpp */,
|
||||||
@ -873,6 +879,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
4261AF8E203C42000052450D /* work_queue.cpp in Sources */,
|
||||||
F63FF2E21C15921A00B3B8E0 /* base64.cpp in Sources */,
|
F63FF2E21C15921A00B3B8E0 /* base64.cpp in Sources */,
|
||||||
022BF1021E7266DF00F382F1 /* binding_callback_thread_observer.cpp in Sources */,
|
022BF1021E7266DF00F382F1 /* binding_callback_thread_observer.cpp in Sources */,
|
||||||
02414BA51CE6ABCF00A8669F /* collection_change_builder.cpp in Sources */,
|
02414BA51CE6ABCF00A8669F /* collection_change_builder.cpp in Sources */,
|
||||||
@ -934,6 +941,7 @@
|
|||||||
F63FF31E1C1642BB00B3B8E0 /* GCDWebServerRequest.m in Sources */,
|
F63FF31E1C1642BB00B3B8E0 /* GCDWebServerRequest.m in Sources */,
|
||||||
F63FF31F1C1642BB00B3B8E0 /* GCDWebServerResponse.m in Sources */,
|
F63FF31F1C1642BB00B3B8E0 /* GCDWebServerResponse.m in Sources */,
|
||||||
F63FF3271C1642BB00B3B8E0 /* GCDWebServerStreamedResponse.m in Sources */,
|
F63FF3271C1642BB00B3B8E0 /* GCDWebServerStreamedResponse.m in Sources */,
|
||||||
|
425A121120235A1400C2F932 /* work_queue.cpp in Sources */,
|
||||||
F63FF3231C1642BB00B3B8E0 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
F63FF3231C1642BB00B3B8E0 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -1115,10 +1123,7 @@
|
|||||||
"-isystem",
|
"-isystem",
|
||||||
"$(SRCROOT)/../vendor/sync/include",
|
"$(SRCROOT)/../vendor/sync/include",
|
||||||
);
|
);
|
||||||
OTHER_LIBTOOLFLAGS = (
|
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios-dbg.a $(SRCROOT)/../vendor/realm-ios/librealm-ios-dbg.a";
|
||||||
"$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios-dbg.a",
|
|
||||||
"$(SRCROOT)/../vendor/realm-ios/librealm-ios-dbg.a",
|
|
||||||
);
|
|
||||||
PRODUCT_NAME = RealmJS;
|
PRODUCT_NAME = RealmJS;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
};
|
};
|
||||||
@ -1140,10 +1145,7 @@
|
|||||||
"-isystem",
|
"-isystem",
|
||||||
"$(SRCROOT)/../vendor/sync/include",
|
"$(SRCROOT)/../vendor/sync/include",
|
||||||
);
|
);
|
||||||
OTHER_LIBTOOLFLAGS = (
|
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios.a $(SRCROOT)/../vendor/realm-ios/librealm-ios.a";
|
||||||
"$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios.a",
|
|
||||||
"$(SRCROOT)/../vendor/realm-ios/librealm-ios.a",
|
|
||||||
);
|
|
||||||
PRODUCT_NAME = RealmJS;
|
PRODUCT_NAME = RealmJS;
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
};
|
};
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
#include "js_observable.hpp"
|
#include "js_observable.hpp"
|
||||||
|
|
||||||
#include "collection_notifications.hpp"
|
#include "collection_notifications.hpp"
|
||||||
|
#if REALM_ENABLE_SYNC
|
||||||
|
#include "sync/subscription_state.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace realm {
|
namespace realm {
|
||||||
namespace js {
|
namespace js {
|
||||||
@ -39,7 +42,7 @@ struct CollectionClass : ClassDefinition<T, Collection, ObservableClass<T>> {
|
|||||||
using Value = js::Value<T>;
|
using Value = js::Value<T>;
|
||||||
|
|
||||||
std::string const name = "Collection";
|
std::string const name = "Collection";
|
||||||
|
|
||||||
static inline ValueType create_collection_change_set(ContextType ctx, const CollectionChangeSet &change_set);
|
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);
|
ObjectType object = Object::create_empty(ctx);
|
||||||
std::vector<ValueType> deletions, insertions, modifications;
|
std::vector<ValueType> deletions, insertions, modifications;
|
||||||
|
|
||||||
if (change_set.deletions.count() == std::numeric_limits<size_t>::max()) {
|
if (change_set.deletions.count() == std::numeric_limits<size_t>::max()) {
|
||||||
deletions.push_back(Value::from_null(ctx));
|
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));
|
Object::set_property(ctx, object, "deletions", Object::create_array(ctx, deletions));
|
||||||
|
|
||||||
for (auto index : change_set.insertions.as_indexes()) {
|
for (auto index : change_set.insertions.as_indexes()) {
|
||||||
insertions.push_back(Value::from_number(ctx, index));
|
insertions.push_back(Value::from_number(ctx, index));
|
||||||
}
|
}
|
||||||
Object::set_property(ctx, object, "insertions", Object::create_array(ctx, insertions));
|
Object::set_property(ctx, object, "insertions", Object::create_array(ctx, insertions));
|
||||||
|
|
||||||
for (auto index : change_set.modifications.as_indexes()) {
|
for (auto index : change_set.modifications.as_indexes()) {
|
||||||
modifications.push_back(Value::from_number(ctx, index));
|
modifications.push_back(Value::from_number(ctx, index));
|
||||||
}
|
}
|
||||||
|
@ -186,9 +186,6 @@ public:
|
|||||||
static void compact(ContextType, ObjectType, Arguments, ReturnValue &);
|
static void compact(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void delete_model(ContextType, ObjectType, Arguments, ReturnValue &);
|
static void delete_model(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void object_for_object_id(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
|
// properties
|
||||||
static void get_empty(ContextType, ObjectType, ReturnValue &);
|
static void get_empty(ContextType, ObjectType, ReturnValue &);
|
||||||
@ -248,7 +245,6 @@ public:
|
|||||||
{"_objectForObjectId", wrap<object_for_object_id>},
|
{"_objectForObjectId", wrap<object_for_object_id>},
|
||||||
#if REALM_ENABLE_SYNC
|
#if REALM_ENABLE_SYNC
|
||||||
{"_waitForDownload", wrap<wait_for_download_completion>},
|
{"_waitForDownload", wrap<wait_for_download_completion>},
|
||||||
{"_subscribeToObjects", wrap<subscribe_to_objects>},
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1061,51 +1057,5 @@ void RealmClass<T>::object_for_object_id(ContextType ctx, ObjectType this_object
|
|||||||
#endif // REALM_ENABLE_SYNC
|
#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
|
} // js
|
||||||
} // realm
|
} // realm
|
||||||
|
@ -28,6 +28,11 @@
|
|||||||
|
|
||||||
#include <realm/parser/parser.hpp>
|
#include <realm/parser/parser.hpp>
|
||||||
#include <realm/parser/query_builder.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 realm {
|
||||||
namespace js {
|
namespace js {
|
||||||
@ -82,6 +87,9 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||||||
static void filtered(ContextType, ObjectType, Arguments, ReturnValue &);
|
static void filtered(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void sorted(ContextType, ObjectType, Arguments, ReturnValue &);
|
static void sorted(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||||
static void is_valid(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 &);
|
static void index_of(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||||
|
|
||||||
@ -107,6 +115,9 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||||||
{"filtered", wrap<filtered>},
|
{"filtered", wrap<filtered>},
|
||||||
{"sorted", wrap<sorted>},
|
{"sorted", wrap<sorted>},
|
||||||
{"isValid", wrap<is_valid>},
|
{"isValid", wrap<is_valid>},
|
||||||
|
#if REALM_ENABLE_SYNC
|
||||||
|
{"subscribe", wrap<subscribe>},
|
||||||
|
#endif
|
||||||
{"min", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Min>>},
|
{"min", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Min>>},
|
||||||
{"max", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Max>>},
|
{"max", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Max>>},
|
||||||
{"sum", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Sum>>},
|
{"sum", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Sum>>},
|
||||||
@ -271,6 +282,28 @@ void ResultsClass<T>::is_valid(ContextType ctx, ObjectType this_object, Argument
|
|||||||
return_value.set(get_internal<T, ResultsClass<T>>(this_object)->is_valid());
|
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 T>
|
||||||
template<typename Fn>
|
template<typename Fn>
|
||||||
void ResultsClass<T>::index_of(ContextType ctx, Fn& fn, Arguments args, ReturnValue &return_value) {
|
void ResultsClass<T>::index_of(ContextType ctx, Fn& fn, Arguments args, ReturnValue &return_value) {
|
||||||
@ -343,13 +376,13 @@ void ResultsClass<T>::add_listener(ContextType ctx, U& collection, ObjectType th
|
|||||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
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) {
|
auto token = collection.add_notification_callback([=](CollectionChangeSet const& change_set, std::exception_ptr exception) {
|
||||||
HANDLESCOPE
|
HANDLESCOPE
|
||||||
ValueType arguments[] {
|
ValueType arguments[] {
|
||||||
static_cast<ObjectType>(protected_this),
|
static_cast<ObjectType>(protected_this),
|
||||||
CollectionClass<T>::create_collection_change_set(protected_ctx, change_set)
|
CollectionClass<T>::create_collection_change_set(protected_ctx, change_set)
|
||||||
};
|
};
|
||||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||||
});
|
});
|
||||||
collection.m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
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_config.hpp"
|
||||||
#include "sync/sync_session.hpp"
|
#include "sync/sync_session.hpp"
|
||||||
#include "sync/sync_user.hpp"
|
#include "sync/sync_user.hpp"
|
||||||
|
#include "sync/partial_sync.hpp"
|
||||||
#include "realm/util/logger.hpp"
|
#include "realm/util/logger.hpp"
|
||||||
#include "realm/util/uri.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>
|
template<typename T>
|
||||||
class SyncClass : public ClassDefinition<T, void*> {
|
class SyncClass : public ClassDefinition<T, void*> {
|
||||||
using GlobalContextType = typename T::GlobalContext;
|
using GlobalContextType = typename T::GlobalContext;
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 56f246831c6bd1bd33cf0f852d049b11e94e50e9
|
Subproject commit d5fe9a626c0adc0345d128201e0df8bb4004725d
|
@ -44,7 +44,7 @@ var TESTS = {
|
|||||||
MigrationTests: require('./migration-tests'),
|
MigrationTests: require('./migration-tests'),
|
||||||
EncryptionTests: require('./encryption-tests'),
|
EncryptionTests: require('./encryption-tests'),
|
||||||
ObjectIDTests: require('./object-id-tests'),
|
ObjectIDTests: require('./object-id-tests'),
|
||||||
// GarbageCollectionTests: require('./garbage-collection'),
|
// Garbagecollectiontests: require('./garbage-collection'),
|
||||||
};
|
};
|
||||||
|
|
||||||
// If sync is enabled, run the sync tests
|
// If sync is enabled, run the sync tests
|
||||||
@ -55,7 +55,7 @@ if (global.enableSyncTests) {
|
|||||||
// FIXME: Permission tests currently fail in chrome debugging mode.
|
// FIXME: Permission tests currently fail in chrome debugging mode.
|
||||||
if (typeof navigator === 'undefined' ||
|
if (typeof navigator === 'undefined' ||
|
||||||
!/Chrome/.test(navigator.userAgent)) { // eslint-disable-line no-undef
|
!/Chrome/.test(navigator.userAgent)) { // eslint-disable-line no-undef
|
||||||
TESTS.PermissionTests = require('./permission-tests');
|
TESTS.PermissionTests = require('./permission-tests');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ module.exports = {
|
|||||||
TestCase.assertInstanceOf(obj.list, Realm.List);
|
TestCase.assertInstanceOf(obj.list, Realm.List);
|
||||||
TestCase.assertInstanceOf(obj.list, Realm.Collection);
|
TestCase.assertInstanceOf(obj.list, Realm.Collection);
|
||||||
});
|
});
|
||||||
|
|
||||||
TestCase.assertThrowsContaining(() => new Realm.List(), 'constructor');
|
TestCase.assertThrowsContaining(() => new Realm.List(), 'constructor');
|
||||||
TestCase.assertInstanceOf(Realm.List, Function);
|
TestCase.assertInstanceOf(Realm.List, Function);
|
||||||
},
|
},
|
||||||
@ -1223,4 +1223,83 @@ module.exports = {
|
|||||||
TestCase.assertThrowsContaining(() => object.list.avg(),
|
TestCase.assertThrowsContaining(() => object.list.avg(),
|
||||||
"JS value must be of type 'string', got (undefined)");
|
"JS value must be of type 'string', got (undefined)");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
testListNested: function() {
|
||||||
|
const realm = new Realm({schema: [schemas.ParentObject, schemas.NameObject]});
|
||||||
|
realm.write(() => {
|
||||||
|
realm.create('ParentObject', {
|
||||||
|
id: 1,
|
||||||
|
name: [
|
||||||
|
{ family: 'Larsen', given: ['Hans', 'Jørgen'], prefix: [] },
|
||||||
|
{ family: 'Hansen', given: ['Ib'], prefix: [] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
realm.create('ParentObject', {
|
||||||
|
id: 2,
|
||||||
|
name: [
|
||||||
|
{family: 'Petersen', given: ['Gurli', 'Margrete'], prefix: [] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let objects = realm.objects('ParentObject');
|
||||||
|
TestCase.assertEqual(objects.length, 2);
|
||||||
|
TestCase.assertEqual(objects[0].name.length, 2);
|
||||||
|
TestCase.assertEqual(objects[0].name[0].given.length, 2);
|
||||||
|
TestCase.assertEqual(objects[0].name[0].prefix.length, 0);
|
||||||
|
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
||||||
|
TestCase.assertEqual(objects[0].name[0].given[1], 'Jørgen')
|
||||||
|
TestCase.assertEqual(objects[0].name[1].given.length, 1);
|
||||||
|
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
||||||
|
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
||||||
|
|
||||||
|
TestCase.assertEqual(objects[1].name.length, 1);
|
||||||
|
TestCase.assertEqual(objects[1].name[0].given.length, 2);
|
||||||
|
TestCase.assertEqual(objects[1].name[0].prefix.length, 0);
|
||||||
|
TestCase.assertEqual(objects[1].name[0].given[0], 'Gurli');
|
||||||
|
TestCase.assertEqual(objects[1].name[0].given[1], 'Margrete');
|
||||||
|
},
|
||||||
|
|
||||||
|
testListNestedFromJSON: function() {
|
||||||
|
let json = '{"id":1, "name": [{ "family": "Larsen", "given": ["Hans", "Jørgen"], "prefix": [] }, { "family": "Hansen", "given": ["Ib"], "prefix": [] }] }';
|
||||||
|
let parent = JSON.parse(json);
|
||||||
|
const realm = new Realm({schema: [schemas.ParentObject, schemas.NameObject]});
|
||||||
|
realm.write(() => {
|
||||||
|
realm.create('ParentObject', parent);
|
||||||
|
});
|
||||||
|
|
||||||
|
let objects = realm.objects('ParentObject');
|
||||||
|
TestCase.assertEqual(objects.length, 1);
|
||||||
|
TestCase.assertEqual(objects[0].name.length, 2);
|
||||||
|
TestCase.assertEqual(objects[0].name[0].given.length, 2);
|
||||||
|
TestCase.assertEqual(objects[0].name[0].prefix.length, 0);
|
||||||
|
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
||||||
|
TestCase.assertEqual(objects[0].name[0].given[1], 'Jørgen');
|
||||||
|
|
||||||
|
TestCase.assertEqual(objects[0].name[1].given.length, 1);
|
||||||
|
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
||||||
|
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
||||||
|
},
|
||||||
|
|
||||||
|
testMultipleLists: function() {
|
||||||
|
const realm = new Realm({schema: [schemas.MultiListObject]});
|
||||||
|
realm.write(() => {
|
||||||
|
realm.create('MultiListObject', { id: 0, list1: ["Hello"], list2: ["World"] });
|
||||||
|
realm.create('MultiListObject', { id: 1, list1: ["Foo"], list2: ["Bar"] });
|
||||||
|
});
|
||||||
|
|
||||||
|
let objects = realm.objects('MultiListObject');
|
||||||
|
TestCase.assertEqual(objects.length, 2);
|
||||||
|
TestCase.assertEqual(objects[0].id, 0);
|
||||||
|
TestCase.assertEqual(objects[0].list1.length, 1);
|
||||||
|
TestCase.assertEqual(objects[0].list1[0], "Hello");
|
||||||
|
TestCase.assertEqual(objects[0].list2.length, 1);
|
||||||
|
TestCase.assertEqual(objects[0].list2[0], "World");
|
||||||
|
|
||||||
|
TestCase.assertEqual(objects[1].id, 1);
|
||||||
|
TestCase.assertEqual(objects[1].list1.length, 1);
|
||||||
|
TestCase.assertEqual(objects[1].list1[0], "Foo");
|
||||||
|
TestCase.assertEqual(objects[1].list2.length, 1);
|
||||||
|
TestCase.assertEqual(objects[1].list2[0], "Bar");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
67
tests/js/nested-list-helper.js
Normal file
67
tests/js/nested-list-helper.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
This script creates new nested objects into a new Realm.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
console.log("nested-list-helper started");
|
||||||
|
const username = process.argv[3];
|
||||||
|
const realmName = process.argv[4];
|
||||||
|
const realmModule = process.argv[5];
|
||||||
|
|
||||||
|
const Realm = require(realmModule);
|
||||||
|
let schemas = require(process.argv[2]);
|
||||||
|
|
||||||
|
function createObjects(user) {
|
||||||
|
const config = {
|
||||||
|
sync: { user,
|
||||||
|
url: `realm://localhost:9080/~/${realmName}`,
|
||||||
|
error: err => console.log(err)
|
||||||
|
},
|
||||||
|
schema: [schemas.ParentObject, schemas.NameObject],
|
||||||
|
};
|
||||||
|
|
||||||
|
const realm = new Realm(config);
|
||||||
|
|
||||||
|
realm.write(() => {
|
||||||
|
realm.create('ParentObject', {
|
||||||
|
id: 1,
|
||||||
|
name: [
|
||||||
|
{ family: 'Larsen', given: ['Hans', 'Jørgen'], prefix: [] },
|
||||||
|
{ family: 'Hansen', given: ['Ib'], prefix: [] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
realm.create('ParentObject', {
|
||||||
|
id: 2,
|
||||||
|
name: [
|
||||||
|
{family: 'Petersen', given: ['Gurli', 'Margrete'], prefix: [] }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("JSON: " + JSON.stringify(realm.objects('ParentObject')));
|
||||||
|
|
||||||
|
let session = realm.syncSession;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let callback = (transferred, total) => {
|
||||||
|
if (transferred === total) {
|
||||||
|
session.removeProgressNotification(callback);
|
||||||
|
resolve(realm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.addProgressNotification('upload', 'forCurrentlyOutstandingWork', callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let registrationError;
|
||||||
|
Realm.Sync.User.register('http://localhost:9080', username, 'password')
|
||||||
|
.catch((error) => {
|
||||||
|
registrationError = JSON.stringify(error);
|
||||||
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password')
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const loginError = JSON.stringify(error);
|
||||||
|
console.error(`nested-list-helper failed:\n User.register() error:\n${registrationError}\n User.login() error:\n${registrationError}`);
|
||||||
|
process.exit(-2);
|
||||||
|
})
|
||||||
|
.then((user) => createObjects(user))
|
||||||
|
.then(() => process.exit(0));
|
@ -280,4 +280,30 @@ exports.LinkingObjectsObject = {
|
|||||||
links: 'LinkingObjectsObject[]',
|
links: 'LinkingObjectsObject[]',
|
||||||
linkingObjects: {type: 'linkingObjects', objectType: 'LinkingObjectsObject', property: 'links'}
|
linkingObjects: {type: 'linkingObjects', objectType: 'LinkingObjectsObject', property: 'links'}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
exports.ParentObject = {
|
||||||
|
name: 'ParentObject',
|
||||||
|
properties: {
|
||||||
|
id: 'int',
|
||||||
|
name: 'NameObject[]'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.NameObject = {
|
||||||
|
name: 'NameObject',
|
||||||
|
properties: {
|
||||||
|
family: 'string',
|
||||||
|
given: 'string[]',
|
||||||
|
prefix: 'string[]'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.MultiListObject = {
|
||||||
|
name: 'MultiListObject',
|
||||||
|
properties: {
|
||||||
|
'id': 'int',
|
||||||
|
'list1': 'string[]',
|
||||||
|
'list2': 'string[]'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
const Realm = require('realm');
|
const Realm = require('realm');
|
||||||
const TestCase = require('./asserts');
|
const TestCase = require('./asserts');
|
||||||
|
let schemas = require('./schemas');
|
||||||
|
|
||||||
const isNodeProccess = (typeof process === 'object' && process + '' === '[object process]');
|
const isNodeProccess = (typeof process === 'object' && process + '' === '[object process]');
|
||||||
|
|
||||||
@ -402,6 +403,49 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
testListNestedSync() {
|
||||||
|
if (!isNodeProccess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const username = uuid();
|
||||||
|
const realmName = uuid();
|
||||||
|
|
||||||
|
return runOutOfProcess(__dirname + '/nested-list-helper.js', __dirname + '/schemas.js', username, realmName, REALM_MODULE_PATH)
|
||||||
|
.then(() => {
|
||||||
|
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let config = {
|
||||||
|
schema: [schemas.ParentObject, schemas.NameObject],
|
||||||
|
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
||||||
|
};
|
||||||
|
Realm.open(config).then(realm => {
|
||||||
|
let objects = realm.objects('ParentObject');
|
||||||
|
|
||||||
|
let json = JSON.stringify(objects);
|
||||||
|
TestCase.assertEqual(json, '{"0":{"id":1,"name":{"0":{"family":"Larsen","given":{"0":"Hans","1":"Jørgen"},"prefix":{}},"1":{"family":"Hansen","given":{"0":"Ib"},"prefix":{}}}},"1":{"id":2,"name":{"0":{"family":"Petersen","given":{"0":"Gurli","1":"Margrete"},"prefix":{}}}}}');
|
||||||
|
TestCase.assertEqual(objects.length, 2);
|
||||||
|
TestCase.assertEqual(objects[0].name.length, 2);
|
||||||
|
TestCase.assertEqual(objects[0].name[0].given.length, 2);
|
||||||
|
TestCase.assertEqual(objects[0].name[0].prefix.length, 0);
|
||||||
|
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
||||||
|
TestCase.assertEqual(objects[0].name[0].given[1], 'Jørgen')
|
||||||
|
TestCase.assertEqual(objects[0].name[1].given.length, 1);
|
||||||
|
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
||||||
|
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
||||||
|
|
||||||
|
TestCase.assertEqual(objects[1].name.length, 1);
|
||||||
|
TestCase.assertEqual(objects[1].name[0].given.length, 2);
|
||||||
|
TestCase.assertEqual(objects[1].name[0].prefix.length, 0);
|
||||||
|
TestCase.assertEqual(objects[1].name[0].given[0], 'Gurli');
|
||||||
|
TestCase.assertEqual(objects[1].name[0].given[1], 'Margrete');
|
||||||
|
resolve();
|
||||||
|
}).catch(() => reject());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
testIncompatibleSyncedRealmOpen() {
|
testIncompatibleSyncedRealmOpen() {
|
||||||
let realm = "sync-v1.realm";
|
let realm = "sync-v1.realm";
|
||||||
if (isNodeProccess) {
|
if (isNodeProccess) {
|
||||||
@ -523,7 +567,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
testProgressNotificationsForRealmConstructor() {
|
/* testProgressNotificationsForRealmConstructor() {
|
||||||
if (!isNodeProccess) {
|
if (!isNodeProccess) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -555,7 +599,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},*/
|
||||||
|
|
||||||
testProgressNotificationsUnregisterForRealmConstructor() {
|
testProgressNotificationsUnregisterForRealmConstructor() {
|
||||||
if (!isNodeProccess) {
|
if (!isNodeProccess) {
|
||||||
@ -707,8 +751,7 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Disabled: waiting for new implementation
|
testPartialSyncAnonymous_SubscriptionListener() {
|
||||||
testPartialSync() {
|
|
||||||
// FIXME: try to enable for React Native
|
// FIXME: try to enable for React Native
|
||||||
if (!isNodeProccess) {
|
if (!isNodeProccess) {
|
||||||
return;
|
return;
|
||||||
@ -733,14 +776,205 @@ module.exports = {
|
|||||||
Realm.deleteFile(config);
|
Realm.deleteFile(config);
|
||||||
const realm = new Realm(config);
|
const realm = new Realm(config);
|
||||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||||
return realm.subscribeToObjects("Dog", "name == 'Lassy 1'").then(results => {
|
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||||
TestCase.assertEqual(results.length, 1);
|
var subscription = results.subscribe();
|
||||||
TestCase.assertTrue(results[0].name === 'Lassy 1', "The object is not synced correctly");
|
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() {
|
testClientReset() {
|
||||||
// FIXME: try to enable for React Native
|
// FIXME: try to enable for React Native
|
||||||
if (!isNodeProccess) {
|
if (!isNodeProccess) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user