Merge branch '2.3.x' of github.com:realm/realm-js into jas/backlink-queries
This commit is contained in:
commit
8e63737008
90
CHANGELOG.md
90
CHANGELOG.md
|
@ -1,32 +1,65 @@
|
|||
2.3.0 Release notes (2018-2-19)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* Sync protocol changed to version 24.
|
||||
* History schema format for server-side Realm files bumped to version 4. This means that after the server has been upgraded, it cannot be downgraded again without restoring state from backup.
|
||||
* Backup protocol version bumped to 2. No compatibility with earlier versions of the backup protocol is provided.
|
||||
* [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
|
||||
* Reduced initial download times in Realms with long transaction histories.
|
||||
* Wait for pending notifications to complete when removing a sync listener (1648).
|
||||
* [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.
|
||||
* [Sync] Added `Realm.Results.subscribe()` to subscribe to partial synced Realms.
|
||||
* [Sync] Added class `Realm.Sync.Subscription` to support partial synced Realms.
|
||||
|
||||
### Internal
|
||||
* Updated to Realm Core 5.2.0.
|
||||
* Updated to Realm Sync 3.0.0-beta.5.
|
||||
* Updated to Realm Sync 3.0.0-beta.9.
|
||||
* Tested against Realm Object Server 3.0.0-alpha.2.
|
||||
|
||||
|
||||
2.2.10 Release notes (2018-2-20)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* [Sync] A use-after-free bug was fixed which could cause arrays of primitives to behave unexpectedly.
|
||||
|
||||
### Internal
|
||||
* Updated to Realm Sync 2.2.12.
|
||||
|
||||
|
||||
2.2.9 Release notes (2018-2-19)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* Improved root certificate checking.
|
||||
|
||||
### Internal
|
||||
* Updated to Realm Sync 2.2.11.
|
||||
|
||||
|
||||
2.2.8 Release notes (2018-2-13)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### 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
|
||||
* None.
|
||||
|
@ -41,15 +74,16 @@
|
|||
* None.
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
* None.
|
||||
|
||||
|
||||
2.2.6 Release notes (2018-1-26)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
|
@ -59,7 +93,7 @@
|
|||
* None.
|
||||
|
||||
### 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
|
||||
* None.
|
||||
|
@ -73,7 +107,7 @@
|
|||
* None.
|
||||
|
||||
### 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).
|
||||
* [Electron] Fixed a `dlopen` error related to OpenSSL that prevented using realm-js on Linux (#1636).
|
||||
|
||||
|
@ -89,8 +123,8 @@
|
|||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* [Object Server] Fixed a bug where errors in `refreshAdminToken` wasn't catched (#1627).
|
||||
* [Object Server] Added `_getExitingUser` to the Chrome debugging support library.
|
||||
* [Sync] Fixed a bug where errors in `refreshAdminToken` wasn't catched (#1627).
|
||||
* [Sync] Added `_getExitingUser` to the Chrome debugging support library.
|
||||
|
||||
### Internal
|
||||
* None.
|
||||
|
@ -104,8 +138,8 @@
|
|||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* [Object Server] 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 in upload progress reporting.
|
||||
* [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
|
||||
* None.
|
||||
|
@ -119,7 +153,7 @@
|
|||
* None.
|
||||
|
||||
### Bug fixes
|
||||
* [Object Server] Added missing `Realm.Sync` listener functions.
|
||||
* [Sync] Added missing `Realm.Sync` listener functions.
|
||||
|
||||
### Internal
|
||||
* None.
|
||||
|
@ -134,7 +168,7 @@
|
|||
* None.
|
||||
|
||||
### 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
|
||||
* None.
|
||||
|
@ -147,8 +181,8 @@
|
|||
### 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.
|
||||
* Potential performance enhancements in cases of many writes between queries.
|
||||
* [Object Server] Added method `Realm.Sync.User.authenticate` to unify authentication of users.
|
||||
* [Object Server] Added JWT authenfication (#1548).
|
||||
* [Sync] Added method `Realm.Sync.User.authenticate` to unify authentication of users.
|
||||
* [Sync] Added JWT authenfication (#1548).
|
||||
|
||||
### Bug fixes
|
||||
* Fix a bug where `Realm.open` could unexpectedly raise a "Realm at path ... already opened with different schema version" error.
|
||||
|
@ -169,10 +203,10 @@
|
|||
* None.
|
||||
|
||||
### 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
|
||||
* [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").
|
||||
|
||||
|
||||
|
@ -200,11 +234,11 @@
|
|||
* None.
|
||||
|
||||
### 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
|
||||
* [Object Server] 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] Strings can now be assigned to Date columns. When that happens the JavaScript Date constructor will be invoked to parse the string.
|
||||
* [Sync] Base64 strings can now be assigned to Data columns.
|
||||
|
||||
2.0.12 Release notes (2017-12-1)
|
||||
=============================================================
|
||||
|
@ -230,8 +264,8 @@
|
|||
* None
|
||||
|
||||
### Bug fixes
|
||||
* [Object Server] 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 where deleted-then-recreated objects with identical primary keys to become empty.
|
||||
* [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
|
||||
* Updated to Realm Sync 2.1.7 (see under "Bug fixes").
|
||||
|
@ -270,10 +304,10 @@
|
|||
* None.
|
||||
|
||||
### Enhancements
|
||||
* [Object Server] Improving performance of processing large changesets.
|
||||
* [Sync] Improving performance of processing large changesets.
|
||||
|
||||
### Bug fixes
|
||||
* [Object Server] Changesets over 16MB in size are now handled correctly.
|
||||
* [Sync] Changesets over 16MB in size are now handled correctly.
|
||||
|
||||
### Internal
|
||||
* Updated to Realm Sync 2.1.6.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
PACKAGE_NAME=realm-js
|
||||
VERSION=2.3.0-alpha.11
|
||||
VERSION=2.3.0-alpha.13
|
||||
REALM_CORE_VERSION=5.2.0
|
||||
REALM_SYNC_VERSION=3.0.0-beta.6
|
||||
REALM_SYNC_VERSION=3.0.0-beta.9
|
||||
REALM_OBJECT_SERVER_VERSION=3.0.0-alpha.2
|
||||
|
|
|
@ -134,6 +134,26 @@ class Collection {
|
|||
*/
|
||||
snapshot() {}
|
||||
|
||||
/**
|
||||
* Subscribe to a subset of objects matching the query of the collection. The Realm will only be
|
||||
* partially synced. Not all queries are currently supported. Once subscribed, it is highly recommended
|
||||
* to add a listener.
|
||||
*
|
||||
* @example
|
||||
* let wines = realm.objects('Wine').filtered('vintage <= $0', maxYear).subscribe();
|
||||
* wines.addListener((collection, changes) => {
|
||||
* if (changes.partial_sync.new_state == Realm.Sync.SubscriptionState.Initialized) {
|
||||
* // update UI
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* @param {string} subscriptionName - an optional name for the subscription.
|
||||
* @returns {Realm.Sync.Subscription} - the Realm.Sync.Subscription instance.
|
||||
* @throws {Error} if the partial sync is not enabled in the configuration or the query is not supported by Realm Object Server.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
subscribe(subscriptionName) {}
|
||||
|
||||
/**
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries Array.prototype.entries}
|
||||
* @returns {Realm.Collection~Iterator<T>} of each `[index, object]` pair in the collection
|
||||
|
@ -400,15 +420,21 @@ class Collection {
|
|||
* The callback function is called with two arguments:
|
||||
* - `collection`: the collection instance that changed,
|
||||
* - `changes`: a dictionary with keys `insertions`, `modifications` and `deletions`,
|
||||
* each containing a list of indices that were inserted, updated or deleted respectively.
|
||||
* each containing a list of indices that were inserted, updated or deleted respectively. If
|
||||
* partial sync is enabled, an additional key `partial_sync` is added.
|
||||
* - `changes.partial_sync`: `error` indicates if an error has occurred, `old_state` is the previous
|
||||
* state, and `new_state` is the current state.
|
||||
* @throws {Error} If `callback` is not a function.
|
||||
* @example
|
||||
* wines.addListener((collection, changes) => {
|
||||
* // collection === wines
|
||||
* console.log(`${changes.insertions.length} insertions`);
|
||||
* console.log(`${changes.modifications.length} modifications`);
|
||||
* console.log(`${changes.deletions.length} deletions`);
|
||||
* console.log(`new size of collection: ${collection.length}`);
|
||||
* if (changes.partial_sync.new_state == Realm.Sync.SubscriptionState.Initialized) {
|
||||
* console.log('Our subset is ready');
|
||||
* console.log(`${changes.insertions.length} insertions`);
|
||||
* console.log(`${changes.modifications.length} modifications`);
|
||||
* console.log(`${changes.deletions.length} deletions`);
|
||||
* console.log(`new size of collection: ${collection.length}`);
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
addListener(callback) {}
|
||||
|
|
|
@ -241,17 +241,6 @@ class Realm {
|
|||
* @returns {true} if compaction succeeds.
|
||||
*/
|
||||
compact() {}
|
||||
|
||||
/**
|
||||
* If the Realm is a partially synchronized Realm, fetch and synchronize the objects
|
||||
* of a given object type that match the given query (in string format).
|
||||
*
|
||||
* **Partial synchronization is a tech preview. Its APIs are subject to change.**
|
||||
* @param {Realm~ObjectType} type - The type of Realm objects to retrieve.
|
||||
* @param {string} query - Query used to filter objects.
|
||||
* @return {Promise} - a promise that will be resolved with the Realm.Results instance when it's available.
|
||||
*/
|
||||
subscribeToObjects(className, query, callback) {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
58
docs/sync.js
58
docs/sync.js
|
@ -468,6 +468,64 @@ class Session {
|
|||
removeProgressNotification(progressCallback) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object encapsulating partial sync subscriptions.
|
||||
* @memberof Realm.Sync
|
||||
*/
|
||||
class Subscription {
|
||||
/**
|
||||
* Gets the current state of the subscription.
|
||||
* Can be either:
|
||||
* - Realm.Sync.SubscriptionState.Error: An error occurred while creating or processing the partial sync subscription.
|
||||
* - Realm.Sync.SubscriptionState.Creating: The subscription is being created.
|
||||
* - Realm.Sync.SubscriptionState.Pending: The subscription was created, but has not yet been processed by the sync server.
|
||||
* - Realm.Sync.SubscriptionState.Complete: The subscription has been processed by the sync server and data is being synced to the device.
|
||||
* - Realm.Sync.SubscriptionState.Invalidated: The subscription has been removed.
|
||||
* @type {number}
|
||||
*/
|
||||
get state() {}
|
||||
|
||||
/**
|
||||
* Gets the error message. `undefined` if no error.
|
||||
* @type {string}
|
||||
*/
|
||||
get error() {}
|
||||
|
||||
/**
|
||||
* Unsubscribe a partial synced `Realm.Results`. The state will change to `Realm.Sync.SubscriptionState.Invalidated`.
|
||||
* The `Realm.Results` will not produce any meaningful values. Moreover, any objects matching the query will be
|
||||
* removed if they are not matched by any other query. The object removal is done asynchronously.
|
||||
*/
|
||||
unsubscribe() {}
|
||||
|
||||
/**
|
||||
* Adds a listener `callback` which will be called when the state of the subscription changes.
|
||||
* @param {function(state)} callback - A function to be called when changes occur.
|
||||
* @throws {Error} If `callback` is not a function.
|
||||
* @example
|
||||
* let subscription = results.subscribe();
|
||||
* subscription.addListener((subscription, state) => {
|
||||
* switch (state) {
|
||||
* case Realm.Sync.SubscriptionState.Complete:
|
||||
* // results is ready to be consumed
|
||||
* break;
|
||||
* case Realm.Sync.SubscriptionState.Error:
|
||||
* console.log('An error occurred: ', subscription.error);
|
||||
* break;
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
addListener(callback) {}
|
||||
|
||||
/**
|
||||
* Remove the listener `callback` from the subscription instance.
|
||||
* @param {function(collection, changes)} callback - Callback function that was previously
|
||||
* added as a listener through the {@link Subscription#addListener addListener} method.
|
||||
* @throws {Error} If `callback` is not a function.
|
||||
*/
|
||||
removeListener(callback) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Realm Worker can be used to process Sync events in multiple automatically-managed child processes.
|
||||
*
|
||||
|
|
|
@ -20,7 +20,7 @@ public class MainApplication extends Application implements ReactApplication {
|
|||
|
||||
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
|
||||
@Override
|
||||
protected boolean getUseDeveloperSupport() {
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ export const propTypes = {};
|
|||
'RESULTS',
|
||||
'USER',
|
||||
'SESSION',
|
||||
'SUBSCRIPTION',
|
||||
'UNDEFINED',
|
||||
].forEach(function(type) {
|
||||
Object.defineProperty(objectTypes, type, {
|
||||
|
|
|
@ -130,7 +130,6 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
|||
'close',
|
||||
'_waitForDownload',
|
||||
'_objectForObjectId',
|
||||
'_subscribeToObjects',
|
||||
]);
|
||||
|
||||
// Mutating methods:
|
||||
|
@ -147,7 +146,8 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
|||
|
||||
const Sync = {
|
||||
User,
|
||||
Session
|
||||
Session,
|
||||
Subscription,
|
||||
};
|
||||
|
||||
Object.defineProperties(Realm, {
|
||||
|
|
|
@ -30,6 +30,7 @@ createMethods(Results.prototype, objectTypes.RESULTS, [
|
|||
'filtered',
|
||||
'sorted',
|
||||
'snapshot',
|
||||
'subscribe',
|
||||
'isValid',
|
||||
'indexOf',
|
||||
'min',
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2018 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
'use strict';
|
||||
|
||||
import { objectTypes } from './constants';
|
||||
import { getterForProperty, createMethods } from './util';
|
||||
|
||||
export default class Subscription {
|
||||
|
||||
}
|
||||
|
||||
Object.defineProperties(Subscription.prototype, {
|
||||
error: { get: getterForProperty('error') },
|
||||
state: { get: getterForProperty('state') }
|
||||
});
|
||||
|
||||
// // Non-mutating methods:
|
||||
createMethods(Subscription.prototype, objectTypes.SUBSCRIPTION, [
|
||||
'unsubscribe',
|
||||
'addListener',
|
||||
'removeListener'
|
||||
]);
|
|
@ -159,18 +159,13 @@ module.exports = function(realmConstructor) {
|
|||
}
|
||||
}
|
||||
|
||||
realmConstructor.prototype.subscribeToObjects = function(objectType, query) {
|
||||
const realm = this;
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
realm._subscribeToObjects(objectType, query, function(err, results) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(results);
|
||||
}
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
// Keep these value in sync with subscription_state.hpp
|
||||
realmConstructor.Sync.SubscriptionState = {
|
||||
Error: -1, // An error occurred while creating or processing the partial sync subscription.
|
||||
Creating: 2, // The subscription is being created.
|
||||
Pending: 0, // The subscription was created, but has not yet been processed by the sync server.
|
||||
Complete: 1, // The subscription has been processed by the sync server and data is being synced to the device.
|
||||
Invalidated: 3, // The subscription has been removed.
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -161,6 +161,11 @@ declare namespace Realm {
|
|||
sorted(descriptor: SortDescriptor[]): Results<T>;
|
||||
sorted(descriptor: string, reverse?: boolean): Results<T>;
|
||||
|
||||
/**
|
||||
* @returns Results<T>
|
||||
*/
|
||||
subscribe(subscriptionName?: string): Realm.Sync.Subscription;
|
||||
|
||||
/**
|
||||
* @returns Results
|
||||
*/
|
||||
|
@ -394,6 +399,21 @@ declare namespace Realm.Sync {
|
|||
removeProgressNotification(progressCallback: ProgressNotificationCallback): void;
|
||||
}
|
||||
|
||||
type SubscriptionNotificationCallback = (state: number) => void;
|
||||
|
||||
/**
|
||||
* Subscription
|
||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.Subscription.html }
|
||||
*/
|
||||
class Subscription {
|
||||
readonly state: number;
|
||||
readonly error: string;
|
||||
|
||||
unsubscribe(): void;
|
||||
addListener(subscruptionCallback: SubscriptionNotificationCallback): void;
|
||||
removeListener(subscruptionCallback: SubscriptionNotificationCallback): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* AuthError
|
||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.AuthError.html }
|
||||
|
@ -545,9 +565,9 @@ declare class Realm {
|
|||
/**
|
||||
* @param {string|Realm.ObjectSchema|Function} type
|
||||
* @param {number|string} key
|
||||
* @returns T
|
||||
* @returns {T | undefined}
|
||||
*/
|
||||
objectForPrimaryKey<T>(type: string | Realm.ObjectSchema | Function, key: number | string): T | null;
|
||||
objectForPrimaryKey<T>(type: string | Realm.ObjectSchema | Function, key: number | string): T | undefined;
|
||||
|
||||
/**
|
||||
* @param {string|Realm.ObjectType|Function} type
|
||||
|
@ -600,11 +620,6 @@ declare class Realm {
|
|||
* @returns boolean
|
||||
*/
|
||||
compact(): boolean;
|
||||
|
||||
/**
|
||||
* @returns Promise<Results<T>>
|
||||
*/
|
||||
subscribeToObjects<T>(objectType: string, query: string): Promise<Realm.Results<T>>;
|
||||
}
|
||||
|
||||
declare module 'realm' {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "realm",
|
||||
"description": "Realm is a mobile database: an alternative to SQLite and key-value stores",
|
||||
"version": "2.3.0-alpha.11",
|
||||
"version": "2.3.0-alpha.13",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://realm.io",
|
||||
"keywords": [
|
||||
|
|
|
@ -78,6 +78,7 @@ LOCAL_SRC_FILES += src/object-store/src/sync/sync_user.cpp
|
|||
LOCAL_SRC_FILES += src/object-store/src/sync/sync_permission.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/impl/sync_file.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/impl/sync_metadata.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/impl/work_queue.cpp
|
||||
endif
|
||||
|
||||
LOCAL_C_INCLUDES := src
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
"src/object-store/src/sync/impl/sync_client.hpp",
|
||||
"src/object-store/src/sync/impl/sync_file.hpp",
|
||||
"src/object-store/src/sync/impl/sync_metadata.hpp",
|
||||
"src/object-store/src/sync/impl/work_queue.hpp",
|
||||
"src/object-store/src/sync/partial_sync.hpp",
|
||||
"src/object-store/src/sync/sync_config.hpp",
|
||||
"src/object-store/src/sync/sync_manager.hpp",
|
||||
|
@ -147,7 +148,8 @@
|
|||
"src/object-store/src/sync/sync_session.cpp",
|
||||
"src/object-store/src/sync/sync_config.cpp",
|
||||
"src/object-store/src/sync/impl/sync_file.cpp",
|
||||
"src/object-store/src/sync/impl/sync_metadata.cpp"
|
||||
"src/object-store/src/sync/impl/sync_metadata.cpp",
|
||||
"src/object-store/src/sync/impl/work_queue.cpp"
|
||||
],
|
||||
}]
|
||||
],
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
3FCE2A931F58BE0300D4855B /* uuid.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FCE2A911F58BDFF00D4855B /* uuid.cpp */; };
|
||||
3FCE2A971F58BE2200D4855B /* sync_permission.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FCE2A951F58BE1D00D4855B /* sync_permission.cpp */; };
|
||||
420FB79F1F7FBFE900D43D0F /* partial_sync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 423737AF1F7E333400FAEDFF /* partial_sync.cpp */; };
|
||||
425A121120235A1400C2F932 /* work_queue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 425A120F20235A1400C2F932 /* work_queue.cpp */; };
|
||||
4261AF8E203C42000052450D /* work_queue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 425A120F20235A1400C2F932 /* work_queue.cpp */; };
|
||||
502B07E41E2CD201007A84ED /* object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 502B07E31E2CD1FA007A84ED /* object.cpp */; };
|
||||
504CF85E1EBCAE3600A9A4B6 /* network_reachability_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 504CF8541EBCAE3600A9A4B6 /* network_reachability_observer.cpp */; };
|
||||
504CF85F1EBCAE3600A9A4B6 /* system_configuration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 504CF8561EBCAE3600A9A4B6 /* system_configuration.cpp */; };
|
||||
|
@ -185,6 +187,8 @@
|
|||
3FCE2A991F58BE3600D4855B /* feature_checks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = feature_checks.hpp; path = "object-store/src/feature_checks.hpp"; sourceTree = SOURCE_ROOT; };
|
||||
423737AF1F7E333400FAEDFF /* partial_sync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = partial_sync.cpp; path = src/sync/partial_sync.cpp; sourceTree = "<group>"; };
|
||||
423737B01F7E333400FAEDFF /* partial_sync.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = partial_sync.hpp; path = src/sync/partial_sync.hpp; sourceTree = "<group>"; };
|
||||
425A120F20235A1400C2F932 /* work_queue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = work_queue.cpp; sourceTree = "<group>"; };
|
||||
425A121020235A1400C2F932 /* work_queue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = work_queue.hpp; sourceTree = "<group>"; };
|
||||
426FCDFF1F7DA2F9005565DC /* sync_config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_config.cpp; path = src/sync/sync_config.cpp; sourceTree = "<group>"; };
|
||||
502B07E31E2CD1FA007A84ED /* object.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = object.cpp; path = src/object.cpp; sourceTree = "<group>"; };
|
||||
502B07E51E2CD20D007A84ED /* object.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = object.hpp; path = src/object.hpp; sourceTree = "<group>"; };
|
||||
|
@ -474,6 +478,8 @@
|
|||
504CF8521EBCAE3600A9A4B6 /* impl */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
425A120F20235A1400C2F932 /* work_queue.cpp */,
|
||||
425A121020235A1400C2F932 /* work_queue.hpp */,
|
||||
504CF8531EBCAE3600A9A4B6 /* apple */,
|
||||
504CF8581EBCAE3600A9A4B6 /* network_reachability.hpp */,
|
||||
504CF8591EBCAE3600A9A4B6 /* sync_client.hpp */,
|
||||
|
@ -873,6 +879,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4261AF8E203C42000052450D /* work_queue.cpp in Sources */,
|
||||
F63FF2E21C15921A00B3B8E0 /* base64.cpp in Sources */,
|
||||
022BF1021E7266DF00F382F1 /* binding_callback_thread_observer.cpp in Sources */,
|
||||
02414BA51CE6ABCF00A8669F /* collection_change_builder.cpp in Sources */,
|
||||
|
@ -934,6 +941,7 @@
|
|||
F63FF31E1C1642BB00B3B8E0 /* GCDWebServerRequest.m in Sources */,
|
||||
F63FF31F1C1642BB00B3B8E0 /* GCDWebServerResponse.m in Sources */,
|
||||
F63FF3271C1642BB00B3B8E0 /* GCDWebServerStreamedResponse.m in Sources */,
|
||||
425A121120235A1400C2F932 /* work_queue.cpp in Sources */,
|
||||
F63FF3231C1642BB00B3B8E0 /* GCDWebServerURLEncodedFormRequest.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1115,10 +1123,7 @@
|
|||
"-isystem",
|
||||
"$(SRCROOT)/../vendor/sync/include",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = (
|
||||
"$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios-dbg.a",
|
||||
"$(SRCROOT)/../vendor/realm-ios/librealm-ios-dbg.a",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios-dbg.a $(SRCROOT)/../vendor/realm-ios/librealm-ios-dbg.a";
|
||||
PRODUCT_NAME = RealmJS;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
|
@ -1140,10 +1145,7 @@
|
|||
"-isystem",
|
||||
"$(SRCROOT)/../vendor/sync/include",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = (
|
||||
"$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios.a",
|
||||
"$(SRCROOT)/../vendor/realm-ios/librealm-ios.a",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../vendor/realm-ios/librealm-parser-ios.a $(SRCROOT)/../vendor/realm-ios/librealm-ios.a";
|
||||
PRODUCT_NAME = RealmJS;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
#include "js_observable.hpp"
|
||||
|
||||
#include "collection_notifications.hpp"
|
||||
#if REALM_ENABLE_SYNC
|
||||
#include "sync/subscription_state.hpp"
|
||||
#endif
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
|
|
@ -186,9 +186,6 @@ public:
|
|||
static void compact(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_model(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void object_for_object_id(ContextType, ObjectType, Arguments, ReturnValue&);
|
||||
#if REALM_ENABLE_SYNC
|
||||
static void subscribe_to_objects(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
#endif
|
||||
|
||||
// properties
|
||||
static void get_empty(ContextType, ObjectType, ReturnValue &);
|
||||
|
@ -248,7 +245,6 @@ public:
|
|||
{"_objectForObjectId", wrap<object_for_object_id>},
|
||||
#if REALM_ENABLE_SYNC
|
||||
{"_waitForDownload", wrap<wait_for_download_completion>},
|
||||
{"_subscribeToObjects", wrap<subscribe_to_objects>},
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -1061,51 +1057,5 @@ void RealmClass<T>::object_for_object_id(ContextType ctx, ObjectType this_object
|
|||
#endif // REALM_ENABLE_SYNC
|
||||
}
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
template<typename T>
|
||||
void RealmClass<T>::subscribe_to_objects(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_count(3);
|
||||
|
||||
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
std::string object_type = Value::validated_to_string(ctx, args[0]);
|
||||
std::string query = Value::validated_to_string(ctx, args[1]);
|
||||
auto callback = Value::validated_to_function(ctx, args[2]);
|
||||
|
||||
auto &schema = realm->schema();
|
||||
auto object_schema = schema.find(object_type);
|
||||
|
||||
if (object_schema == schema.end()) {
|
||||
throw std::runtime_error("Object type '" + object_type + "' not found in schema.");
|
||||
}
|
||||
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
auto cb = [=](realm::Results results, std::exception_ptr err) {
|
||||
HANDLESCOPE
|
||||
|
||||
if (err) {
|
||||
try {
|
||||
std::rethrow_exception(err);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
ValueType callback_arguments[2];
|
||||
callback_arguments[0] = Value::from_string(protected_ctx, e.what());
|
||||
callback_arguments[1] = Value::from_null(protected_ctx);
|
||||
Function<T>::callback(ctx, protected_callback, protected_this, 2, callback_arguments);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ValueType callback_arguments[2];
|
||||
callback_arguments[0] = Value::from_null(protected_ctx);
|
||||
callback_arguments[1] = ResultsClass<T>::create_instance(protected_ctx, results);
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, callback_arguments);
|
||||
};
|
||||
|
||||
partial_sync::register_query(realm, object_type, query, std::move(cb));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -28,6 +28,11 @@
|
|||
|
||||
#include <realm/parser/parser.hpp>
|
||||
#include <realm/parser/query_builder.hpp>
|
||||
#include <realm/util/optional.hpp>
|
||||
#ifdef REALM_ENABLE_SYNC
|
||||
#include "js_sync.hpp"
|
||||
#include "sync/partial_sync.hpp"
|
||||
#endif
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
@ -82,6 +87,9 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||
static void filtered(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void sorted(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void is_valid(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
#if REALM_ENABLE_SYNC
|
||||
static void subscribe(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
#endif
|
||||
|
||||
static void index_of(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
|
@ -107,6 +115,9 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||
{"filtered", wrap<filtered>},
|
||||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
#if REALM_ENABLE_SYNC
|
||||
{"subscribe", wrap<subscribe>},
|
||||
#endif
|
||||
{"min", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Min>>},
|
||||
{"max", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Max>>},
|
||||
{"sum", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Sum>>},
|
||||
|
@ -271,6 +282,28 @@ void ResultsClass<T>::is_valid(ContextType ctx, ObjectType this_object, Argument
|
|||
return_value.set(get_internal<T, ResultsClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
template<typename T>
|
||||
void ResultsClass<T>::subscribe(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
auto realm = results->get_realm();
|
||||
auto sync_config = realm->config().sync_config;
|
||||
|
||||
util::Optional<std::string> subscription_name;
|
||||
if (args.count == 1) {
|
||||
subscription_name = util::Optional<std::string>(Value::validated_to_string(ctx, args[0]));
|
||||
}
|
||||
else {
|
||||
subscription_name = util::none;
|
||||
}
|
||||
|
||||
auto subscription = partial_sync::subscribe(*results, subscription_name);
|
||||
return_value.set(SubscriptionClass<T>::create_instance(ctx, std::move(subscription)));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
template<typename Fn>
|
||||
void ResultsClass<T>::index_of(ContextType ctx, Fn& fn, Arguments args, ReturnValue &return_value) {
|
||||
|
@ -343,13 +376,13 @@ void ResultsClass<T>::add_listener(ContextType ctx, U& collection, ObjectType th
|
|||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
auto token = collection.add_notification_callback([=](CollectionChangeSet const& change_set, std::exception_ptr exception) {
|
||||
HANDLESCOPE
|
||||
ValueType arguments[] {
|
||||
static_cast<ObjectType>(protected_this),
|
||||
CollectionClass<T>::create_collection_change_set(protected_ctx, change_set)
|
||||
};
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
HANDLESCOPE
|
||||
ValueType arguments[] {
|
||||
static_cast<ObjectType>(protected_this),
|
||||
CollectionClass<T>::create_collection_change_set(protected_ctx, change_set)
|
||||
};
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
collection.m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
||||
}
|
||||
|
||||
|
|
122
src/js_sync.hpp
122
src/js_sync.hpp
|
@ -29,6 +29,7 @@
|
|||
#include "sync/sync_config.hpp"
|
||||
#include "sync/sync_session.hpp"
|
||||
#include "sync/sync_user.hpp"
|
||||
#include "sync/partial_sync.hpp"
|
||||
#include "realm/util/logger.hpp"
|
||||
#include "realm/util/uri.hpp"
|
||||
|
||||
|
@ -555,6 +556,127 @@ void SessionClass<T>::override_server(ContextType ctx, ObjectType this_object, A
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class Subscription : public partial_sync::Subscription {
|
||||
public:
|
||||
Subscription(partial_sync::Subscription s) : partial_sync::Subscription(std::move(s)) {}
|
||||
Subscription(Subscription &&) = default;
|
||||
|
||||
std::vector<std::pair<Protected<typename T::Function>, partial_sync::SubscriptionNotificationToken>> m_notification_tokens;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class SubscriptionClass : public ClassDefinition<T, Subscription<T>> {
|
||||
using GlobalContextType = typename T::GlobalContext;
|
||||
using ContextType = typename T::Context;
|
||||
using FunctionType = typename T::Function;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
using String = js::String<T>;
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using Function = js::Function<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
using Arguments = js::Arguments<T>;
|
||||
|
||||
public:
|
||||
std::string const name = "Subscription";
|
||||
|
||||
static FunctionType create_constructor(ContextType);
|
||||
static ObjectType create_instance(ContextType, partial_sync::Subscription);
|
||||
|
||||
static void get_state(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_error(ContextType, ObjectType, ReturnValue &);
|
||||
|
||||
static void unsubscribe(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void add_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
{"state", {wrap<get_state>, nullptr}},
|
||||
{"error", {wrap<get_error>, nullptr}}
|
||||
};
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
{"unsubscribe", wrap<unsubscribe>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
typename T::Object SubscriptionClass<T>::create_instance(ContextType ctx, partial_sync::Subscription subscription) {
|
||||
return create_object<T, SubscriptionClass<T>>(ctx, new Subscription<T>(std::move(subscription)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::get_state(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(object);
|
||||
return_value.set(static_cast<int8_t>(subscription->state()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::get_error(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(object);
|
||||
if (auto error = subscription->error()) {
|
||||
try {
|
||||
std::rethrow_exception(error);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
return_value.set(e.what());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return_value.set_undefined();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::unsubscribe(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(0);
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(this_object);
|
||||
partial_sync::unsubscribe(*subscription);
|
||||
return_value.set_undefined();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::add_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(this_object);
|
||||
|
||||
auto callback = Value::validated_to_function(ctx, args[0]);
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
auto token = subscription->add_notification_callback([=]() {
|
||||
HANDLESCOPE
|
||||
|
||||
ValueType arguments[2];
|
||||
arguments[0] = static_cast<ObjectType>(protected_this),
|
||||
arguments[1] = Value::from_number(ctx, static_cast<double>(subscription->state()));
|
||||
Function::callback(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
|
||||
subscription->m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubscriptionClass<T>::remove_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(1);
|
||||
auto subscription = get_internal<T, SubscriptionClass<T>>(this_object);
|
||||
|
||||
auto callback = Value::validated_to_function(ctx, args[0]);
|
||||
auto protected_function = Protected<FunctionType>(ctx, callback);
|
||||
|
||||
auto& tokens = subscription->m_notification_tokens;
|
||||
auto compare = [&](auto&& token) {
|
||||
return typename Protected<FunctionType>::Comparator()(token.first, protected_function);
|
||||
};
|
||||
tokens.erase(std::remove_if(tokens.begin(), tokens.end(), compare), tokens.end());
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
class SyncClass : public ClassDefinition<T, void*> {
|
||||
using GlobalContextType = typename T::GlobalContext;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 56f246831c6bd1bd33cf0f852d049b11e94e50e9
|
||||
Subproject commit d5fe9a626c0adc0345d128201e0df8bb4004725d
|
|
@ -44,7 +44,7 @@ var TESTS = {
|
|||
MigrationTests: require('./migration-tests'),
|
||||
EncryptionTests: require('./encryption-tests'),
|
||||
ObjectIDTests: require('./object-id-tests'),
|
||||
// GarbageCollectionTests: require('./garbage-collection'),
|
||||
// Garbagecollectiontests: require('./garbage-collection'),
|
||||
};
|
||||
|
||||
// If sync is enabled, run the sync tests
|
||||
|
@ -55,7 +55,7 @@ if (global.enableSyncTests) {
|
|||
// FIXME: Permission tests currently fail in chrome debugging mode.
|
||||
if (typeof navigator === 'undefined' ||
|
||||
!/Chrome/.test(navigator.userAgent)) { // eslint-disable-line no-undef
|
||||
TESTS.PermissionTests = require('./permission-tests');
|
||||
TESTS.PermissionTests = require('./permission-tests');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1223,4 +1223,83 @@ module.exports = {
|
|||
TestCase.assertThrowsContaining(() => object.list.avg(),
|
||||
"JS value must be of type 'string', got (undefined)");
|
||||
},
|
||||
|
||||
testListNested: function() {
|
||||
const realm = new Realm({schema: [schemas.ParentObject, schemas.NameObject]});
|
||||
realm.write(() => {
|
||||
realm.create('ParentObject', {
|
||||
id: 1,
|
||||
name: [
|
||||
{ family: 'Larsen', given: ['Hans', 'Jørgen'], prefix: [] },
|
||||
{ family: 'Hansen', given: ['Ib'], prefix: [] }
|
||||
]
|
||||
});
|
||||
realm.create('ParentObject', {
|
||||
id: 2,
|
||||
name: [
|
||||
{family: 'Petersen', given: ['Gurli', 'Margrete'], prefix: [] }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
let objects = realm.objects('ParentObject');
|
||||
TestCase.assertEqual(objects.length, 2);
|
||||
TestCase.assertEqual(objects[0].name.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].given.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
||||
TestCase.assertEqual(objects[0].name[0].given[1], 'Jørgen')
|
||||
TestCase.assertEqual(objects[0].name[1].given.length, 1);
|
||||
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
||||
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
||||
|
||||
TestCase.assertEqual(objects[1].name.length, 1);
|
||||
TestCase.assertEqual(objects[1].name[0].given.length, 2);
|
||||
TestCase.assertEqual(objects[1].name[0].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[1].name[0].given[0], 'Gurli');
|
||||
TestCase.assertEqual(objects[1].name[0].given[1], 'Margrete');
|
||||
},
|
||||
|
||||
testListNestedFromJSON: function() {
|
||||
let json = '{"id":1, "name": [{ "family": "Larsen", "given": ["Hans", "Jørgen"], "prefix": [] }, { "family": "Hansen", "given": ["Ib"], "prefix": [] }] }';
|
||||
let parent = JSON.parse(json);
|
||||
const realm = new Realm({schema: [schemas.ParentObject, schemas.NameObject]});
|
||||
realm.write(() => {
|
||||
realm.create('ParentObject', parent);
|
||||
});
|
||||
|
||||
let objects = realm.objects('ParentObject');
|
||||
TestCase.assertEqual(objects.length, 1);
|
||||
TestCase.assertEqual(objects[0].name.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].given.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
||||
TestCase.assertEqual(objects[0].name[0].given[1], 'Jørgen');
|
||||
|
||||
TestCase.assertEqual(objects[0].name[1].given.length, 1);
|
||||
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
||||
},
|
||||
|
||||
testMultipleLists: function() {
|
||||
const realm = new Realm({schema: [schemas.MultiListObject]});
|
||||
realm.write(() => {
|
||||
realm.create('MultiListObject', { id: 0, list1: ["Hello"], list2: ["World"] });
|
||||
realm.create('MultiListObject', { id: 1, list1: ["Foo"], list2: ["Bar"] });
|
||||
});
|
||||
|
||||
let objects = realm.objects('MultiListObject');
|
||||
TestCase.assertEqual(objects.length, 2);
|
||||
TestCase.assertEqual(objects[0].id, 0);
|
||||
TestCase.assertEqual(objects[0].list1.length, 1);
|
||||
TestCase.assertEqual(objects[0].list1[0], "Hello");
|
||||
TestCase.assertEqual(objects[0].list2.length, 1);
|
||||
TestCase.assertEqual(objects[0].list2[0], "World");
|
||||
|
||||
TestCase.assertEqual(objects[1].id, 1);
|
||||
TestCase.assertEqual(objects[1].list1.length, 1);
|
||||
TestCase.assertEqual(objects[1].list1[0], "Foo");
|
||||
TestCase.assertEqual(objects[1].list2.length, 1);
|
||||
TestCase.assertEqual(objects[1].list2[0], "Bar");
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
This script creates new nested objects into a new Realm.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
console.log("nested-list-helper started");
|
||||
const username = process.argv[3];
|
||||
const realmName = process.argv[4];
|
||||
const realmModule = process.argv[5];
|
||||
|
||||
const Realm = require(realmModule);
|
||||
let schemas = require(process.argv[2]);
|
||||
|
||||
function createObjects(user) {
|
||||
const config = {
|
||||
sync: { user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
error: err => console.log(err)
|
||||
},
|
||||
schema: [schemas.ParentObject, schemas.NameObject],
|
||||
};
|
||||
|
||||
const realm = new Realm(config);
|
||||
|
||||
realm.write(() => {
|
||||
realm.create('ParentObject', {
|
||||
id: 1,
|
||||
name: [
|
||||
{ family: 'Larsen', given: ['Hans', 'Jørgen'], prefix: [] },
|
||||
{ family: 'Hansen', given: ['Ib'], prefix: [] }
|
||||
]
|
||||
});
|
||||
realm.create('ParentObject', {
|
||||
id: 2,
|
||||
name: [
|
||||
{family: 'Petersen', given: ['Gurli', 'Margrete'], prefix: [] }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
console.log("JSON: " + JSON.stringify(realm.objects('ParentObject')));
|
||||
|
||||
let session = realm.syncSession;
|
||||
return new Promise((resolve, reject) => {
|
||||
let callback = (transferred, total) => {
|
||||
if (transferred === total) {
|
||||
session.removeProgressNotification(callback);
|
||||
resolve(realm);
|
||||
}
|
||||
}
|
||||
session.addProgressNotification('upload', 'forCurrentlyOutstandingWork', callback);
|
||||
});
|
||||
}
|
||||
|
||||
let registrationError;
|
||||
Realm.Sync.User.register('http://localhost:9080', username, 'password')
|
||||
.catch((error) => {
|
||||
registrationError = JSON.stringify(error);
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password')
|
||||
})
|
||||
.catch((error) => {
|
||||
const loginError = JSON.stringify(error);
|
||||
console.error(`nested-list-helper failed:\n User.register() error:\n${registrationError}\n User.login() error:\n${registrationError}`);
|
||||
process.exit(-2);
|
||||
})
|
||||
.then((user) => createObjects(user))
|
||||
.then(() => process.exit(0));
|
|
@ -280,4 +280,30 @@ exports.LinkingObjectsObject = {
|
|||
links: 'LinkingObjectsObject[]',
|
||||
linkingObjects: {type: 'linkingObjects', objectType: 'LinkingObjectsObject', property: 'links'}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.ParentObject = {
|
||||
name: 'ParentObject',
|
||||
properties: {
|
||||
id: 'int',
|
||||
name: 'NameObject[]'
|
||||
}
|
||||
};
|
||||
|
||||
exports.NameObject = {
|
||||
name: 'NameObject',
|
||||
properties: {
|
||||
family: 'string',
|
||||
given: 'string[]',
|
||||
prefix: 'string[]'
|
||||
}
|
||||
};
|
||||
|
||||
exports.MultiListObject = {
|
||||
name: 'MultiListObject',
|
||||
properties: {
|
||||
'id': 'int',
|
||||
'list1': 'string[]',
|
||||
'list2': 'string[]'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
const Realm = require('realm');
|
||||
const TestCase = require('./asserts');
|
||||
let schemas = require('./schemas');
|
||||
|
||||
const isNodeProccess = (typeof process === 'object' && process + '' === '[object process]');
|
||||
|
||||
|
@ -402,6 +403,49 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
testListNestedSync() {
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/nested-list-helper.js', __dirname + '/schemas.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let config = {
|
||||
schema: [schemas.ParentObject, schemas.NameObject],
|
||||
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
|
||||
};
|
||||
Realm.open(config).then(realm => {
|
||||
let objects = realm.objects('ParentObject');
|
||||
|
||||
let json = JSON.stringify(objects);
|
||||
TestCase.assertEqual(json, '{"0":{"id":1,"name":{"0":{"family":"Larsen","given":{"0":"Hans","1":"Jørgen"},"prefix":{}},"1":{"family":"Hansen","given":{"0":"Ib"},"prefix":{}}}},"1":{"id":2,"name":{"0":{"family":"Petersen","given":{"0":"Gurli","1":"Margrete"},"prefix":{}}}}}');
|
||||
TestCase.assertEqual(objects.length, 2);
|
||||
TestCase.assertEqual(objects[0].name.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].given.length, 2);
|
||||
TestCase.assertEqual(objects[0].name[0].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[0].name[0].given[0], 'Hans');
|
||||
TestCase.assertEqual(objects[0].name[0].given[1], 'Jørgen')
|
||||
TestCase.assertEqual(objects[0].name[1].given.length, 1);
|
||||
TestCase.assertEqual(objects[0].name[1].given[0], 'Ib');
|
||||
TestCase.assertEqual(objects[0].name[1].prefix.length, 0);
|
||||
|
||||
TestCase.assertEqual(objects[1].name.length, 1);
|
||||
TestCase.assertEqual(objects[1].name[0].given.length, 2);
|
||||
TestCase.assertEqual(objects[1].name[0].prefix.length, 0);
|
||||
TestCase.assertEqual(objects[1].name[0].given[0], 'Gurli');
|
||||
TestCase.assertEqual(objects[1].name[0].given[1], 'Margrete');
|
||||
resolve();
|
||||
}).catch(() => reject());
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testIncompatibleSyncedRealmOpen() {
|
||||
let realm = "sync-v1.realm";
|
||||
if (isNodeProccess) {
|
||||
|
@ -523,7 +567,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmConstructor() {
|
||||
/* testProgressNotificationsForRealmConstructor() {
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
@ -555,7 +599,7 @@ module.exports = {
|
|||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
},*/
|
||||
|
||||
testProgressNotificationsUnregisterForRealmConstructor() {
|
||||
if (!isNodeProccess) {
|
||||
|
@ -707,8 +751,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
/* Disabled: waiting for new implementation
|
||||
testPartialSync() {
|
||||
testPartialSyncAnonymous_SubscriptionListener() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
|
@ -733,14 +776,205 @@ module.exports = {
|
|||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
return realm.subscribeToObjects("Dog", "name == 'Lassy 1'").then(results => {
|
||||
TestCase.assertEqual(results.length, 1);
|
||||
TestCase.assertTrue(results[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
subscription.addListener((subscription, state) => {
|
||||
if (state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(results.length, 1);
|
||||
TestCase.assertTrue(results[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
})
|
||||
})
|
||||
},
|
||||
*/
|
||||
|
||||
testPartialSyncAnonymous_ResultsListener() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
results.addListener((collection, changes) => {
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
testPartialSyncMultipleSubscriptions() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
var results1 = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var results2 = realm.objects('Dog').filtered("name == 'Lassy 2'");
|
||||
var subscription1 = results1.subscribe();
|
||||
var subscription2 = results2.subscribe();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let called1 = false;
|
||||
let called2 = false;
|
||||
results1.addListener((collection, changeset) => {
|
||||
if (subscription1.state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 1', "The object is not synced correctly");
|
||||
called1 = true;
|
||||
if (called1 && called2) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
results2.addListener((collection, changeset) => {
|
||||
if (subscription2.state == Realm.Sync.SubscriptionState.Complete) {
|
||||
TestCase.assertEqual(collection.length, 1);
|
||||
TestCase.assertTrue(collection[0].name === 'Lassy 2', "The object is not synced correctly");
|
||||
called2 = true;
|
||||
if (called1 && called2) {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
testPartialSyncFailing() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: false, // <---- calling subscribe should fail
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
TestCase.assertEqual(realm.objects('Dog').length, 0);
|
||||
TestCase.assertThrows(function () { var subscription = realm.objects('Dog').filtered("name == 'Lassy 1'").subscribe(); } );
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testPartialSyncUnsubscribe() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
let config = {
|
||||
sync: {
|
||||
user: user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
partial: true,
|
||||
error: (session, error) => console.log(error)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
Realm.deleteFile(config);
|
||||
const realm = new Realm(config);
|
||||
var results = realm.objects('Dog').filtered("name == 'Lassy 1'");
|
||||
var subscription = results.subscribe();
|
||||
TestCase.assertEqual(subscription.state, Realm.Sync.SubscriptionState.Creating);
|
||||
return new Promise((resolve, reject) => {
|
||||
results.addListener((collection, changes) => {
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Complete) {
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
if (subscription.state === Realm.Sync.SubscriptionState.Invalidated) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
reject("listener never called");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testClientReset() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
|
|
Loading…
Reference in New Issue