commit
0efc4fbedb
93
CHANGELOG.md
93
CHANGELOG.md
|
@ -1,3 +1,43 @@
|
||||||
|
2.3.0 Release notes (2018-3-13)
|
||||||
|
=============================================================
|
||||||
|
### Breaking changes
|
||||||
|
* [Sync] Sync protocol changed to version 24.
|
||||||
|
* [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.
|
||||||
|
* [Sync] `Realm.subscribeToObjects()` has been removed. Use `Realm.Results.subscribe()` instead.
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
* [Sync] Reduced initial download times in Realms with long transaction histories.
|
||||||
|
* [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.
|
||||||
|
- 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.
|
||||||
|
* Added support for queries over named backlinks (#1498/#1660).
|
||||||
|
- Example syntax: `parents.age > 25` and `parents.@count == 2`.
|
||||||
|
* [Sync] Added `Realm.Results.subscribe()` to subscribe to partial synced Realms.
|
||||||
|
* [Sync] Added class `Realm.Sync.Subscription` and enum `Realm.Sync.SubscriptionState` to support partial synced Realms.
|
||||||
|
* [Sync] Added an object-level permission subsystem. It is possible to grant fine-grained priviliges to users.
|
||||||
|
* Added object-level permissions:
|
||||||
|
- Schemas `Realm.Permissions.Realm`, `Realm.Permissions.Class`, `Realm.Permissions.Role`, `Realm.Permissions.User`, and `Realm.Permissions.Permission` to support working with permissions. These schemas can be used in user-defined Realms and schemas.
|
||||||
|
- Permissions are enforced by the object server but connectivity is not required.
|
||||||
|
- Method `Realm.privilges()` to compute privileges on a Realm, a Realm object schema, or a Realm object. The method returns either a `Realm.Permissions.Realm` or `Realm.Permissions.Class` object.
|
||||||
|
- For non-synced Realms, all privileges are always granted.
|
||||||
|
- For more details, please read the reference documentation.
|
||||||
|
* [Sync] Revoke refresh token upon logout (#1354).
|
||||||
|
* Added `Realm.automaticSyncConfiguration()` which will return the configuration for a default synced Realm (#1688).
|
||||||
|
* [Sync] Deprecated `Realm.Sync.setFeatureToken` (#1689).
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
* Fixed usage of disk space preallocation which would occasionally fail on recent MacOS running with the APFS filesystem (Realm Core #3005).
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
* Updated to Realm Core 5.4.0.
|
||||||
|
* Updated to Realm Sync 3.0.0.
|
||||||
|
* Tested against Realm Object Server 3.0.0-alpha.8.
|
||||||
|
* Added `_disablePartialSyncUrlChecks` to `Realm.Configuration`.
|
||||||
|
|
||||||
2.2.15 Release notes (2018-3-9)
|
2.2.15 Release notes (2018-3-9)
|
||||||
=============================================================
|
=============================================================
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
@ -22,8 +62,7 @@
|
||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed race condition in handling of session bootstrapping in client.
|
* [Sync] Fixed race condition in handling of session bootstrapping in client.
|
||||||
|
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* Updated to Realm Sync 2.2.15.
|
* Updated to Realm Sync 2.2.15.
|
||||||
|
@ -38,7 +77,7 @@
|
||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] Fixed handling of SSL certificates for the sync client.
|
* [Sync] Fixed handling of SSL certificates for the sync client.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* Updated to Realm Sync 2.2.14.
|
* Updated to Realm Sync 2.2.14.
|
||||||
|
@ -68,7 +107,7 @@
|
||||||
* None.
|
* None.
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* [Object Server] A use-after-free bug was fixed which could cause arrays of primitives to behave unexpectedly.
|
* [Sync] A use-after-free bug was fixed which could cause arrays of primitives to behave unexpectedly.
|
||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
* Updated to Realm Sync 2.2.12.
|
* Updated to Realm Sync 2.2.12.
|
||||||
|
@ -95,12 +134,12 @@
|
||||||
* 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.
|
||||||
|
|
||||||
### Internaæ
|
### Internal
|
||||||
* Updated to Realm Sync 2.2.10.
|
* Updated to Realm Sync 2.2.10.
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,11 +149,11 @@
|
||||||
* 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.
|
||||||
|
@ -129,7 +168,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.
|
||||||
|
@ -143,7 +182,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).
|
||||||
|
|
||||||
|
@ -159,8 +198,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.
|
||||||
|
@ -174,8 +213,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.
|
||||||
|
@ -189,7 +228,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.
|
||||||
|
@ -204,7 +243,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.
|
||||||
|
@ -217,8 +256,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.
|
||||||
|
@ -239,10 +278,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").
|
||||||
|
|
||||||
|
|
||||||
|
@ -270,11 +309,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)
|
||||||
=============================================================
|
=============================================================
|
||||||
|
@ -300,8 +339,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").
|
||||||
|
@ -340,10 +379,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.2.15
|
VERSION=2.3.0
|
||||||
REALM_CORE_VERSION=5.1.2
|
REALM_CORE_VERSION=5.4.0
|
||||||
REALM_SYNC_VERSION=2.2.15
|
REALM_SYNC_VERSION=3.0.0
|
||||||
REALM_OBJECT_SERVER_VERSION=2.5.1
|
REALM_OBJECT_SERVER_VERSION=3.0.0-alpha.8
|
||||||
|
|
|
@ -134,6 +134,27 @@ 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);
|
||||||
|
* let subscription = wines.subscribe();
|
||||||
|
* wines.addListener((collection, changes) => {
|
||||||
|
* if (subscription.state === Realm.Sync.SubscriptionState.Complete) {
|
||||||
|
* // 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 +421,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) {}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
* They are created exclusively by the client and are processed by the server
|
* They are created exclusively by the client and are processed by the server
|
||||||
* as indicated by the status fields.
|
* as indicated by the status fields.
|
||||||
* PermissionChange objects allow to grant and revoke permissions by setting
|
* PermissionChange objects allow to grant and revoke permissions by setting
|
||||||
* mayRead, mayWrite and mayManage accordingly.
|
* mayRead, mayWrite and mayManage accordingly.
|
||||||
* If any of these flags are not set, these are merged
|
* If any of these flags are not set, these are merged
|
||||||
* with either the existing or default permissions as applicable. As a
|
* with either the existing or default permissions as applicable. As a
|
||||||
* side-effect this causes that the default permissions are permanently
|
* side-effect this causes that the default permissions are permanently
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
* ErrorCode will be updated accordingly.
|
* ErrorCode will be updated accordingly.
|
||||||
*/
|
*/
|
||||||
class PermissionChange {
|
class PermissionChange {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the unique identifier of this object in the Management realm.
|
* Gets the unique identifier of this object in the Management realm.
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -229,3 +229,161 @@ class PermissionOfferResponse {
|
||||||
*/
|
*/
|
||||||
get realmUrl() {}
|
get realmUrl() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A permission which can be applied to a Realm, Class, or specific Object.
|
||||||
|
* Permissions are applied by adding the permission to the Realm.Permission singleton
|
||||||
|
* object, the RealmClass.Permission object for the desired class, or to a user-defined
|
||||||
|
* Realm.List<Realm.Permission> property on a specific Object instance. The meaning of each of
|
||||||
|
* the properties of Permission depend on what the permission is applied to, and so are
|
||||||
|
* left undocumented here.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
class Permission {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Role which this Permission applies to. All users within the Role are
|
||||||
|
* granted the permissions specified by the fields below any
|
||||||
|
* objects/classes/realms which use this Permission.
|
||||||
|
*
|
||||||
|
* This property cannot be modified once set.
|
||||||
|
* @type {Role}
|
||||||
|
*/
|
||||||
|
get role() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user can read the object to which this Permission is attached.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get canRead() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user can modify the object to which this Permission is attached.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get canUpdate() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user can delete the object to which this Permission is attached.
|
||||||
|
*
|
||||||
|
* This property is only applicable to Permissions attached to Objects, and not
|
||||||
|
* to Realms or Classes.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get canDelete() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user can add or modify Permissions for the object which this
|
||||||
|
* Permission is attached to.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get canSetPermissions() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user can subscribe to queries for this object type.
|
||||||
|
*
|
||||||
|
* This property is only applicable to Permissions attached to Classes, and not
|
||||||
|
* to Realms or Objects.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get canQuery() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user can create new objects of the type this Permission is attached to.
|
||||||
|
*
|
||||||
|
* This property is only applicable to Permissions attached to Classes, and not
|
||||||
|
* to Realms or Objects.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get canCreate() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user can modify the schema of the Realm which this
|
||||||
|
* Permission is attached to.
|
||||||
|
*
|
||||||
|
* This property is only applicable to Permissions attached to Realms, and not
|
||||||
|
* to Realms or Objects.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get canModifySchema() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a sync user within the permissions system.
|
||||||
|
*
|
||||||
|
* User objects are created automatically for each sync user which connects to
|
||||||
|
* a Realm, and can also be created manually if you wish to grant permissions to a user
|
||||||
|
* which has not yet connected to this Realm.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
class User {
|
||||||
|
/**
|
||||||
|
* The unique Realm Object Server user ID string identifying this user. This will have
|
||||||
|
* the same value as Realm.Sync.User.identity.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
get id() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Role within the permissions system.
|
||||||
|
*
|
||||||
|
* A Role consists of a name for the role and a list of users which are members of the role.
|
||||||
|
* Roles are granted privileges on Realms, Classes and Objects, and in turn grant those
|
||||||
|
* privileges to all users which are members of the role.
|
||||||
|
* A role named "everyone" is automatically created in new Realms, and all new users which
|
||||||
|
* connect to the Realm are automatically added to it. Any other roles you wish to use are
|
||||||
|
* managed as normal Realm objects.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
class Role {
|
||||||
|
/**
|
||||||
|
* The name of the Role.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
get name() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The users which belong to the role.
|
||||||
|
* @type {Array<Realm.Sync.Permissions.User>}
|
||||||
|
*/
|
||||||
|
get members() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object which describes class-wide permissions.
|
||||||
|
*
|
||||||
|
* An instance of this object is automatically created in the Realm for class in your schema,
|
||||||
|
* and should not be created manually.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
class Class {
|
||||||
|
/**
|
||||||
|
* The name of the class which these permissions apply to.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
get class_name() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The permissions for this class.
|
||||||
|
* @type {Array<Realm.Sync.Permissions.Permission>}
|
||||||
|
*/
|
||||||
|
get permissions() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A singleton object which describes Realm-wide permissions.
|
||||||
|
*
|
||||||
|
* An object of this type is automatically created in the Realm for you, and more objects
|
||||||
|
* cannot be created manually.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
class Realm {
|
||||||
|
/**
|
||||||
|
* The permissions for the Realm.
|
||||||
|
* @type {Array<Realm.Sync.Permission>}
|
||||||
|
*/
|
||||||
|
get permissions() {}
|
||||||
|
}
|
||||||
|
|
|
@ -119,12 +119,42 @@ class Realm {
|
||||||
*/
|
*/
|
||||||
static openAsync(config, callback, progressCallback) {}
|
static openAsync(config, callback, progressCallback) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a configuration for a default synced Realm. The server URL for the current user will be used as base for
|
||||||
|
* the URL for the synced Realm.
|
||||||
|
* @throws {Error} if zero or multiple users are logged in
|
||||||
|
* @returns {Realm~Configuration} - a configuration matching a default synced Realm.
|
||||||
|
* @since 2.3.0
|
||||||
|
*/
|
||||||
|
static automaticSyncConfiguration() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes this Realm so it may be re-opened with a newer schema version.
|
* Closes this Realm so it may be re-opened with a newer schema version.
|
||||||
* All objects and collections from this Realm are no longer valid after calling this method.
|
* All objects and collections from this Realm are no longer valid after calling this method.
|
||||||
*/
|
*/
|
||||||
close() {}
|
close() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the granted privilges.
|
||||||
|
*
|
||||||
|
* This combines all privileges granted on the Realm/Class/Object by all Roles which
|
||||||
|
* the current User is a member of into the final privileges which will
|
||||||
|
* be enforced by the server.
|
||||||
|
*
|
||||||
|
* The privilege calculation is done locally using cached data, and inherently may
|
||||||
|
* be stale. It is possible that this method may indicate that an operation is
|
||||||
|
* permitted but the server will still reject it if permission is revoked before
|
||||||
|
* the changes have been integrated on the server.
|
||||||
|
*
|
||||||
|
* Non-synchronized Realms always have permission to perform all operations.
|
||||||
|
*
|
||||||
|
* @param {(Realm~ObjectType|Realm.Object)} arg - the object type or the object to compute priviliges from
|
||||||
|
* @returns {Object} as the computed priviliges as properties
|
||||||
|
* @since 2.3.0
|
||||||
|
* @see {Realm.Permissions} for details of priviliges and roles.
|
||||||
|
*/
|
||||||
|
privileges(arg) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Realm object of the given type and with the specified properties.
|
* Create a new Realm object of the given type and with the specified properties.
|
||||||
* @param {Realm~ObjectType} type - The type of Realm object to create.
|
* @param {Realm~ObjectType} type - The type of Realm object to create.
|
||||||
|
@ -241,17 +271,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(subscription, state)} callback - A function to be called when changes to the subscription 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(subscription, state)} 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',
|
||||||
|
|
|
@ -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'
|
||||||
|
]);
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const URL = require('url-parse');
|
||||||
|
|
||||||
let getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || function(obj) {
|
let getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || function(obj) {
|
||||||
return Object.getOwnPropertyNames(obj).reduce(function (descriptors, name) {
|
return Object.getOwnPropertyNames(obj).reduce(function (descriptors, name) {
|
||||||
descriptors[name] = Object.getOwnPropertyDescriptor(obj, name);
|
descriptors[name] = Object.getOwnPropertyDescriptor(obj, name);
|
||||||
|
@ -149,29 +151,105 @@ module.exports = function(realmConstructor) {
|
||||||
setConstructorOnPrototype(realmConstructor.Sync.User);
|
setConstructorOnPrototype(realmConstructor.Sync.User);
|
||||||
setConstructorOnPrototype(realmConstructor.Sync.Session);
|
setConstructorOnPrototype(realmConstructor.Sync.Session);
|
||||||
|
|
||||||
|
// A configuration for a default Realm
|
||||||
|
realmConstructor.automaticSyncConfiguration = function() {
|
||||||
|
let users = this.Sync.User.all;
|
||||||
|
let identities = Object.keys(users);
|
||||||
|
if (identities.length === 1) {
|
||||||
|
let user = users[identities[0]];
|
||||||
|
let url = new URL(user.server);
|
||||||
|
let secure = (url.protocol === 'https:')?'s':'';
|
||||||
|
let port = (url.port === undefined)?'9080':url.port
|
||||||
|
let realmUrl = `realm${secure}://${url.hostname}:${port}/~/default`;
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
sync: {
|
||||||
|
user,
|
||||||
|
url: realmUrl
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
new Error(`One and only one user should be logged in but found ${users.length} users.`);
|
||||||
|
}
|
||||||
|
|
||||||
if (realmConstructor.Sync._setFeatureToken) {
|
if (realmConstructor.Sync._setFeatureToken) {
|
||||||
realmConstructor.Sync.setFeatureToken = function(featureToken) {
|
realmConstructor.Sync.setFeatureToken = function(featureToken) {
|
||||||
if (typeof featureToken !== 'string' && !(featureToken instanceof String)) {
|
console.log('Realm.Sync.setFeatureToken() is deprecated and you can remove any calls to it.');
|
||||||
throw new Error("featureToken should be a string");
|
|
||||||
}
|
|
||||||
|
|
||||||
realmConstructor.Sync._setFeatureToken(featureToken.trim());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Define the permission schemas as constructors so that they can be
|
||||||
|
// passed into directly to functions which want object type names
|
||||||
|
const permissionsSchema = Object.freeze({
|
||||||
|
Class: function() {},
|
||||||
|
Permission: function() {},
|
||||||
|
Realm: function() {},
|
||||||
|
Role: function() {},
|
||||||
|
User: function() {},
|
||||||
|
});
|
||||||
|
permissionsSchema.Permission.schema = Object.freeze({
|
||||||
|
name: '__Permission',
|
||||||
|
properties: {
|
||||||
|
role: '__Role',
|
||||||
|
canRead: {type: 'bool', default: false},
|
||||||
|
canUpdate: {type: 'bool', default: false},
|
||||||
|
canDelete: {type: 'bool', default: false},
|
||||||
|
canSetPermissions: {type: 'bool', default: false},
|
||||||
|
canQuery: {type: 'bool', default: false},
|
||||||
|
canCreate: {type: 'bool', default: false},
|
||||||
|
canModifySchema: {type: 'bool', default: false},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
permissionsSchema.User.schema = Object.freeze({
|
||||||
|
name: '__User',
|
||||||
|
primaryKey: 'id',
|
||||||
|
properties: {
|
||||||
|
id: 'string',
|
||||||
|
role: '__Role'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
permissionsSchema.Role.schema = Object.freeze({
|
||||||
|
name: '__Role',
|
||||||
|
primaryKey: 'name',
|
||||||
|
properties: {
|
||||||
|
name: 'string',
|
||||||
|
members: '__User[]'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
permissionsSchema.Class.schema = Object.freeze({
|
||||||
|
name: '__Class',
|
||||||
|
primaryKey: 'class_name',
|
||||||
|
properties: {
|
||||||
|
class_name: 'string',
|
||||||
|
permissions: '__Permission[]'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
permissionsSchema.Realm.schema = Object.freeze({
|
||||||
|
name: '__Realm',
|
||||||
|
primaryKey: 'id',
|
||||||
|
properties: {
|
||||||
|
id: 'int',
|
||||||
|
permissions: '__Permission[]'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(realmConstructor, 'Permissions', {
|
||||||
|
value: permissionsSchema,
|
||||||
|
configurable: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this now useless object.
|
// TODO: Remove this now useless object.
|
||||||
|
|
|
@ -81,7 +81,7 @@ declare namespace Realm {
|
||||||
path?: string;
|
path?: string;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
inMemory?: boolean;
|
inMemory?: boolean;
|
||||||
schema?: ObjectClass[] | ObjectSchema[];
|
schema?: (ObjectClass | ObjectSchema)[];
|
||||||
schemaVersion?: number;
|
schemaVersion?: number;
|
||||||
sync?: Realm.Sync.SyncConfiguration;
|
sync?: Realm.Sync.SyncConfiguration;
|
||||||
deleteRealmIfMigrationNeeded?: boolean;
|
deleteRealmIfMigrationNeeded?: boolean;
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -374,6 +379,7 @@ declare namespace Realm.Sync {
|
||||||
open_ssl_verify_callback?: SSLVerifyCallback;
|
open_ssl_verify_callback?: SSLVerifyCallback;
|
||||||
error?: ErrorCallback;
|
error?: ErrorCallback;
|
||||||
partial?: boolean;
|
partial?: boolean;
|
||||||
|
_disablePartialSyncUrlChecks?:boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProgressNotificationCallback = (transferred: number, transferable: number) => void;
|
type ProgressNotificationCallback = (transferred: number, transferable: number) => void;
|
||||||
|
@ -394,6 +400,29 @@ declare namespace Realm.Sync {
|
||||||
removeProgressNotification(progressCallback: ProgressNotificationCallback): void;
|
removeProgressNotification(progressCallback: ProgressNotificationCallback): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SubscriptionNotificationCallback = (subscription: Subscription, state: number) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscription
|
||||||
|
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.Subscription.html }
|
||||||
|
*/
|
||||||
|
class Subscription {
|
||||||
|
readonly state: SubscriptionState;
|
||||||
|
readonly error: string;
|
||||||
|
|
||||||
|
unsubscribe(): void;
|
||||||
|
addListener(subscruptionCallback: SubscriptionNotificationCallback): void;
|
||||||
|
removeListener(subscruptionCallback: SubscriptionNotificationCallback): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SubscriptionState {
|
||||||
|
Error,
|
||||||
|
Creating,
|
||||||
|
Pending,
|
||||||
|
Complete,
|
||||||
|
Invalidated,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 }
|
||||||
|
@ -420,6 +449,10 @@ declare namespace Realm.Sync {
|
||||||
function removeListener(regex: string, name: string, changeCallback: (changeEvent: ChangeEvent) => void): Promise<void>;
|
function removeListener(regex: string, name: string, changeCallback: (changeEvent: ChangeEvent) => void): Promise<void>;
|
||||||
function setLogLevel(logLevel: 'all' | 'trace' | 'debug' | 'detail' | 'info' | 'warn' | 'error' | 'fatal' | 'off'): void;
|
function setLogLevel(logLevel: 'all' | 'trace' | 'debug' | 'detail' | 'info' | 'warn' | 'error' | 'fatal' | 'off'): void;
|
||||||
function initiateClientReset(path: string): void;
|
function initiateClientReset(path: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated, to be removed in future versions
|
||||||
|
*/
|
||||||
function setFeatureToken(token: string): void;
|
function setFeatureToken(token: string): void;
|
||||||
|
|
||||||
type Instruction = {
|
type Instruction = {
|
||||||
|
@ -455,6 +488,42 @@ declare namespace Realm.Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare namespace Realm.Permissions {
|
||||||
|
class Permission {
|
||||||
|
static schema: ObjectSchema;
|
||||||
|
|
||||||
|
identity: string;
|
||||||
|
canRead: boolean;
|
||||||
|
canUpdate: boolean;
|
||||||
|
canDelete: boolean;
|
||||||
|
canSetPermissions: boolean;
|
||||||
|
canQuery: boolean;
|
||||||
|
canCreate: boolean;
|
||||||
|
canModifySchema: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class User {
|
||||||
|
static schema: ObjectSchema;
|
||||||
|
identity: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Role {
|
||||||
|
static schema: ObjectSchema;
|
||||||
|
name: string;
|
||||||
|
members: User[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class Class {
|
||||||
|
static schema: ObjectSchema;
|
||||||
|
class_name: string;
|
||||||
|
permissions: Permission[];
|
||||||
|
}
|
||||||
|
|
||||||
|
class Realm {
|
||||||
|
static schema: ObjectSchema;
|
||||||
|
permissions: Permission[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface ProgressPromise extends Promise<Realm> {
|
interface ProgressPromise extends Promise<Realm> {
|
||||||
progress(callback: Realm.Sync.ProgressNotificationCallback): Promise<Realm>
|
progress(callback: Realm.Sync.ProgressNotificationCallback): Promise<Realm>
|
||||||
|
@ -497,11 +566,16 @@ declare class Realm {
|
||||||
*/
|
*/
|
||||||
static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void, progressCallback?: Realm.Sync.ProgressNotificationCallback): void
|
static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void, progressCallback?: Realm.Sync.ProgressNotificationCallback): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a configuration for a default Realm.
|
||||||
|
*/
|
||||||
|
static automaticSyncConfiguration(): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the Realm file for the given configuration.
|
* Delete the Realm file for the given configuration.
|
||||||
* @param {Configuration} config
|
* @param {Configuration} config
|
||||||
*/
|
*/
|
||||||
static deleteFile(config: Realm.Configuration): void
|
static deleteFile(config: Realm.Configuration): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Realm.Configuration} config?
|
* @param {Realm.Configuration} config?
|
||||||
|
@ -524,7 +598,7 @@ declare class Realm {
|
||||||
* @param {boolean} update?
|
* @param {boolean} update?
|
||||||
* @returns T
|
* @returns T
|
||||||
*/
|
*/
|
||||||
create<T>(type: string | Realm.ObjectClass | Function, properties: T & Realm.ObjectPropsType, update?: boolean): T;
|
create<T>(type: string | Realm.ObjectClass | Function, properties: T | Realm.ObjectPropsType, update?: boolean): T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Realm.Object|Realm.Object[]|Realm.List<any>|Realm.Results<any>|any} object
|
* @param {Realm.Object|Realm.Object[]|Realm.List<any>|Realm.Results<any>|any} object
|
||||||
|
@ -601,10 +675,9 @@ declare class Realm {
|
||||||
*/
|
*/
|
||||||
compact(): boolean;
|
compact(): boolean;
|
||||||
|
|
||||||
/**
|
privileges() : Realm.Permissions.Realm;
|
||||||
* @returns Promise<Results<T>>
|
privileges(objectType: string | Realm.ObjectSchema | Function) : Realm.Permissions.Class;
|
||||||
*/
|
privileges(obj: Realm.Object) : Realm.Permissions.Class;
|
||||||
subscribeToObjects<T>(objectType: string, query: string): Promise<Realm.Results<T>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'realm' {
|
declare module 'realm' {
|
||||||
|
|
|
@ -348,6 +348,27 @@ const staticMethods = {
|
||||||
|
|
||||||
|
|
||||||
const instanceMethods = {
|
const instanceMethods = {
|
||||||
|
logout() {
|
||||||
|
this._logout();
|
||||||
|
const url = url_parse(this.server);
|
||||||
|
url.set('pathname', '/auth/revoke');
|
||||||
|
const headers = {
|
||||||
|
Authorization: this.token
|
||||||
|
};
|
||||||
|
const body = {
|
||||||
|
token: this.token
|
||||||
|
};
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
headers,
|
||||||
|
body: body,
|
||||||
|
open_timeout: 5000
|
||||||
|
};
|
||||||
|
|
||||||
|
performFetch(url.href, options)
|
||||||
|
.then(() => console.log('User is logged out'))
|
||||||
|
.catch((e) => print_error(e));
|
||||||
|
},
|
||||||
openManagementRealm() {
|
openManagementRealm() {
|
||||||
let url = url_parse(this.server);
|
let url = url_parse(this.server);
|
||||||
if (url.protocol === 'http:') {
|
if (url.protocol === 'http:') {
|
||||||
|
|
|
@ -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.2.15",
|
"version": "2.3.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"homepage": "https://realm.io",
|
"homepage": "https://realm.io",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
"request": "^2.78.0",
|
"request": "^2.78.0",
|
||||||
"stream-counter": "^1.0.0",
|
"stream-counter": "^1.0.0",
|
||||||
"sync-request": "^3.0.1",
|
"sync-request": "^3.0.1",
|
||||||
"url-parse": "^1.1.7"
|
"url-parse": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^4.0.35",
|
"@types/node": "^4.0.35",
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -961,7 +969,7 @@
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 2.2.15;
|
CURRENT_PROJECT_VERSION = 2.3.0;
|
||||||
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
|
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
@ -1025,7 +1033,7 @@
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 2.2.15;
|
CURRENT_PROJECT_VERSION = 2.3.0;
|
||||||
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
|
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
156
src/js_realm.hpp
156
src/js_realm.hpp
|
@ -35,7 +35,6 @@
|
||||||
#include "js_sync.hpp"
|
#include "js_sync.hpp"
|
||||||
#include "sync/sync_config.hpp"
|
#include "sync/sync_config.hpp"
|
||||||
#include "sync/sync_manager.hpp"
|
#include "sync/sync_manager.hpp"
|
||||||
#include "sync/partial_sync.hpp"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "shared_realm.hpp"
|
#include "shared_realm.hpp"
|
||||||
|
@ -186,9 +185,7 @@ 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 privileges(ContextType, ObjectType, Arguments, ReturnValue&);
|
||||||
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 &);
|
||||||
|
@ -245,10 +242,10 @@ public:
|
||||||
{"close", wrap<close>},
|
{"close", wrap<close>},
|
||||||
{"compact", wrap<compact>},
|
{"compact", wrap<compact>},
|
||||||
{"deleteModel", wrap<delete_model>},
|
{"deleteModel", wrap<delete_model>},
|
||||||
|
{"privileges", wrap<privileges>},
|
||||||
{"_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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -295,7 +292,8 @@ public:
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const ObjectSchema& validated_object_schema_for_value(ContextType ctx, const SharedRealm &realm, const ValueType &value, std::string& object_type) {
|
static const ObjectSchema& validated_object_schema_for_value(ContextType ctx, const SharedRealm &realm, const ValueType &value) {
|
||||||
|
std::string object_type;
|
||||||
if (Value::is_constructor(ctx, value)) {
|
if (Value::is_constructor(ctx, value)) {
|
||||||
FunctionType constructor = Value::to_constructor(ctx, value);
|
FunctionType constructor = Value::to_constructor(ctx, value);
|
||||||
|
|
||||||
|
@ -545,7 +543,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Config config, bool schema_updated,
|
SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Config config, bool schema_updated,
|
||||||
ObjectDefaultsMap && defaults, ConstructorMap && constructors) {
|
ObjectDefaultsMap&& defaults, ConstructorMap&& constructors) {
|
||||||
config.execution_context = Context<T>::get_execution_context_id(ctx);
|
config.execution_context = Context<T>::get_execution_context_id(ctx);
|
||||||
|
|
||||||
SharedRealm realm;
|
SharedRealm realm;
|
||||||
|
@ -555,9 +553,6 @@ SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Co
|
||||||
catch (const RealmFileException& ex) {
|
catch (const RealmFileException& ex) {
|
||||||
handleRealmFileException(ctx, config, ex);
|
handleRealmFileException(ctx, config, ex);
|
||||||
}
|
}
|
||||||
catch (...) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalContextType global_context = Context<T>::get_global_context(ctx);
|
GlobalContextType global_context = Context<T>::get_global_context(ctx);
|
||||||
if (!realm->m_binding_context) {
|
if (!realm->m_binding_context) {
|
||||||
|
@ -794,10 +789,8 @@ void RealmClass<T>::objects(ContextType ctx, ObjectType this_object, Arguments a
|
||||||
args.validate_maximum(1);
|
args.validate_maximum(1);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
std::string object_type;
|
auto& object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||||
validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
return_value.set(ResultsClass<T>::create_instance(ctx, realm, object_schema.name));
|
||||||
|
|
||||||
return_value.set(ResultsClass<T>::create_instance(ctx, realm, object_type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -806,7 +799,7 @@ void RealmClass<T>::object_for_primary_key(ContextType ctx, ObjectType this_obje
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
std::string object_type;
|
std::string object_type;
|
||||||
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||||
NativeAccessor accessor(ctx, realm, object_schema);
|
NativeAccessor accessor(ctx, realm, object_schema);
|
||||||
auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, args[1]);
|
auto realm_object = realm::Object::get_for_primary_key(accessor, realm, object_schema, args[1]);
|
||||||
|
|
||||||
|
@ -824,8 +817,7 @@ void RealmClass<T>::create(ContextType ctx, ObjectType this_object, Arguments ar
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
realm->verify_open();
|
realm->verify_open();
|
||||||
std::string object_type;
|
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||||
auto &object_schema = validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
|
||||||
|
|
||||||
ObjectType object = Value::validated_to_object(ctx, args[1], "properties");
|
ObjectType object = Value::validated_to_object(ctx, args[1], "properties");
|
||||||
if (Value::is_array(ctx, args[1])) {
|
if (Value::is_array(ctx, args[1])) {
|
||||||
|
@ -902,7 +894,13 @@ void RealmClass<T>::delete_all(ContextType ctx, ObjectType this_object, Argument
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto objectSchema : realm->schema()) {
|
for (auto objectSchema : realm->schema()) {
|
||||||
ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name)->clear();
|
auto table = ObjectStore::table_for_object_type(realm->read_group(), objectSchema.name);
|
||||||
|
if (realm->is_partial()) {
|
||||||
|
realm::Results(realm, *table).clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
table->clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1006,36 +1004,6 @@ void RealmClass<T>::compact(ContextType ctx, ObjectType this_object, Arguments a
|
||||||
return_value.set(realm->compact());
|
return_value.set(realm->compact());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if REALM_ENABLE_SYNC
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// FIXME: Sync should provide this: https://github.com/realm/realm-sync/issues/1796
|
|
||||||
inline sync::ObjectID object_id_from_string(std::string const& string)
|
|
||||||
{
|
|
||||||
if (string.front() != '{' || string.back() != '}')
|
|
||||||
throw std::invalid_argument("Invalid object ID.");
|
|
||||||
|
|
||||||
size_t dash_index = string.find('-');
|
|
||||||
if (dash_index == std::string::npos)
|
|
||||||
throw std::invalid_argument("Invalid object ID.");
|
|
||||||
|
|
||||||
std::string high_string = string.substr(1, dash_index - 1);
|
|
||||||
std::string low_string = string.substr(dash_index + 1, string.size() - dash_index - 2);
|
|
||||||
|
|
||||||
if (high_string.size() == 0 || high_string.size() > 16 || low_string.size() == 0 || low_string.size() > 16)
|
|
||||||
throw std::invalid_argument("Invalid object ID.");
|
|
||||||
|
|
||||||
auto isxdigit = static_cast<int(*)(int)>(std::isxdigit);
|
|
||||||
if (!std::all_of(high_string.begin(), high_string.end(), isxdigit) ||
|
|
||||||
!std::all_of(low_string.begin(), low_string.end(), isxdigit)) {
|
|
||||||
throw std::invalid_argument("Invalid object ID.");
|
|
||||||
}
|
|
||||||
return sync::ObjectID(strtoull(high_string.c_str(), nullptr, 16), strtoull(low_string.c_str(), nullptr, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // unnamed namespace
|
|
||||||
#endif // REALM_ENABLE_SYNC
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::object_for_object_id(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue& return_value) {
|
void RealmClass<T>::object_for_object_id(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue& return_value) {
|
||||||
args.validate_count(2);
|
args.validate_count(2);
|
||||||
|
@ -1045,67 +1013,67 @@ void RealmClass<T>::object_for_object_id(ContextType ctx, ObjectType this_object
|
||||||
if (!sync::has_object_ids(realm->read_group()))
|
if (!sync::has_object_ids(realm->read_group()))
|
||||||
throw std::logic_error("Realm._objectForObjectId() can only be used with synced Realms.");
|
throw std::logic_error("Realm._objectForObjectId() can only be used with synced Realms.");
|
||||||
|
|
||||||
std::string object_type = Value::validated_to_string(ctx, args[0]);
|
auto& object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||||
validated_object_schema_for_value(ctx, realm, args[0], object_type);
|
|
||||||
|
|
||||||
std::string object_id_string = Value::validated_to_string(ctx, args[1]);
|
std::string object_id_string = Value::validated_to_string(ctx, args[1]);
|
||||||
auto object_id = object_id_from_string(object_id_string);
|
auto object_id = sync::ObjectID::from_string(object_id_string);
|
||||||
|
|
||||||
const Group& group = realm->read_group();
|
const Group& group = realm->read_group();
|
||||||
size_t ndx = sync::row_for_object_id(group, *ObjectStore::table_for_object_type(group, object_type), object_id);
|
size_t ndx = sync::row_for_object_id(group, *ObjectStore::table_for_object_type(group, object_schema.name), object_id);
|
||||||
if (ndx != realm::npos) {
|
if (ndx != realm::npos) {
|
||||||
return_value.set(RealmObjectClass<T>::create_instance(ctx, realm::Object(realm, object_type, ndx)));
|
return_value.set(RealmObjectClass<T>::create_instance(ctx, realm::Object(realm, object_schema.name, ndx)));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
throw std::logic_error("Realm._objectForObjectId() can only be used with synced Realms.");
|
throw std::logic_error("Realm._objectForObjectId() can only be used with synced Realms.");
|
||||||
#endif // REALM_ENABLE_SYNC
|
#endif // REALM_ENABLE_SYNC
|
||||||
}
|
}
|
||||||
|
|
||||||
#if REALM_ENABLE_SYNC
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RealmClass<T>::subscribe_to_objects(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
void RealmClass<T>::privileges(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||||
args.validate_count(3);
|
args.validate_maximum(1);
|
||||||
|
|
||||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
using Privilege = realm::ComputedPrivileges;
|
||||||
std::string object_type = Value::validated_to_string(ctx, args[0]);
|
auto has_privilege = [](Privilege actual, Privilege expected) {
|
||||||
std::string query = Value::validated_to_string(ctx, args[1]);
|
return (static_cast<int>(actual) & static_cast<int>(expected)) == static_cast<int>(expected);
|
||||||
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));
|
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||||
|
if (args.count == 0) {
|
||||||
|
auto p = realm->get_privileges();
|
||||||
|
ObjectType object = Object::create_empty(ctx);
|
||||||
|
Object::set_property(ctx, object, "read", Value::from_boolean(ctx, has_privilege(p, Privilege::Read)));
|
||||||
|
Object::set_property(ctx, object, "update", Value::from_boolean(ctx,has_privilege(p, Privilege::Update)));
|
||||||
|
Object::set_property(ctx, object, "modifySchema", Value::from_boolean(ctx, has_privilege(p, Privilege::ModifySchema)));
|
||||||
|
Object::set_property(ctx, object, "setPermissions", Value::from_boolean(ctx, has_privilege(p, Privilege::SetPermissions)));
|
||||||
|
return_value.set(object);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Value::is_object(ctx, args[0])) {
|
||||||
|
auto arg = Value::to_object(ctx, args[0]);
|
||||||
|
if (Object::template is_instance<RealmObjectClass<T>>(ctx, arg)) {
|
||||||
|
auto obj = get_internal<T, RealmObjectClass<T>>(arg);
|
||||||
|
auto p = realm->get_privileges(obj->row());
|
||||||
|
|
||||||
|
ObjectType object = Object::create_empty(ctx);
|
||||||
|
Object::set_property(ctx, object, "read", Value::from_boolean(ctx, has_privilege(p, Privilege::Read)));
|
||||||
|
Object::set_property(ctx, object, "update", Value::from_boolean(ctx,has_privilege(p, Privilege::Update)));
|
||||||
|
Object::set_property(ctx, object, "delete", Value::from_boolean(ctx,has_privilege(p, Privilege::Delete)));
|
||||||
|
Object::set_property(ctx, object, "setPermissions", Value::from_boolean(ctx, has_privilege(p, Privilege::SetPermissions)));
|
||||||
|
return_value.set(object);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& object_schema = validated_object_schema_for_value(ctx, realm, args[0]);
|
||||||
|
auto p = realm->get_privileges(object_schema.name);
|
||||||
|
ObjectType object = Object::create_empty(ctx);
|
||||||
|
Object::set_property(ctx, object, "read", Value::from_boolean(ctx, has_privilege(p, Privilege::Read)));
|
||||||
|
Object::set_property(ctx, object, "update", Value::from_boolean(ctx,has_privilege(p, Privilege::Update)));
|
||||||
|
Object::set_property(ctx, object, "create", Value::from_boolean(ctx, has_privilege(p, Privilege::Create)));
|
||||||
|
Object::set_property(ctx, object, "subscribe", Value::from_boolean(ctx, has_privilege(p, Privilege::Query)));
|
||||||
|
Object::set_property(ctx, object, "setPermissions", Value::from_boolean(ctx, has_privilege(p, Privilege::SetPermissions)));
|
||||||
|
return_value.set(object);
|
||||||
}
|
}
|
||||||
#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>>},
|
||||||
|
@ -141,6 +152,21 @@ typename T::Object ResultsClass<T>::create_instance(ContextType ctx, SharedRealm
|
||||||
return create_object<T, ResultsClass<T>>(ctx, new realm::js::Results<T>(realm, *table));
|
return create_object<T, ResultsClass<T>>(ctx, new realm::js::Results<T>(realm, *table));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void alias_backlinks(parser::KeyPathMapping &mapping, const realm::SharedRealm &realm)
|
||||||
|
{
|
||||||
|
const realm::Schema &schema = realm->schema();
|
||||||
|
for (auto it = schema.begin(); it != schema.end(); ++it) {
|
||||||
|
for (const Property &property : it->computed_properties) {
|
||||||
|
if (property.type == realm::PropertyType::LinkingObjects) {
|
||||||
|
auto target_object_schema = schema.find(property.object_type);
|
||||||
|
const TableRef table = ObjectStore::table_for_object_type(realm->read_group(), target_object_schema->name);
|
||||||
|
std::string native_name = "@links." + std::string(table->get_name()) + "." + property.link_origin_property_name;
|
||||||
|
mapping.add_mapping(table, property.name, native_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
template<typename U>
|
template<typename U>
|
||||||
typename T::Object ResultsClass<T>::create_filtered(ContextType ctx, const U &collection, Arguments args) {
|
typename T::Object ResultsClass<T>::create_filtered(ContextType ctx, const U &collection, Arguments args) {
|
||||||
|
@ -152,13 +178,17 @@ typename T::Object ResultsClass<T>::create_filtered(ContextType ctx, const U &co
|
||||||
auto query = collection.get_query();
|
auto query = collection.get_query();
|
||||||
auto const &realm = collection.get_realm();
|
auto const &realm = collection.get_realm();
|
||||||
auto const &object_schema = collection.get_object_schema();
|
auto const &object_schema = collection.get_object_schema();
|
||||||
|
DescriptorOrdering ordering;
|
||||||
|
parser::KeyPathMapping mapping;
|
||||||
|
alias_backlinks(mapping, realm);
|
||||||
|
|
||||||
parser::Predicate predicate = parser::parse(query_string);
|
parser::ParserResult result = parser::parse(query_string);
|
||||||
NativeAccessor<T> accessor(ctx, realm, object_schema);
|
NativeAccessor<T> accessor(ctx, realm, object_schema);
|
||||||
query_builder::ArgumentConverter<ValueType, NativeAccessor<T>> converter(accessor, &args.value[1], args.count - 1);
|
query_builder::ArgumentConverter<ValueType, NativeAccessor<T>> converter(accessor, &args.value[1], args.count - 1);
|
||||||
query_builder::apply_predicate(query, predicate, converter);
|
query_builder::apply_predicate(query, result.predicate, converter, mapping);
|
||||||
|
query_builder::apply_ordering(ordering, query.get_table(), result.ordering);
|
||||||
|
|
||||||
return create_instance(ctx, collection.filter(std::move(query)));
|
return create_instance(ctx, collection.filter(std::move(query)).apply_ordering(std::move(ordering)));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -252,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) {
|
||||||
|
@ -324,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
138
src/js_sync.hpp
138
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"
|
||||||
|
|
||||||
|
@ -102,7 +103,7 @@ public:
|
||||||
static void session_for_on_disk_path(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void session_for_on_disk_path(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||||
|
|
||||||
MethodMap<T> const methods = {
|
MethodMap<T> const methods = {
|
||||||
{"logout", wrap<logout>},
|
{"_logout", wrap<logout>},
|
||||||
{"_sessionForOnDiskPath", wrap<session_for_on_disk_path>}
|
{"_sessionForOnDiskPath", wrap<session_for_on_disk_path>}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -701,7 +823,19 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
||||||
is_partial = Value::validated_to_boolean(ctx, partial_value);
|
is_partial = Value::validated_to_boolean(ctx, partial_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
config.sync_config = std::make_shared<SyncConfig>(shared_user, std::move(raw_realm_url));
|
bool disable_partial_sync_url_checks = false;
|
||||||
|
ValueType disable_partial_sync_url_checks_value = Object::get_property(ctx, sync_config_object, "_disablePartialSyncUrlChecks");
|
||||||
|
if (!Value::is_undefined(ctx, disable_partial_sync_url_checks_value)) {
|
||||||
|
disable_partial_sync_url_checks = Value::validated_to_boolean(ctx, disable_partial_sync_url_checks_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disable_partial_sync_url_checks) {
|
||||||
|
config.sync_config = std::make_shared<SyncConfig>(shared_user, std::move(""));
|
||||||
|
config.sync_config->reference_realm_url = std::move(raw_realm_url);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
config.sync_config = std::make_shared<SyncConfig>(shared_user, std::move(raw_realm_url));
|
||||||
|
}
|
||||||
config.sync_config->bind_session_handler = std::move(bind);
|
config.sync_config->bind_session_handler = std::move(bind);
|
||||||
config.sync_config->error_handler = std::move(error_handler);
|
config.sync_config->error_handler = std::move(error_handler);
|
||||||
config.sync_config->client_validate_ssl = client_validate_ssl;
|
config.sync_config->client_validate_ssl = client_validate_ssl;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit d5fe9a626c0adc0345d128201e0df8bb4004725d
|
Subproject commit b250563ea1eb9f32ec7dbd76f2c6f8f1a26914cc
|
|
@ -37,7 +37,8 @@ module.exports = {
|
||||||
}
|
}
|
||||||
else if (type === 'object') {
|
else if (type === 'object') {
|
||||||
for (const key of Object.keys(val1)) {
|
for (const key of Object.keys(val1)) {
|
||||||
this.assertEqual(val1[key], val2[key], errorMessage, depth + 1);
|
const message = errorMessage ? `${errorMessage}: ${key}` : key;
|
||||||
|
this.assertEqual(val1[key], val2[key], message, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type === 'list') {
|
else if (type === 'list') {
|
||||||
|
|
|
@ -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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,26 @@ module.exports = {
|
||||||
TestCase.assertArraysEqual(names(resultsC), ['JP']);
|
TestCase.assertArraysEqual(names(resultsC), ['JP']);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
testFilteredLinkingObjectsByName: function() {
|
||||||
|
var realm = new Realm({schema: [schemas.PersonObject]});
|
||||||
|
|
||||||
|
var christine, olivier;
|
||||||
|
realm.write(function() {
|
||||||
|
olivier = realm.create('PersonObject', {name: 'Olivier', age: 0});
|
||||||
|
christine = realm.create('PersonObject', {name: 'Christine', age: 25, children: [olivier]});
|
||||||
|
realm.create('PersonObject', {name: 'JP', age: 28, children: [olivier]});
|
||||||
|
});
|
||||||
|
|
||||||
|
let people = realm.objects('PersonObject')
|
||||||
|
|
||||||
|
TestCase.assertEqual(people.filtered('parents.age > 25').length, 1);
|
||||||
|
TestCase.assertEqual(people.filtered('parents.age > 25')[0].name, 'Olivier');
|
||||||
|
TestCase.assertEqual(people.filtered('parents.@count == 2').length, 1);
|
||||||
|
TestCase.assertEqual(people.filtered('parents.name CONTAINS[c] "chris"').length, 1);
|
||||||
|
TestCase.assertEqual(people.filtered('parents.name.@size == 2').length, 1);
|
||||||
|
TestCase.assertEqual(people.filtered('25 IN parents.age').length, 1);
|
||||||
|
},
|
||||||
|
|
||||||
testMethod: function() {
|
testMethod: function() {
|
||||||
var realm = new Realm({schema: [schemas.PersonObject]});
|
var realm = new Realm({schema: [schemas.PersonObject]});
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ const realmName = process.argv[4];
|
||||||
const realmModule = process.argv[5];
|
const realmModule = process.argv[5];
|
||||||
|
|
||||||
const Realm = require(realmModule);
|
const Realm = require(realmModule);
|
||||||
|
// Ensure that schemas.js gets the correct module with `require('realm')`
|
||||||
|
require.cache[require.resolve('realm')] = require.cache[require.resolve(realmModule)];
|
||||||
let schemas = require(process.argv[2]);
|
let schemas = require(process.argv[2]);
|
||||||
|
|
||||||
function createObjects(user) {
|
function createObjects(user) {
|
||||||
|
|
|
@ -16,33 +16,33 @@
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Realm = require('realm');
|
var Realm = require('realm');
|
||||||
var TestCase = require('./asserts');
|
var TestCase = require('./asserts');
|
||||||
|
|
||||||
function uuid() {
|
function uuid() {
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
return v.toString(16);
|
return v.toString(16);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createUsersWithTestRealms(count) {
|
function createUsersWithTestRealms(count) {
|
||||||
const createUserWithTestRealm = () => {
|
const createUserWithTestRealm = () => {
|
||||||
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password')
|
return Realm.Sync.User
|
||||||
.then(user => {
|
.register('http://localhost:9080', uuid(), 'password')
|
||||||
new Realm({sync: {user, url: 'realm://localhost:9080/~/test'}}).close();
|
.then(user => {
|
||||||
return user;
|
new Realm({sync: {user, url: 'realm://localhost:9080/~/test'}}).close();
|
||||||
});
|
return user;
|
||||||
};
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return Promise.all(Array.from({length: count}, createUserWithTestRealm));
|
return Promise.all(Array.from({length: count}, createUserWithTestRealm));
|
||||||
}
|
}
|
||||||
|
|
||||||
function wait(t) {
|
function wait(t) {
|
||||||
return new Promise(resolve => setTimeout(resolve, t));
|
return new Promise(resolve => setTimeout(resolve, t));
|
||||||
}
|
}
|
||||||
|
|
||||||
function repeatUntil(fn, predicate) {
|
function repeatUntil(fn, predicate) {
|
||||||
|
@ -57,66 +57,183 @@ function repeatUntil(fn, predicate) {
|
||||||
return check;
|
return check;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function subscribe(results) {
|
||||||
|
const subscription = results.subscribe();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
subscription.addListener((subscription, state) => {
|
||||||
|
if (state == Realm.Sync.SubscriptionState.Complete) {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
else if (state == Realm.Sync.SubscriptionState.Error) {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTimeout(() => reject("listener never called"), 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForUpload(realm) {
|
||||||
|
let session = realm.syncSession;
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let callback = (transferred, total) => {
|
||||||
|
if (transferred === total) {
|
||||||
|
session.removeProgressNotification(callback);
|
||||||
|
resolve(realm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
session.addProgressNotification('upload', 'forCurrentlyOutstandingWork', callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function permissionForPath(permissions, path) {
|
||||||
|
for (const permission of permissions) {
|
||||||
|
if (permission.path == path) {
|
||||||
|
return permission;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
testApplyAndGetGrantedPermissions() {
|
testApplyAndGetGrantedPermissions() {
|
||||||
return createUsersWithTestRealms(1)
|
return createUsersWithTestRealms(1).then(([user]) => {
|
||||||
.then(([user]) => {
|
const path = `/${user.identity}/test`;
|
||||||
return user.applyPermissions({ userId: `${user.identity}` }, `/${user.identity}/test`, 'read')
|
return user
|
||||||
.then(repeatUntil(() => user.getGrantedPermissions('any'),
|
.applyPermissions({userId: `${user.identity}`},
|
||||||
permissions => permissions.length > 1))
|
`/${user.identity}/test`, 'read')
|
||||||
.then(permissions => {
|
.then(repeatUntil(() => user.getGrantedPermissions('any'),
|
||||||
TestCase.assertEqual(permissions[0].path, `/${user.identity}/test`);
|
permissions => {
|
||||||
TestCase.assertEqual(permissions[0].mayRead, true);
|
let permission = permissionForPath(permissions, path);
|
||||||
TestCase.assertEqual(permissions[0].mayWrite, false);
|
return permission && !permission.mayWrite;
|
||||||
TestCase.assertEqual(permissions[0].mayManage, false);
|
}))
|
||||||
});
|
.then(permissions => {
|
||||||
|
let permission = permissionForPath(permissions, path);
|
||||||
|
TestCase.assertDefined(permission);
|
||||||
|
TestCase.assertEqual(permission.mayRead, true);
|
||||||
|
TestCase.assertEqual(permission.mayWrite, false);
|
||||||
|
TestCase.assertEqual(permission.mayManage, false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
testOfferPermissions() {
|
testOfferPermissions() {
|
||||||
return createUsersWithTestRealms(2)
|
return createUsersWithTestRealms(2).then(([user1, user2]) => {
|
||||||
.then(([user1, user2]) => {
|
const path = `/${user1.identity}/test`;
|
||||||
return user1.offerPermissions(`/${user1.identity}/test`, 'read')
|
return user1.offerPermissions(`/${user1.identity}/test`, 'read')
|
||||||
.then(token => user2.acceptPermissionOffer(token))
|
.then(token => user2.acceptPermissionOffer(token))
|
||||||
.then(realmUrl => {
|
.then(realmUrl => {
|
||||||
TestCase.assertEqual(realmUrl, `/${user1.identity}/test`);
|
TestCase.assertEqual(realmUrl, path);
|
||||||
return realmUrl;
|
return realmUrl;
|
||||||
})
|
})
|
||||||
.then(repeatUntil(() => user2.getGrantedPermissions('any'),
|
.then(repeatUntil(() => user2.getGrantedPermissions('any'),
|
||||||
permissions => permissions.length > 1))
|
permissions => permissions.length > 2
|
||||||
.then(permissions => {
|
&& permissionForPath(permissions, path)))
|
||||||
TestCase.assertEqual(permissions[2].path, `/${user1.identity}/test`);
|
.then(permissions => {
|
||||||
TestCase.assertEqual(permissions[2].mayRead, true);
|
let permission = permissionForPath(permissions, path)
|
||||||
TestCase.assertEqual(permissions[2].mayWrite, false);
|
TestCase.assertDefined(permission);
|
||||||
TestCase.assertEqual(permissions[2].mayManage, false);
|
TestCase.assertEqual(permission.mayRead, true);
|
||||||
});
|
TestCase.assertEqual(permission.mayWrite, false);
|
||||||
|
TestCase.assertEqual(permission.mayManage, false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
testInvalidatePermissionOffer() {
|
testInvalidatePermissionOffer() {
|
||||||
let user1, user2, token;
|
let user1, user2, token;
|
||||||
return createUsersWithTestRealms(2)
|
return createUsersWithTestRealms(2)
|
||||||
.then(users => {
|
.then(users => {
|
||||||
user1 = users[0];
|
user1 = users[0];
|
||||||
user2 = users[1];
|
user2 = users[1];
|
||||||
return user1.offerPermissions(`/${user1.identity}/test`, 'read');
|
return user1.offerPermissions(`/${user1.identity}/test`, 'read');
|
||||||
})
|
})
|
||||||
.then(t => { token = t; return user1.invalidatePermissionOffer(token); })
|
.then(t => {
|
||||||
// Since we don't yet support notification when the invalidation has gone through,
|
token = t;
|
||||||
// wait for a bit and hope the server is done processing.
|
return user1.invalidatePermissionOffer(token);
|
||||||
.then(wait(100))
|
})
|
||||||
.then(() => user2.acceptPermissionOffer(token))
|
// Since we don't yet support notification when the invalidation has
|
||||||
// We want the call to fail, i.e. the catch() below should be called.
|
// gone through, wait for a bit and hope the server is done
|
||||||
.then(() => { throw new Error("User was able to accept an invalid permission offer token"); })
|
// processing.
|
||||||
.catch(error => {
|
.then(() => wait(100))
|
||||||
try {
|
.then(() => user2.acceptPermissionOffer(token))
|
||||||
TestCase.assertEqual(error.message, 'The permission offer is expired.');
|
// We want the call to fail, i.e. the catch() below should be
|
||||||
TestCase.assertEqual(error.statusCode, 701);
|
// called.
|
||||||
}
|
.then(() => {
|
||||||
catch (e) {
|
throw new Error("User was able to accept an invalid permission offer token");
|
||||||
throw new Error(e);
|
})
|
||||||
}
|
.catch(error => {
|
||||||
});
|
try {
|
||||||
|
TestCase.assertEqual(error.message, 'The permission offer is expired.');
|
||||||
|
TestCase.assertEqual(error.statusCode, 701);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}
|
|
||||||
|
|
||||||
|
testObjectPermissions() {
|
||||||
|
let config = (user, url) => {
|
||||||
|
return {
|
||||||
|
schema: [
|
||||||
|
Realm.Permissions.Permission,
|
||||||
|
Realm.Permissions.User,
|
||||||
|
Realm.Permissions.Role,
|
||||||
|
{
|
||||||
|
name: 'Object',
|
||||||
|
properties: {
|
||||||
|
value: 'int',
|
||||||
|
permissions: '__Permission[]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
sync: {user, url, partial: true}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let owner, otherUser
|
||||||
|
return Realm.Sync.User
|
||||||
|
.register('http://localhost:9080', uuid(), 'password')
|
||||||
|
.then(user => {
|
||||||
|
owner = user;
|
||||||
|
new Realm({sync: {user, url: 'realm://localhost:9080/~/test'}}).close();
|
||||||
|
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password')
|
||||||
|
})
|
||||||
|
.then(user => {
|
||||||
|
otherUser = user;
|
||||||
|
return owner.applyPermissions({userId: otherUser.identity},
|
||||||
|
`/${owner.identity}/test`, 'read')
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
let realm = new Realm(config(owner, 'realm://localhost:9080/~/test'));
|
||||||
|
realm.write(() => {
|
||||||
|
let user = realm.create(Realm.Permissions.User, {id: otherUser.identity})
|
||||||
|
let role = realm.create(Realm.Permissions.Role, {name: 'reader'})
|
||||||
|
role.members.push(user)
|
||||||
|
|
||||||
|
let obj1 = realm.create('Object', {value: 1});
|
||||||
|
let obj2 = realm.create('Object', {value: 2});
|
||||||
|
obj2.permissions.push(realm.create(Realm.Permissions.Permission,
|
||||||
|
{role: role, canRead: true, canUpdate: false}))
|
||||||
|
});
|
||||||
|
return waitForUpload(realm).then(() => realm.close());
|
||||||
|
})
|
||||||
|
.then(() => Realm.open(config(otherUser, `realm://localhost:9080/${owner.identity}/test`)))
|
||||||
|
.then((realm) => subscribe(realm.objects('Object')).then(() => realm))
|
||||||
|
.then((realm) => {
|
||||||
|
// Should have full access to the Realm as a whole
|
||||||
|
TestCase.assertSimilar('object', realm.privileges(),
|
||||||
|
{read: true, update: true, modifySchema: true, setPermissions: true});
|
||||||
|
TestCase.assertSimilar('object', realm.privileges('Object'),
|
||||||
|
{read: true, update: true, create: true, subscribe: true, setPermissions: true});
|
||||||
|
// Verify that checking via constructor works too
|
||||||
|
TestCase.assertSimilar('object', realm.privileges(Realm.Permissions.User),
|
||||||
|
{read: true, update: true, create: true, subscribe: true, setPermissions: true});
|
||||||
|
|
||||||
|
// Should only be able to see the second object
|
||||||
|
let results = realm.objects('Object')
|
||||||
|
TestCase.assertEqual(results.length, 1);
|
||||||
|
TestCase.assertEqual(results[0].value, 2);
|
||||||
|
TestCase.assertSimilar('object', realm.privileges(results[0]),
|
||||||
|
{read: true, update: false, delete: false, setPermissions: false});
|
||||||
|
realm.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -154,5 +154,8 @@ module.exports = {
|
||||||
},
|
},
|
||||||
testOptionalQueries: function() {
|
testOptionalQueries: function() {
|
||||||
runQuerySuite(testCases.optionalTests);
|
runQuerySuite(testCases.optionalTests);
|
||||||
|
},
|
||||||
|
testOrderingQueries: function() {
|
||||||
|
runQuerySuite(testCases.orderingTests);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
|
|
||||||
"dateTests" : {
|
"dateTests" : {
|
||||||
"schema" : [{
|
"schema" : [{
|
||||||
"name": "DateObject",
|
"name": "DateObject",
|
||||||
"properties": [{ "name": "date", "type": "date" }]
|
"properties": [{ "name": "date", "type": "date" }]
|
||||||
}],
|
}],
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"boolTests" : {
|
"boolTests" : {
|
||||||
"schema" : [{
|
"schema" : [{
|
||||||
"name": "BoolObject",
|
"name": "BoolObject",
|
||||||
"properties": [{ "name": "boolCol", "type": "bool" }]
|
"properties": [{ "name": "boolCol", "type": "bool" }]
|
||||||
}],
|
}],
|
||||||
|
@ -57,11 +57,11 @@
|
||||||
["QueryThrows", "BoolObject", "boolCol BEGINSWITH true"],
|
["QueryThrows", "BoolObject", "boolCol BEGINSWITH true"],
|
||||||
["QueryThrows", "BoolObject", "boolCol CONTAINS true"],
|
["QueryThrows", "BoolObject", "boolCol CONTAINS true"],
|
||||||
["QueryThrows", "BoolObject", "boolCol ENDSWITH true"]
|
["QueryThrows", "BoolObject", "boolCol ENDSWITH true"]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
"intTests" : {
|
"intTests" : {
|
||||||
"schema" : [{
|
"schema" : [{
|
||||||
"name": "IntObject",
|
"name": "IntObject",
|
||||||
"properties": [{ "name": "intCol", "type": "int" }]
|
"properties": [{ "name": "intCol", "type": "int" }]
|
||||||
}],
|
}],
|
||||||
|
@ -92,7 +92,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"floatTests" : {
|
"floatTests" : {
|
||||||
"schema" : [{
|
"schema" : [{
|
||||||
"name": "FloatObject",
|
"name": "FloatObject",
|
||||||
"properties": [{ "name": "floatCol", "type": "float" }]
|
"properties": [{ "name": "floatCol", "type": "float" }]
|
||||||
}],
|
}],
|
||||||
|
@ -126,7 +126,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"doubleTests" : {
|
"doubleTests" : {
|
||||||
"schema" : [{
|
"schema" : [{
|
||||||
"name": "DoubleObject",
|
"name": "DoubleObject",
|
||||||
"properties": [{ "name": "doubleCol", "type": "double" }]
|
"properties": [{ "name": "doubleCol", "type": "double" }]
|
||||||
}],
|
}],
|
||||||
|
@ -157,7 +157,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"stringTests" : {
|
"stringTests" : {
|
||||||
"schema" : [{
|
"schema" : [{
|
||||||
"name": "StringObject",
|
"name": "StringObject",
|
||||||
"properties": [{ "name": "stringCol", "type": "string" }]
|
"properties": [{ "name": "stringCol", "type": "string" }]
|
||||||
}],
|
}],
|
||||||
|
@ -205,7 +205,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"binaryTests" : {
|
"binaryTests" : {
|
||||||
"schema" : [{
|
"schema" : [{
|
||||||
"name": "BinaryObject",
|
"name": "BinaryObject",
|
||||||
"properties": [{ "name": "binaryCol", "type": "data" }]
|
"properties": [{ "name": "binaryCol", "type": "data" }]
|
||||||
}],
|
}],
|
||||||
|
@ -256,14 +256,14 @@
|
||||||
|
|
||||||
"compoundTests" : {
|
"compoundTests" : {
|
||||||
"schema" : [
|
"schema" : [
|
||||||
{ "name": "IntObject",
|
{ "name": "IntObject",
|
||||||
"properties": [{ "name": "intCol", "type": "int" }],
|
"properties": [{ "name": "intCol", "type": "int" }],
|
||||||
"primaryKey" : "intCol" }
|
"primaryKey" : "intCol" }
|
||||||
],
|
],
|
||||||
"objects": [
|
"objects": [
|
||||||
{ "type": "IntObject", "value": [0] },
|
{ "type": "IntObject", "value": [0] },
|
||||||
{ "type": "IntObject", "value": [1] },
|
{ "type": "IntObject", "value": [1] },
|
||||||
{ "type": "IntObject", "value": [2] },
|
{ "type": "IntObject", "value": [2] },
|
||||||
{ "type": "IntObject", "value": [3] }
|
{ "type": "IntObject", "value": [3] }
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
|
@ -281,14 +281,14 @@
|
||||||
["ObjectSet", [0, 1, 2],"IntObject", "intCol == 0 || intCol == 1 || intCol <= 2"],
|
["ObjectSet", [0, 1, 2],"IntObject", "intCol == 0 || intCol == 1 || intCol <= 2"],
|
||||||
["ObjectSet", [0, 1], "IntObject", "intCol == 1 && intCol >= 1 || intCol == 0"],
|
["ObjectSet", [0, 1], "IntObject", "intCol == 1 && intCol >= 1 || intCol == 0"],
|
||||||
["ObjectSet", [0, 1], "IntObject", "intCol == 1 || intCol == 0 && intCol <= 0 && intCol >= 0"],
|
["ObjectSet", [0, 1], "IntObject", "intCol == 1 || intCol == 0 && intCol <= 0 && intCol >= 0"],
|
||||||
["ObjectSet", [0, 1], "IntObject", "intCol == 0 || NOT (intCol == 3 && intCol >= 0) && intCol == 1"]
|
["ObjectSet", [0, 1], "IntObject", "intCol == 0 || NOT (intCol == 3 && intCol >= 0) && intCol == 1"]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
"keyPathTests" : {
|
"keyPathTests" : {
|
||||||
"schema" : [
|
"schema" : [
|
||||||
{
|
{
|
||||||
"name": "BasicTypesObject",
|
"name": "BasicTypesObject",
|
||||||
"properties": [
|
"properties": [
|
||||||
{ "name": "intCol", "type": "int" },
|
{ "name": "intCol", "type": "int" },
|
||||||
{ "name": "floatCol", "type": "float" },
|
{ "name": "floatCol", "type": "float" },
|
||||||
|
@ -296,7 +296,7 @@
|
||||||
{ "name": "stringCol", "type": "string" },
|
{ "name": "stringCol", "type": "string" },
|
||||||
{ "name": "dateCol", "type": "date?" }
|
{ "name": "dateCol", "type": "date?" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "LinkTypesObject",
|
"name": "LinkTypesObject",
|
||||||
"primaryKey": "primaryKey",
|
"primaryKey": "primaryKey",
|
||||||
|
@ -326,8 +326,8 @@
|
||||||
|
|
||||||
"optionalTests" : {
|
"optionalTests" : {
|
||||||
"schema" : [
|
"schema" : [
|
||||||
{
|
{
|
||||||
"name": "OptionalTypesObject",
|
"name": "OptionalTypesObject",
|
||||||
"primaryKey": "primaryKey",
|
"primaryKey": "primaryKey",
|
||||||
"properties": [
|
"properties": [
|
||||||
{ "name": "primaryKey", "type": "int" },
|
{ "name": "primaryKey", "type": "int" },
|
||||||
|
@ -338,7 +338,7 @@
|
||||||
{ "name": "dateCol", "type": "date", "optional": true },
|
{ "name": "dateCol", "type": "date", "optional": true },
|
||||||
{ "name": "dataCol", "type": "data", "optional": true }
|
{ "name": "dataCol", "type": "data", "optional": true }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "LinkTypesObject",
|
"name": "LinkTypesObject",
|
||||||
"primaryKey": "primaryKey",
|
"primaryKey": "primaryKey",
|
||||||
|
@ -375,9 +375,35 @@
|
||||||
["ObjectSet", [1], "LinkTypesObject", "basicLink.stringCol == null"],
|
["ObjectSet", [1], "LinkTypesObject", "basicLink.stringCol == null"],
|
||||||
["ObjectSet", [0], "LinkTypesObject", "basicLink.stringCol != null"],
|
["ObjectSet", [0], "LinkTypesObject", "basicLink.stringCol != null"],
|
||||||
["ObjectSet", [1], "LinkTypesObject", "basicLink.dateCol == null"],
|
["ObjectSet", [1], "LinkTypesObject", "basicLink.dateCol == null"],
|
||||||
["ObjectSet", [0], "LinkTypesObject", "basicLink.dateCol != null"],
|
["ObjectSet", [0], "LinkTypesObject", "basicLink.dateCol != null"]
|
||||||
["QueryThrows", "LinkTypesObject", "basicLink.dataCol == null"]
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"orderingTests" : {
|
||||||
|
"schema" : [
|
||||||
|
{ "name": "Person",
|
||||||
|
"properties": [
|
||||||
|
{ "name": "id", "type": "int"},
|
||||||
|
{ "name": "name", "type": "string" },
|
||||||
|
{ "name": "age", "type": "int"}
|
||||||
|
],
|
||||||
|
"primaryKey" : "id" }
|
||||||
|
],
|
||||||
|
"objects": [
|
||||||
|
{ "type": "Person", "value": [0, "John", 28] },
|
||||||
|
{ "type": "Person", "value": [1, "John", 37] },
|
||||||
|
{ "type": "Person", "value": [2, "Jake", 27] },
|
||||||
|
{ "type": "Person", "value": [3, "Jake", 32] },
|
||||||
|
{ "type": "Person", "value": [4, "Jake", 32] },
|
||||||
|
{ "type": "Person", "value": [5, "Johnny", 19] }
|
||||||
|
],
|
||||||
|
"tests": [
|
||||||
|
["ObjectSet", [1, 3], "Person", "age > 20 SORT(age DESC) DISTINCT(name)"],
|
||||||
|
["ObjectSet", [2, 0], "Person", "age > 20 SORT(age ASC) DISTINCT(name)"],
|
||||||
|
["ObjectSet", [2, 0], "Person", "age > 20 SORT(age ASC, name DESC) DISTINCT(name)"],
|
||||||
|
["ObjectSet", [2, 0], "Person", "age > 20 SORT(name DESC) SORT(age ASC) DISTINCT(name)"],
|
||||||
|
["ObjectSet", [2, 0, 3, 1], "Person", "age > 20 SORT(age ASC, name DESC) DISTINCT(name, age)"],
|
||||||
|
["ObjectSet", [0, 2], "Person", "age > 20 SORT(age ASC) DISTINCT(age) SORT(name DESC) DISTINCT(name)"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ module.exports = {
|
||||||
const realm2 = new Realm({schema: [], path: testPath2});
|
const realm2 = new Realm({schema: [], path: testPath2});
|
||||||
TestCase.assertEqual(realm2.path, defaultDir + testPath2);
|
TestCase.assertEqual(realm2.path, defaultDir + testPath2);
|
||||||
},
|
},
|
||||||
|
|
||||||
testRealmIsClosed: function() {
|
testRealmIsClosed: function() {
|
||||||
const realm = new Realm({schema: []});
|
const realm = new Realm({schema: []});
|
||||||
TestCase.assertFalse(realm.isClosed);
|
TestCase.assertFalse(realm.isClosed);
|
||||||
|
|
|
@ -61,27 +61,23 @@ function copyFileToTempDir(filename) {
|
||||||
return tmpFile.name;
|
return tmpFile.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function runOutOfProcess(nodeJsFilePath) {
|
function runOutOfProcess() {
|
||||||
var nodeArgs = Array.prototype.slice.call(arguments);
|
const args = Array.prototype.slice.call(arguments);
|
||||||
let tmpDir = tmp.dirSync();
|
let tmpDir = tmp.dirSync();
|
||||||
let content = fs.readFileSync(nodeJsFilePath, 'utf8');
|
console.log(`runOutOfProcess : ${args.join(' ')}`);
|
||||||
let tmpFile = tmp.fileSync({ dir: tmpDir.name });
|
|
||||||
fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' });
|
|
||||||
nodeArgs[0] = tmpFile.name;
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
console.log('runOutOfProcess command\n node ' + nodeArgs.join(" "));
|
execFile(process.execPath, args, {cwd: tmpDir.name}, (error, stdout, stderr) => {
|
||||||
const child = execFile('node', nodeArgs, { cwd: tmpDir.name }, (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("runOutOfProcess failed\n" + error);
|
console.error("runOutOfProcess failed\n", error, stdout, stderr);
|
||||||
reject(new Error(`Running ${nodeJsFilePath} failed. error: ${error}`));
|
reject(new Error(`Running ${args[0]} failed. error: ${error}`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('runOutOfProcess success\n' + stdout);
|
console.log('runOutOfProcess success\n' + stdout);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
|
@ -170,6 +166,33 @@ module.exports = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
testDefaultRealm() {
|
||||||
|
if (!isNodeProccess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const username = uuid();
|
||||||
|
const realmName = 'default';
|
||||||
|
const expectedObjectsCount = 3;
|
||||||
|
|
||||||
|
let user;
|
||||||
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
|
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||||
|
.then(u => {
|
||||||
|
user = u;
|
||||||
|
return Realm.open(Realm.automaticSyncConfiguration());
|
||||||
|
})
|
||||||
|
.then(realm => {
|
||||||
|
let actualObjectsCount = realm.objects('Dog').length;
|
||||||
|
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count");
|
||||||
|
|
||||||
|
const session = realm.syncSession;
|
||||||
|
TestCase.assertInstanceOf(session, Realm.Sync.Session);
|
||||||
|
TestCase.assertEqual(session.user.identity, user.identity);
|
||||||
|
TestCase.assertEqual(session.state, 'active');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
testRealmOpenWithExistingLocalRealm() {
|
testRealmOpenWithExistingLocalRealm() {
|
||||||
if (!isNodeProccess) {
|
if (!isNodeProccess) {
|
||||||
return;
|
return;
|
||||||
|
@ -275,43 +298,40 @@ module.exports = {
|
||||||
const expectedObjectsCount = 3;
|
const expectedObjectsCount = 3;
|
||||||
|
|
||||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
.then(user => {
|
||||||
return new Promise((resolve, reject) => {
|
const accessTokenRefreshed = this;
|
||||||
const accessTokenRefreshed = this;
|
let successCounter = 0;
|
||||||
let successCounter = 0;
|
|
||||||
|
|
||||||
let config = {
|
let config = {
|
||||||
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
||||||
};
|
};
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
Realm.openAsync(config, (error, realm) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let actualObjectsCount = realm.objects('Dog').length;
|
||||||
|
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count");
|
||||||
|
|
||||||
Realm.openAsync(config, (error, realm) => {
|
let firstDog = realm.objects('Dog')[0];
|
||||||
try {
|
TestCase.assertTrue(({}).hasOwnProperty.call(firstDog, 'name'), "Synced realm does not have an inffered schema");
|
||||||
if (error) {
|
TestCase.assertTrue(firstDog.name, "Synced realm object's property should have a value");
|
||||||
reject(error);
|
TestCase.assertTrue(firstDog.name.indexOf('Lassy') !== -1, "Synced realm object's property should contain the actual written value");
|
||||||
}
|
|
||||||
|
|
||||||
let actualObjectsCount = realm.objects('Dog').length;
|
const session = realm.syncSession;
|
||||||
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count");
|
TestCase.assertInstanceOf(session, Realm.Sync.Session);
|
||||||
|
TestCase.assertEqual(session.user.identity, user.identity);
|
||||||
let firstDog = realm.objects('Dog')[0];
|
TestCase.assertEqual(session.config.url, config.sync.url);
|
||||||
TestCase.assertTrue(({}).hasOwnProperty.call(firstDog, 'name'), "Synced realm does not have an inffered schema");
|
TestCase.assertEqual(session.config.user.identity, config.sync.user.identity);
|
||||||
TestCase.assertTrue(firstDog.name, "Synced realm object's property should have a value");
|
TestCase.assertEqual(session.state, 'active');
|
||||||
TestCase.assertTrue(firstDog.name.indexOf('Lassy') !== -1, "Synced realm object's property should contain the actual written value");
|
resolve();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
const session = realm.syncSession;
|
reject(e);
|
||||||
TestCase.assertInstanceOf(session, Realm.Sync.Session);
|
}
|
||||||
TestCase.assertEqual(session.user.identity, user.identity);
|
|
||||||
TestCase.assertEqual(session.config.url, config.sync.url);
|
|
||||||
TestCase.assertEqual(session.config.user.identity, config.sync.user.identity);
|
|
||||||
TestCase.assertEqual(session.state, 'active');
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
reject(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -321,26 +341,22 @@ module.exports = {
|
||||||
const username = uuid();
|
const username = uuid();
|
||||||
const expectedObjectsCount = 3;
|
const expectedObjectsCount = 3;
|
||||||
|
|
||||||
|
const accessTokenRefreshed = this;
|
||||||
|
let successCounter = 0;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
let config = {
|
||||||
const accessTokenRefreshed = this;
|
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||||
let successCounter = 0;
|
};
|
||||||
|
|
||||||
let config = {
|
return Realm.open(config).then(realm => {
|
||||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
realm.write(() => {
|
||||||
};
|
for (let i = 1; i <= 3; i++) {
|
||||||
|
realm.create('Dog', { name: `Lassy ${i}` });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Realm.open(config).then(realm => {
|
let actualObjectsCount = realm.objects('Dog').length;
|
||||||
realm.write(() => {
|
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Local realm does not contain the expected objects count");
|
||||||
for (let i = 1; i <= 3; i++) {
|
|
||||||
realm.create('Dog', { name: `Lassy ${i}` });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let actualObjectsCount = realm.objects('Dog').length;
|
|
||||||
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Local realm does not contain the expected objects count");
|
|
||||||
resolve();
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -412,37 +428,33 @@ module.exports = {
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
|
|
||||||
return runOutOfProcess(__dirname + '/nested-list-helper.js', __dirname + '/schemas.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/nested-list-helper.js', __dirname + '/schemas.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
.then(user => {
|
||||||
return new Promise((resolve, reject) => {
|
let config = {
|
||||||
let config = {
|
schema: [schemas.ParentObject, schemas.NameObject],
|
||||||
schema: [schemas.ParentObject, schemas.NameObject],
|
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
||||||
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
};
|
||||||
};
|
return Realm.open(config)
|
||||||
Realm.open(config).then(realm => {
|
}).then(realm => {
|
||||||
let objects = realm.objects('ParentObject');
|
let objects = realm.objects('ParentObject');
|
||||||
|
|
||||||
let json = JSON.stringify(objects);
|
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(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.length, 2);
|
||||||
TestCase.assertEqual(objects[0].name.length, 2);
|
TestCase.assertEqual(objects[0].name.length, 2);
|
||||||
TestCase.assertEqual(objects[0].name[0].given.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].prefix.length, 0);
|
||||||
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
||||||
TestCase.assertEqual(objects[0].name[0].given[1], 'Jørgen')
|
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.length, 1);
|
||||||
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
||||||
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
||||||
|
|
||||||
TestCase.assertEqual(objects[1].name.length, 1);
|
TestCase.assertEqual(objects[1].name.length, 1);
|
||||||
TestCase.assertEqual(objects[1].name[0].given.length, 2);
|
TestCase.assertEqual(objects[1].name[0].given.length, 2);
|
||||||
TestCase.assertEqual(objects[1].name[0].prefix.length, 0);
|
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[0], 'Gurli');
|
||||||
TestCase.assertEqual(objects[1].name[0].given[1], 'Margrete');
|
TestCase.assertEqual(objects[1].name[0].given[1], 'Margrete');
|
||||||
resolve();
|
|
||||||
}).catch(() => reject());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -567,7 +579,7 @@ module.exports = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
testProgressNotificationsForRealmConstructor() {
|
/* testProgressNotificationsForRealmConstructor() {
|
||||||
if (!isNodeProccess) {
|
if (!isNodeProccess) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -599,7 +611,7 @@ module.exports = {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},*/
|
||||||
|
|
||||||
testProgressNotificationsUnregisterForRealmConstructor() {
|
testProgressNotificationsUnregisterForRealmConstructor() {
|
||||||
if (!isNodeProccess) {
|
if (!isNodeProccess) {
|
||||||
|
@ -610,60 +622,59 @@ module.exports = {
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
|
|
||||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
.then(user => {
|
||||||
return new Promise((resolve, reject) => {
|
let config = {
|
||||||
let config = {
|
sync: {
|
||||||
sync: {
|
user,
|
||||||
user,
|
url: `realm://localhost:9080/~/${realmName}`
|
||||||
url: `realm://localhost:9080/~/${realmName}`
|
},
|
||||||
},
|
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let realm = new Realm(config);
|
let realm = new Realm(config);
|
||||||
let unregisterFunc;
|
let unregisterFunc;
|
||||||
|
|
||||||
let writeDataFunc = () => {
|
let writeDataFunc = () => {
|
||||||
realm.write(() => {
|
realm.write(() => {
|
||||||
for (let i = 1; i <= 3; i++) {
|
for (let i = 1; i <= 3; i++) {
|
||||||
realm.create('Dog', { name: `Lassy ${i}` });
|
realm.create('Dog', { name: `Lassy ${i}` });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let syncFinished = false;
|
||||||
|
let failOnCall = false;
|
||||||
|
const progressCallback = (transferred, total) => {
|
||||||
|
if (failOnCall) {
|
||||||
|
reject(new Error("Progress callback should not be called after removeProgressNotification"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let syncFinished = false;
|
syncFinished = transferred === total;
|
||||||
let failOnCall = false;
|
|
||||||
const progressCallback = (transferred, total) => {
|
|
||||||
if (failOnCall) {
|
|
||||||
reject(new Error("Progress callback should not be called after removeProgressNotification"));
|
|
||||||
}
|
|
||||||
|
|
||||||
syncFinished = transferred === total;
|
//unregister and write some new data.
|
||||||
|
if (syncFinished) {
|
||||||
|
failOnCall = true;
|
||||||
|
unregisterFunc();
|
||||||
|
|
||||||
//unregister and write some new data.
|
//use second callback to wait for sync finished
|
||||||
if (syncFinished) {
|
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', (transferred, transferable) => {
|
||||||
failOnCall = true;
|
if (transferred === transferable) {
|
||||||
unregisterFunc();
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
writeDataFunc();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//use second callback to wait for sync finished
|
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', progressCallback);
|
||||||
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', (transferred, transferable) => {
|
|
||||||
if (transferred === transferable) {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
writeDataFunc();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', progressCallback);
|
unregisterFunc = () => {
|
||||||
|
realm.syncSession.removeProgressNotification(progressCallback);
|
||||||
|
};
|
||||||
|
|
||||||
unregisterFunc = () => {
|
writeDataFunc();
|
||||||
realm.syncSession.removeProgressNotification(progressCallback);
|
|
||||||
};
|
|
||||||
|
|
||||||
writeDataFunc();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -675,36 +686,24 @@ module.exports = {
|
||||||
|
|
||||||
const username = uuid();
|
const username = uuid();
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
|
let progressCalled = false;
|
||||||
|
|
||||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
.then(user => {
|
||||||
return new Promise((resolve, reject) => {
|
let config = {
|
||||||
let config = {
|
sync: {
|
||||||
sync: {
|
user,
|
||||||
user,
|
url: `realm://localhost:9080/~/${realmName}`
|
||||||
url: `realm://localhost:9080/~/${realmName}`
|
},
|
||||||
},
|
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let progressCalled = false;
|
return Promise.race([
|
||||||
Realm.open(config)
|
Realm.open(config).progress((transferred, total) => { progressCalled = true; }),
|
||||||
.progress((transferred, total) => {
|
new Promise((_, reject) => setTimeout(() => reject("Progress Notifications API failed to call progress callback for Realm constructor"), 5000))
|
||||||
progressCalled = true;
|
]);
|
||||||
})
|
}).then(() => TestCase.assertTrue(progressCalled));
|
||||||
.then(() => {
|
|
||||||
TestCase.assertTrue(progressCalled);
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.catch((e) => reject(e));
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
|
||||||
}, 5000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
testProgressNotificationsForRealmOpenAsync() {
|
testProgressNotificationsForRealmOpenAsync() {
|
||||||
|
@ -716,42 +715,71 @@ module.exports = {
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
|
|
||||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
.then(user => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let config = {
|
let config = {
|
||||||
sync: {
|
sync: {
|
||||||
user,
|
user,
|
||||||
url: `realm://localhost:9080/~/${realmName}`
|
url: `realm://localhost:9080/~/${realmName}`
|
||||||
},
|
},
|
||||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||||
};
|
};
|
||||||
|
|
||||||
let progressCalled = false;
|
let progressCalled = false;
|
||||||
|
|
||||||
Realm.openAsync(config,
|
Realm.openAsync(config,
|
||||||
(error, realm) => {
|
(error, realm) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase.assertTrue(progressCalled);
|
TestCase.assertTrue(progressCalled);
|
||||||
resolve();
|
resolve();
|
||||||
},
|
},
|
||||||
(transferred, total) => {
|
(transferred, total) => {
|
||||||
progressCalled = true;
|
progressCalled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
||||||
}, 5000);
|
}, 5000);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
testPartialSync() {
|
testOpenPartialSyncUrl() {
|
||||||
|
if (!isNodeProccess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const username = uuid();
|
||||||
|
return Realm.Sync.User.register('http://localhost:9080', username, 'password')
|
||||||
|
.then(user => {
|
||||||
|
let config1 = {
|
||||||
|
sync: {
|
||||||
|
user: user,
|
||||||
|
url: `realm://localhost:9080/~/default/__partial/`,
|
||||||
|
partial: true,
|
||||||
|
_disablePartialSyncUrlChecks: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const realm = new Realm(config1);
|
||||||
|
TestCase.assertFalse(realm.isClosed);
|
||||||
|
|
||||||
|
let config2 = {
|
||||||
|
sync: {
|
||||||
|
user: user,
|
||||||
|
url: `realm://localhost:9080/~/default/__partial/`, // <--- not allowed URL
|
||||||
|
partial: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TestCase.assertThrows(() => new Realm(config2));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
testPartialSyncAnonymous_SubscriptionListener() {
|
||||||
// FIXME: try to enable for React Native
|
// FIXME: try to enable for React Native
|
||||||
if (!isNodeProccess) {
|
if (!isNodeProccess) {
|
||||||
return;
|
return;
|
||||||
|
@ -761,27 +789,213 @@ module.exports = {
|
||||||
const realmName = uuid();
|
const realmName = uuid();
|
||||||
|
|
||||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||||
.then(() => {
|
.then(() => Realm.Sync.User.login('http://localhost:9080', username, 'password'))
|
||||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
.then(user => {
|
||||||
let config = {
|
let config = {
|
||||||
sync: {
|
sync: {
|
||||||
user: user,
|
user: user,
|
||||||
url: `realm://localhost:9080/~/${realmName}`,
|
url: `realm://localhost:9080/~/${realmName}`,
|
||||||
partial: true,
|
partial: true,
|
||||||
error: (session, error) => console.log(error)
|
error: (session, error) => console.log(error)
|
||||||
},
|
},
|
||||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||||
};
|
};
|
||||||
|
|
||||||
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(() => 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(() => 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(() => 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(() => 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() {
|
||||||
|
|
Loading…
Reference in New Issue