Merge branch 'master' of github.com:realm/realm-js into yg/windows-sync
# Conflicts: # binding.gyp # src/object-store
This commit is contained in:
commit
2ac8160b32
|
@ -1,20 +1,11 @@
|
|||
<!--
|
||||
Make sure to assign one and only one Type (`T:`) and State (`S:`) label.
|
||||
Select reviewers if ready for review. Our bot will automatically assign you.
|
||||
-->
|
||||
<!-- Make sure to assign one and only one Type (`T:`) and State (`S:`) label.
|
||||
Select reviewers if ready for review. Our bot will automatically assign you. -->
|
||||
|
||||
## What, How & Why?
|
||||
<!-- Describe the changes and give some hints to guide your reviewers if possible. -->
|
||||
<!-- E.g. reference to other repos: This closes realm/realm-sync#??? -->
|
||||
|
||||
<!--
|
||||
Describe the changes and give some hints to guide your reviewers if possible.
|
||||
-->
|
||||
|
||||
Reference to the issue(s) addressed by this PR: # ???
|
||||
|
||||
<!--
|
||||
- This fixes #???
|
||||
- This closes realm/realm-sync#???
|
||||
-->
|
||||
This closes # ???
|
||||
|
||||
## ☑️ ToDos
|
||||
<!-- Add your own todos here -->
|
||||
|
|
101
CHANGELOG.md
101
CHANGELOG.md
|
@ -1,16 +1,103 @@
|
|||
2.0.0 Release notes (2017-9-29)
|
||||
2.0.0-rc20 Release notes (2017-10-11)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None
|
||||
|
||||
### Enhancements
|
||||
* Added `update` method to `Realm.Results` to support bulk updates (#808).
|
||||
* Added support for aggregate functions on `Realm.Results` and `Realm.List` of primitive types.
|
||||
|
||||
### Bug fixes
|
||||
* Avoid closing then reopening a sync session when using `Realm.open` (#1391).
|
||||
* Respect custom Realm paths when using `Realm.open` (#1392 and #1393).
|
||||
|
||||
### Internal
|
||||
* Upgrading to Realm Sync 2.0.0-rc28.
|
||||
* Upgrading to Realm Object Server 2.0.0-rc.4.
|
||||
* OpenSSL for Android is distributed in a separate package, and the build system needed updates to accommendate this.
|
||||
* Added `-fvisibility=hidden` to Android builds (reduces size of `.so` file).
|
||||
|
||||
|
||||
2.0.0-rc19 Release notes (2017-10-7)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* None
|
||||
|
||||
### Bug fixes
|
||||
* None
|
||||
|
||||
### Internal
|
||||
* Update object store libs to fix partial sync
|
||||
|
||||
1.13.0 Release notes (2017-10-5)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
||||
### Enhancements
|
||||
* Add a callback function used to verify SSL certificates in the sync config.
|
||||
* Added aggregate functions `min()`, `max()`, `sum()`, and `avg()` to `Realm.Results` and `Realm.List` (#807).
|
||||
* Added `deleteRealmIfMigrationNeeded` to configuration to delete a Realm if migration needed (#502).
|
||||
|
||||
### Bug fixes
|
||||
* Fixed port conflict between RN >= 0.48 inspector proxy and RPC server used for Chrome debugging (#1294).
|
||||
* Workaround for RN >= 0.49 metro-bundler check for single string literal argument to `require()` (#1342)
|
||||
|
||||
2.0.0-rc18 Release notes (2017-10-4)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* Deprecate node 4 and node 5 support
|
||||
|
||||
### Enhancements
|
||||
* None
|
||||
|
||||
### Bug fixes
|
||||
* Fixed bug in `Realm.subscribeToObjects()`.
|
||||
|
||||
### Internal
|
||||
* None
|
||||
|
||||
2.0.0-rc17 Release notes (2017-10-3)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* Removed `setAccessToken()`; use `setFeatureToken()` instead.
|
||||
* During iteration (`for ... of`) of `Realm.Results`, the results will be frozen using the `snapshot()` method (#1366).
|
||||
|
||||
### Enhancements
|
||||
* Support migration from Realms sync 1.0 to sync 2.0 versions
|
||||
* Handling of the situation when the client has to reset the Realm due to diverging histories (#795).
|
||||
* Added `Realm.subscribeToObjects()` to listen for changes in partially synced Realms.
|
||||
|
||||
### Bug fixes
|
||||
* None.
|
||||
|
||||
### Internal
|
||||
* Upgraded to Realm Sync 2.0.0-rc27.
|
||||
|
||||
|
||||
2.0.0-rc16 Release notes (2017-9-29)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* Upgtading to Realm Core 4.0.1 (bug fixes)
|
||||
* Upgrading to Realm Sync 2.0.0-rc26 (sync protocol 22 + bug fixes)
|
||||
|
||||
|
||||
2.0.0-rc14 Release notes (2017-9-29)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* Upgrading to Realm Core 4.0.0 and Realm Sync 2.0.0-rc25.
|
||||
|
||||
### Enhancements
|
||||
* None.
|
||||
* None
|
||||
|
||||
### Bug fixes
|
||||
* Configuration of sync file system is not done on module import but later when actually needed by sync (#1351)
|
||||
|
||||
|
||||
2.0.0 Release notes (2017-9-28)
|
||||
2.0.0-rc12 Release notes (2017-9-28)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None.
|
||||
|
@ -27,19 +114,11 @@
|
|||
### Breaking changes
|
||||
* None
|
||||
|
||||
### Enhancements
|
||||
* Add a callback function used to verify SSL certificates in the sync config.
|
||||
|
||||
### Bug fixes
|
||||
* Fixed port conflict between RN >= 0.48 inspector proxy and RPC server used for Chrome debugging (#1294).
|
||||
|
||||
### Internal
|
||||
* Alignment of permission schemas.
|
||||
* Updating sync (2.0.0-rc24).
|
||||
|
||||
|
||||
|
||||
|
||||
2.0.0-rc10 Release notes (2017-9-19)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
|
|
|
@ -175,7 +175,24 @@ def doAndroidBuild(target, postStep = null) {
|
|||
def doDockerBuild(target, postStep = null) {
|
||||
return {
|
||||
node('docker') {
|
||||
doDockerInside("./scripts/docker-wrapper.sh ./scripts/test.sh", target, postStep)
|
||||
deleteDir()
|
||||
unstash 'source'
|
||||
|
||||
try {
|
||||
reportStatus(target, 'PENDING', 'Build has started')
|
||||
|
||||
docker.image('node:6').inside('-e HOME=/tmp') {
|
||||
sh "scripts/test.sh ${target}"
|
||||
if(postStep) {
|
||||
postStep.call()
|
||||
}
|
||||
deleteDir()
|
||||
reportStatus(target, 'SUCCESS', 'Success!')
|
||||
}
|
||||
} catch(Exception e) {
|
||||
reportStatus(target, 'FAILURE', e.toString())
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
PACKAGE_NAME=realm-js
|
||||
VERSION=2.0.0-rc14
|
||||
REALM_CORE_VERSION=4.0.0
|
||||
REALM_SYNC_VERSION=2.0.0-rc25
|
||||
REALM_OBJECT_SERVER_VERSION=2.0.0-alpha.36
|
||||
VERSION=2.0.0-rc20
|
||||
REALM_CORE_VERSION=4.0.1
|
||||
REALM_SYNC_VERSION=2.0.0-rc28
|
||||
REALM_OBJECT_SERVER_VERSION=2.0.0-rc.4
|
||||
|
|
|
@ -24,6 +24,11 @@
|
|||
* accessed in any of the ways that a normal Javascript Array can, including
|
||||
* subscripting, enumerating with `for-of` and so on.
|
||||
*
|
||||
* A Collection always reflect the current state of the Realm. The one exception to this is
|
||||
* when using `for...in` or `for...of` enumeration, which will always enumerate over the
|
||||
* objects which matched the query when the enumeration is begun, even if some of them are
|
||||
* deleted or modified to be excluded by the filter during the enumeration.
|
||||
*
|
||||
* @memberof Realm
|
||||
* @since 0.11.0
|
||||
*/
|
||||
|
@ -233,6 +238,64 @@ class Collection {
|
|||
*/
|
||||
indexOf(object) {}
|
||||
|
||||
/**
|
||||
* Returns the minimum value of the values in the collection or of the
|
||||
* given property among all the objects in the collection, or `undefined`
|
||||
* if the collection is empty.
|
||||
*
|
||||
* Only supported for int, float, double and date properties. `null` values
|
||||
* are ignored entirely by this method and will not be returned.
|
||||
*
|
||||
* @param {string} [property] - For a collection of objects, the property to take the minimum of.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric/date.
|
||||
* @returns {number} the minimum value.
|
||||
* @since 1.12.1
|
||||
*/
|
||||
min(property) {}
|
||||
|
||||
/**
|
||||
* Returns the maximum value of the values in the collection or of the
|
||||
* given property among all the objects in the collection, or `undefined`
|
||||
* if the collection is empty.
|
||||
*
|
||||
* Only supported for int, float, double and date properties. `null` values
|
||||
* are ignored entirely by this method and will not be returned.
|
||||
*
|
||||
* @param {string} [property] - For a collection of objects, the property to take the maximum of.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric/date.
|
||||
* @returns {number} the maximum value.
|
||||
* @since 1.12.1
|
||||
*/
|
||||
max(property) {}
|
||||
|
||||
/**
|
||||
* Computes the sum of the values in the collection or of the given
|
||||
* property among all the objects in the collection, or 0 if the collection
|
||||
* is empty.
|
||||
*
|
||||
* Only supported for int, float and double properties. `null` values are
|
||||
* ignored entirely by this method.
|
||||
* @param {string} [property] - For a collection of objects, the property to take the sum of.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric.
|
||||
* @returns {number} the sum.
|
||||
* @since 1.12.1
|
||||
*/
|
||||
sum(property) {}
|
||||
|
||||
/**
|
||||
* Computes the average of the values in the collection or of the given
|
||||
* property among all the objects in the collection, or `undefined` if the collection
|
||||
* is empty.
|
||||
*
|
||||
* Only supported for int, float and double properties. `null` values are
|
||||
* ignored entirely by this method and will not be factored into the average.
|
||||
* @param {string} [property] - For a collection of objects, the property to take the average of.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric.
|
||||
* @returns {number} the sum.
|
||||
* @since 1.12.1
|
||||
*/
|
||||
avg(property) {}
|
||||
|
||||
/**
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach Array.prototype.forEach}
|
||||
* @param {function} callback - Function to execute on each object in the collection.
|
||||
|
|
|
@ -88,6 +88,7 @@ class Realm {
|
|||
* migrated to use the new schema.
|
||||
* @param {Realm~Configuration} [config] - **Required** when first creating the Realm.
|
||||
* @throws {Error} If anything in the provided `config` is invalid.
|
||||
* @throws {IncompatibleSyncedRealmError} when an incompatible synced Realm is opened
|
||||
*/
|
||||
constructor(config) {}
|
||||
|
||||
|
@ -105,7 +106,8 @@ class Realm {
|
|||
* @param {Realm~Configuration} config
|
||||
* @param {callback(error, realm)} - will be called when the Realm is ready.
|
||||
* @param {callback(transferred, transferable)} [progressCallback] - an optional callback for download progress notifications
|
||||
* @throws {Error} If anything in the provided `config` is invalid.
|
||||
* @throws {Error} If anything in the provided `config` is invalid
|
||||
* @throws {IncompatibleSyncedRealmError} when an incompatible synced Realm is opened
|
||||
*/
|
||||
static openAsync(config, callback, progressCallback) {}
|
||||
|
||||
|
@ -214,7 +216,7 @@ class Realm {
|
|||
*/
|
||||
cancelTransaction() {}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Replaces all string columns in this Realm with a string enumeration column and compacts the
|
||||
* database file.
|
||||
*
|
||||
|
@ -231,6 +233,17 @@ 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) {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -269,6 +282,8 @@ Realm.defaultPath;
|
|||
* This function takes two arguments:
|
||||
* - `oldRealm` - The Realm before migration is performed.
|
||||
* - `newRealm` - The Realm that uses the latest `schema`, which should be modified as necessary.
|
||||
* @property {boolean} [deleteRealmIfMigrationNeeded=false] - Specifies if this Realm should be deleted
|
||||
* if a migration is needed.
|
||||
* @property {callback(number, number)} [shouldCompactOnLaunch] - The function called when opening
|
||||
* a Realm for the first time during the life of a process to determine if it should be compacted
|
||||
* before being returned to the user. The function takes two arguments:
|
||||
|
@ -331,7 +346,10 @@ Realm.defaultPath;
|
|||
* The purpose of open_ssl_verify_callback is to enable custom certificate handling and to solve cases where
|
||||
* OpenSSL erroneously rejects valid certificates possibly because OpenSSL doesn't have access to the
|
||||
* proper trust certificates.
|
||||
*
|
||||
* - `partial` - Whether this Realm should be opened in 'partial synchronization' mode.
|
||||
* Partial synchronisation only synchronizes those objects that match the query specified in contrast
|
||||
* to the normal mode of operation that synchronises all objects in a remote Realm.
|
||||
* **Partial synchronization is a tech preview. Its APIs are subject to change.**
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,4 +28,12 @@
|
|||
* @memberof Realm
|
||||
*/
|
||||
class Results extends Collection {
|
||||
/**
|
||||
* Bulk update objects in the collection.
|
||||
* @param {string} property - The name of the property.
|
||||
* @param {string} value - The updated property value.
|
||||
* @throws {Error} If no property with the name exists.
|
||||
* @since 2.0.0-rc20
|
||||
*/
|
||||
update(property, value) {}
|
||||
}
|
||||
|
|
49
docs/sync.js
49
docs/sync.js
|
@ -17,6 +17,13 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* When opening a Realm created with Realm Mobile Platform v1.x, it is automatically
|
||||
* migrated to the v2.x format. In case this migration
|
||||
* is not possible, an exception is thrown. The exception´s `message` property will be equal
|
||||
* to `IncompatibleSyncedRealmException`. The Realm is backed up, and the property `configuration`
|
||||
* is a {Realm~Configuration} which refers to it. You can open it as a local, read-only Realm, and
|
||||
* copy objects to a new synced Realm.
|
||||
*
|
||||
* @memberof Realm
|
||||
*/
|
||||
class Sync {
|
||||
|
@ -24,9 +31,9 @@ class Sync {
|
|||
* Add a sync listener to listen to changes across multiple Realms
|
||||
* @param {string} server_url - the sync server to listen to
|
||||
* @param {SyncUser} admin_user - an admin user obtained by calling `new Realm.Sync.User.adminUser`
|
||||
* @param {string} regex - a regular expression used to determine which cahnged Realms should trigger events -
|
||||
* @param {string} regex - a regular expression used to determine which changed Realms should trigger events -
|
||||
* Use `.*` to match all all Realms
|
||||
* @param {string} name - The name of event that should cause the callback to be called
|
||||
* @param {string} name - The name of the event that should trigger the callback to be called
|
||||
* _Currently only the 'change' event is supported_
|
||||
* @param {function(change_event)} change_callback - called when changes are made to any Realm which
|
||||
* match the given regular expression
|
||||
|
@ -55,11 +62,26 @@ class Sync {
|
|||
*/
|
||||
static setLogLevel(log_level) {}
|
||||
|
||||
|
||||
/**
|
||||
* Initiate a client reset. The Realm must be closed prior to the reset.
|
||||
* @param {string} [path] - The path to the Realm to reset.
|
||||
* Throws error if reset is not possible.
|
||||
* @example
|
||||
* {
|
||||
* const config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
|
||||
* config.sync.error = (sender, error) => {
|
||||
* if (error.code === 7) { // 7 -> client reset
|
||||
* Realm.Sync.initiateClientReset(original_path);
|
||||
* // copy required objects from Realm at error.config.path
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
static initiateClientReset(path) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change info passed when receiving sync 'change' events
|
||||
* Change information passed when receiving sync 'change' events
|
||||
* @memberof Realm.Sync
|
||||
*/
|
||||
class ChangeEvent {
|
||||
|
@ -122,6 +144,23 @@ class AuthError extends Error {
|
|||
get type() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes an error when an incompatible synced Realm is opened. The old version of the Realm can be accessed in readonly mode using the configuration() member
|
||||
* @memberof Realm.Sync
|
||||
*/
|
||||
class IncompatibleSyncedRealmError {
|
||||
/**
|
||||
* The name of the error is 'IncompatibleSyncedRealmError'
|
||||
*/
|
||||
get name() {}
|
||||
|
||||
/**
|
||||
* The {Realm~Configuration} of the backed up Realm.
|
||||
* @type {Realm~Configuration}
|
||||
*/
|
||||
get configuration() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for logging in and managing Sync users.
|
||||
* @memberof Realm.Sync
|
||||
|
@ -248,7 +287,7 @@ class User {
|
|||
* @param {string} recipient the optional recipient of the permission. Can be either
|
||||
* 'any' which is the default, or 'currentUser' or 'otherUser' if you want only permissions
|
||||
* belonging to the user or *not* belonging to the user.
|
||||
* @returns {Results} a queryable collection of permission objects that provides detailed
|
||||
* @returns {Promise} a Promise with a queryable collection of permission objects that provides detailed
|
||||
* information regarding the granted access.
|
||||
* The collection is a live query similar to what you would get by callig Realm.objects,
|
||||
* so the same features apply - you can listen for notifications or filter it.
|
||||
|
|
|
@ -60,6 +60,7 @@ function setupRealm(realm, realmId) {
|
|||
'schemaVersion',
|
||||
'syncSession',
|
||||
'isInTransaction',
|
||||
'subscribeToObjects',
|
||||
].forEach((name) => {
|
||||
Object.defineProperty(realm, name, {get: util.getterForProperty(name)});
|
||||
});
|
||||
|
@ -127,6 +128,7 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
|||
'removeListener',
|
||||
'removeAllListeners',
|
||||
'close',
|
||||
'_waitForDownload',
|
||||
]);
|
||||
|
||||
// Mutating methods:
|
||||
|
@ -188,12 +190,6 @@ Object.defineProperties(Realm, {
|
|||
rpc.clearTestState();
|
||||
},
|
||||
},
|
||||
_waitForDownload: {
|
||||
value: function(_config, sessionCallback, callback) {
|
||||
sessionCallback();
|
||||
callback();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
for (let i = 0, len = debugHosts.length; i < len; i++) {
|
||||
|
|
|
@ -32,6 +32,10 @@ createMethods(List.prototype, objectTypes.LIST, [
|
|||
'snapshot',
|
||||
'isValid',
|
||||
'indexOf',
|
||||
'min',
|
||||
'max',
|
||||
'sum',
|
||||
'avg',
|
||||
'addListener',
|
||||
'removeListener',
|
||||
'removeAllListeners',
|
||||
|
|
|
@ -25,17 +25,27 @@ import { createMethods } from './util';
|
|||
export default class Results extends Collection {
|
||||
}
|
||||
|
||||
// Non-mutating methods:
|
||||
createMethods(Results.prototype, objectTypes.RESULTS, [
|
||||
'filtered',
|
||||
'sorted',
|
||||
'snapshot',
|
||||
'isValid',
|
||||
'indexOf',
|
||||
'min',
|
||||
'max',
|
||||
'sum',
|
||||
'avg',
|
||||
'addListener',
|
||||
'removeListener',
|
||||
'removeAllListeners',
|
||||
]);
|
||||
|
||||
// Mutating methods:
|
||||
createMethods(Results.prototype, objectTypes.RESULTS, [
|
||||
'update',
|
||||
], true);
|
||||
|
||||
export function createResults(realmId, info) {
|
||||
return createCollection(Results.prototype, realmId, info);
|
||||
}
|
||||
|
|
|
@ -212,6 +212,23 @@ function makeRequest(url, data) {
|
|||
return JSON.parse(responseText);
|
||||
}
|
||||
|
||||
//returns an object from rpc serialized json value
|
||||
function deserialize_json_value(value) {
|
||||
let result = {};
|
||||
for (let index = 0; index < value.keys.length; index++) {
|
||||
var propName = value.keys[index];
|
||||
var propValue = value.values[index];
|
||||
if (propValue.type && propValue.type == 'dict') {
|
||||
result[propName] = deserialize_json_value(propValue);
|
||||
}
|
||||
else {
|
||||
result[propName] = propValue.value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function sendRequest(command, data, host = sessionHost) {
|
||||
if (!host) {
|
||||
throw new Error('Must first create RPC session with a valid host');
|
||||
|
@ -226,9 +243,21 @@ function sendRequest(command, data, host = sessionHost) {
|
|||
let error = response && response.error;
|
||||
|
||||
// Remove the type prefix from the error message (e.g. "Error: ").
|
||||
if (error) {
|
||||
if (error && error.replace) {
|
||||
error = error.replace(/^[a-z]+: /i, '');
|
||||
}
|
||||
else if (error.type && error.type === 'dict') {
|
||||
const responseError = deserialize_json_value(error);
|
||||
let responeMessage;
|
||||
if (response.message && response.message !== '') {
|
||||
// Remove the type prefix from the error message (e.g. "Error: ").
|
||||
responeMessage = response.message.replace(/^[a-z]+: /i, '');
|
||||
}
|
||||
|
||||
const exceptionToReport = new Error(responeMessage);
|
||||
Object.assign(exceptionToReport, responseError);
|
||||
throw exceptionToReport;
|
||||
}
|
||||
|
||||
throw new Error(error || `Invalid response for "${command}"`);
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ Object.defineProperty(iteratorPrototype, Symbol.iterator, {
|
|||
|
||||
['entries', 'keys', 'values'].forEach(function(methodName) {
|
||||
var method = function() {
|
||||
var self = this;
|
||||
var self = this.snapshot();
|
||||
var index = 0;
|
||||
|
||||
return Object.create(iteratorPrototype, {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
function AuthError(problem) {
|
||||
const error = Error.call(this, problem.title);
|
||||
|
||||
|
||||
this.name = 'AuthError';
|
||||
this.message = error.message;
|
||||
this.stack = error.stack;
|
||||
|
|
|
@ -31,6 +31,25 @@ function setConstructorOnPrototype(klass) {
|
|||
}
|
||||
}
|
||||
|
||||
// Return a configuration usable by `Realm.open` when waiting for a download.
|
||||
// It must have caching disabled, and no schema or schema version specified.
|
||||
function waitForDownloadConfig(config) {
|
||||
if (!config) {
|
||||
return {_cache: false};
|
||||
}
|
||||
|
||||
if (typeof config == 'string') {
|
||||
return {path: config, _cache: false};
|
||||
}
|
||||
|
||||
if (typeof config == 'object') {
|
||||
return Object.assign({}, config, {schema: undefined, schemaVersion: undefined, _cache: false});
|
||||
}
|
||||
|
||||
// Unknown type. Pass the config through.
|
||||
return config;
|
||||
}
|
||||
|
||||
module.exports = function(realmConstructor) {
|
||||
// Add the specified Array methods to the Collection prototype.
|
||||
Object.defineProperties(realmConstructor.Collection.prototype, require('./collection-methods'));
|
||||
|
@ -45,7 +64,8 @@ module.exports = function(realmConstructor) {
|
|||
open(config) {
|
||||
let syncSession;
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
realmConstructor._waitForDownload(config,
|
||||
let realm = new realmConstructor(waitForDownloadConfig(config));
|
||||
realm._waitForDownload(
|
||||
(session) => {
|
||||
syncSession = session;
|
||||
},
|
||||
|
@ -55,7 +75,7 @@ module.exports = function(realmConstructor) {
|
|||
}
|
||||
else {
|
||||
try {
|
||||
let syncedRealm = new this(config);
|
||||
let syncedRealm = new realmConstructor(config);
|
||||
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented
|
||||
setTimeout(() => { resolve(syncedRealm); }, 1);
|
||||
} catch (e) {
|
||||
|
@ -79,27 +99,17 @@ module.exports = function(realmConstructor) {
|
|||
openAsync(config, callback, progressCallback) {
|
||||
const message = "Realm.openAsync is now deprecated in favor of Realm.open. This function will be removed in future versions.";
|
||||
(console.warn || console.log).call(console, message);
|
||||
|
||||
realmConstructor._waitForDownload(config,
|
||||
(syncSession) => {
|
||||
if (progressCallback) {
|
||||
syncSession.addProgressNotification('download', 'forCurrentlyOutstandingWork', progressCallback);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
if (error) {
|
||||
setTimeout(() => { callback(error); }, 1);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
let syncedRealm = new this(config);
|
||||
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented
|
||||
setTimeout(() => { callback(null, syncedRealm); }, 1);
|
||||
} catch (e) {
|
||||
setTimeout(() => { callback(e); }, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let promise = this.open(config)
|
||||
if (progressCallback) {
|
||||
promise.progress(progressCallback)
|
||||
}
|
||||
|
||||
promise.then(realm => {
|
||||
callback(null, realm)
|
||||
}).catch(error => {
|
||||
callback(error);
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -137,10 +147,21 @@ module.exports = function(realmConstructor) {
|
|||
|
||||
realmConstructor.Sync._setFeatureToken(featureToken.trim());
|
||||
}
|
||||
|
||||
//enable deprecated setAccessToken
|
||||
realmConstructor.Sync.setAccessToken = realmConstructor.Sync.setFeatureToken;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Remove this now useless object.
|
||||
|
|
|
@ -84,6 +84,7 @@ declare namespace Realm {
|
|||
schema?: ObjectClass[] | ObjectSchema[];
|
||||
schemaVersion?: number;
|
||||
sync?: Realm.Sync.SyncConfiguration;
|
||||
deleteRealmIfMigrationNeeded?: boolean;
|
||||
}
|
||||
|
||||
// object props type
|
||||
|
@ -143,6 +144,11 @@ declare namespace Realm {
|
|||
*/
|
||||
isValid(): boolean;
|
||||
|
||||
min(property?: string): number|Date|null;
|
||||
max(property?: string): number|Date|null;
|
||||
sum(property?: string): number|null;
|
||||
avg(property?: string): number;
|
||||
|
||||
/**
|
||||
* @param {string} query
|
||||
* @param {any[]} ...arg
|
||||
|
@ -221,7 +227,13 @@ declare namespace Realm {
|
|||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Results.html }
|
||||
*/
|
||||
interface Results<T> extends Collection<T> {
|
||||
|
||||
/**
|
||||
* Bulk update objects in the collection.
|
||||
* @param {string} property
|
||||
* @param {any} value
|
||||
* @returns void
|
||||
*/
|
||||
update(property: string, value: any): void;
|
||||
}
|
||||
|
||||
const Results: {
|
||||
|
@ -281,7 +293,7 @@ declare namespace Realm.Sync {
|
|||
openManagementRealm(): Realm;
|
||||
retrieveAccount(provider: string, username: string): Promise<Account>;
|
||||
|
||||
getGrantedPermissions(recipient: 'any' | 'currentUser' | 'otherUser'): Results<Permission>;
|
||||
getGrantedPermissions(recipient: 'any' | 'currentUser' | 'otherUser'): Promise<Results<Permission>>;
|
||||
applyPermissions(condition: PermissionCondition, realmUrl: string, accessLevel: AccessLevel): Promise<PermissionChange>;
|
||||
offerPermissions(realmUrl: string, accessLevel: AccessLevel, expiresAt?: Date): Promise<string>;
|
||||
acceptPermissionOffer(token: string): Promise<string>
|
||||
|
@ -333,7 +345,14 @@ declare namespace Realm.Sync {
|
|||
expiresAt?: Date;
|
||||
}
|
||||
|
||||
type ErrorCallback = (message?: string, isFatal?: boolean, category?: string, code?: number) => void;
|
||||
interface SyncError {
|
||||
message: string;
|
||||
isFatal: boolean
|
||||
category?: string
|
||||
code: number;
|
||||
}
|
||||
|
||||
type ErrorCallback = (session: Session, error: SyncError) => void;
|
||||
type SSLVerifyCallback = (serverAddress: string, serverPort: number, pemCertificate: string, preverifyOk: number, depth: number) => boolean;
|
||||
|
||||
interface SyncConfiguration {
|
||||
|
@ -387,13 +406,9 @@ declare namespace Realm.Sync {
|
|||
function removeAllListeners(name?: string): void;
|
||||
function removeListener(regex: string, name: string, changeCallback: (changeEvent: ChangeEvent) => void): void;
|
||||
function setLogLevel(logLevel: 'all' | 'trace' | 'debug' | 'detail' | 'info' | 'warn' | 'error' | 'fatal' | 'off'): void;
|
||||
function initiateClientReset(path: string): void;
|
||||
function setFeatureToken(token: string): void;
|
||||
|
||||
/**
|
||||
* @deprecated, to be removed in 2.0
|
||||
*/
|
||||
function setAccessToken(accessToken: string): void;
|
||||
|
||||
type Instruction = {
|
||||
type: 'INSERT' | 'SET' | 'DELETE' | 'CLEAR' | 'LIST_SET' | 'LIST_INSERT' | 'LIST_ERASE' | 'LIST_CLEAR' | 'ADD_TYPE' | 'ADD_PROPERTIES' | 'CHANGE_IDENTITY' | 'SWAP_IDENTITY'
|
||||
object_type: string,
|
||||
|
@ -571,6 +586,11 @@ declare class Realm {
|
|||
* @returns boolean
|
||||
*/
|
||||
compact(): boolean;
|
||||
|
||||
/**
|
||||
* @returns Promise<Results<T>>
|
||||
*/
|
||||
subscribeToObjects<T>(objectType: string, query: string): Promise<Realm.Results<T>>;
|
||||
}
|
||||
|
||||
declare module 'realm' {
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
const require_method = require;
|
||||
|
||||
// Prevent React Native packager from seeing modules required with this
|
||||
function nodeRequire(module) {
|
||||
return require(module);
|
||||
return require_method(module);
|
||||
}
|
||||
|
||||
function getContext() {
|
||||
|
@ -91,7 +93,7 @@ switch(getContext()) {
|
|||
var pkg = path.resolve(path.join(__dirname,'../package.json'));
|
||||
var binding_path = binary.find(pkg);
|
||||
|
||||
realmConstructor = require(binding_path).Realm;
|
||||
realmConstructor = require_method(binding_path).Realm;
|
||||
break;
|
||||
|
||||
case 'reactnative':
|
||||
|
|
|
@ -72,31 +72,11 @@ function getSpecialPurposeRealm(user, realmName, schema) {
|
|||
}
|
||||
};
|
||||
|
||||
const _Realm = user.constructor._realmConstructor;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
_Realm._waitForDownload(config, (error) => {
|
||||
|
||||
// FIXME: I don't understand why, but removing the following setTimeout causes the subsequent
|
||||
// setTimeout call (when resolving the promise) to hang on RN iOS.
|
||||
// This might be related to our general makeCallback issue: #1255.
|
||||
setTimeout(() => {}, 1);
|
||||
|
||||
if (error) {
|
||||
setTimeout(() => reject(error), 1);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
let syncedRealm = new _Realm(config);
|
||||
user[specialPurposeRealmsKey][realmName] = syncedRealm;
|
||||
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented (#1255)
|
||||
setTimeout(() => resolve(syncedRealm), 1);
|
||||
} catch (e) {
|
||||
setTimeout(() => reject(e), 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
let Realm = user.constructor._realmConstructor;
|
||||
return Realm.open(config).then(realm => {
|
||||
user[specialPurposeRealmsKey][realmName] = realm;
|
||||
return realm;
|
||||
})
|
||||
}
|
||||
|
||||
function createInManagementRealm(user, modelName, modelInitializer) {
|
||||
|
|
|
@ -25,6 +25,11 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
if ('REALM_DISABLE_ANALYTICS' in process.env) {
|
||||
module.exports = function(){};
|
||||
return;
|
||||
}
|
||||
|
||||
const os = require('os');
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
|
@ -69,9 +74,6 @@ function getAnonymizedMachineIdentifier() {
|
|||
}
|
||||
|
||||
module.exports = function(eventName) {
|
||||
if ('REALM_DISABLE_ANALYTICS' in process.env)
|
||||
return;
|
||||
|
||||
const identifier = getAnonymizedMachineIdentifier();
|
||||
const payload = {
|
||||
'event': eventName,
|
||||
|
|
|
@ -21,8 +21,10 @@
|
|||
const AuthError = require('./errors').AuthError;
|
||||
const permissionApis = require('./permission-api');
|
||||
|
||||
const require_method = require;
|
||||
|
||||
function node_require(module) {
|
||||
return require(module);
|
||||
return require_method(module);
|
||||
}
|
||||
|
||||
function checkTypes(args, types) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "realm",
|
||||
"description": "Realm is a mobile database: an alternative to SQLite and key-value stores",
|
||||
"version": "2.0.0-rc14",
|
||||
"version": "2.0.0-rc20",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://realm.io",
|
||||
"keywords": [
|
||||
|
|
|
@ -92,6 +92,33 @@ task prepareRealmCore(dependsOn: downloadRealmCore, type:Copy) {
|
|||
}
|
||||
}
|
||||
|
||||
task downloadOpenSSL_x86(type: Download) {
|
||||
src "https://static.realm.io/downloads/openssl/1.0.2k/Android/x86/openssl-release-1.0.2k-Android-x86.tar.gz"
|
||||
onlyIfNewer true
|
||||
overwrite true
|
||||
dest new File(downloadsDir, "openssl-release-1.0.2k-Android-x86.tar.gz")
|
||||
}
|
||||
|
||||
task prepareOpenSSL_x86(dependsOn: downloadOpenSSL_x86, type:Copy) {
|
||||
from tarTree(downloadOpenSSL_x86.dest)
|
||||
into "$coreDownloadDir/core"
|
||||
}
|
||||
|
||||
task downloadOpenSSL_arm(type: Download) {
|
||||
src "https://static.realm.io/downloads/openssl/1.0.2k/Android/armeabi-v7a/openssl-release-1.0.2k-Android-armeabi-v7a.tar.gz"
|
||||
onlyIfNewer true
|
||||
overwrite true
|
||||
dest new File(downloadsDir, "openssl-release-1.0.2k-Android-armeabi-v7a.tar.gz")
|
||||
}
|
||||
|
||||
task prepareOpenSSL_arm(dependsOn: downloadOpenSSL_arm, type:Copy) {
|
||||
from tarTree(downloadOpenSSL_arm.dest)
|
||||
into "$coreDownloadDir/core"
|
||||
rename { String fileName ->
|
||||
fileName.replace("-arm-", "-armeabi-")
|
||||
}
|
||||
}
|
||||
|
||||
def getDependenciesVersion(keyName) {
|
||||
def inputFile = new File(buildscript.sourceFile.getParent() + "/../../dependencies.list")
|
||||
def line
|
||||
|
@ -194,7 +221,7 @@ def getNdkBuildFullPath() {
|
|||
return ndkBuildFullPath
|
||||
}
|
||||
|
||||
task buildReactNdkLib(dependsOn: [downloadJSCHeaders,prepareRealmCore], type: Exec) {
|
||||
task buildReactNdkLib(dependsOn: [downloadJSCHeaders,prepareRealmCore,prepareOpenSSL_x86,prepareOpenSSL_arm], type: Exec) {
|
||||
inputs.file('src/main/jni')
|
||||
outputs.dir("$buildDir/realm-react-ndk/all")
|
||||
commandLine getNdkBuildFullPath(),
|
||||
|
|
|
@ -158,6 +158,11 @@ class RealmReactModule extends ReactContextBaseJavaModule {
|
|||
e.printStackTrace();
|
||||
}
|
||||
final String json = map.get("postData");
|
||||
if (json == null) {
|
||||
Response response = newFixedLengthResponse("");
|
||||
response.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");
|
||||
return response;
|
||||
}
|
||||
final String jsonResponse = processChromeDebugCommand(cmdUri, json);
|
||||
|
||||
Response response = newFixedLengthResponse(jsonResponse);
|
||||
|
|
|
@ -6,6 +6,12 @@ LOCAL_MODULE := realm-android-sync-$(TARGET_ARCH_ABI)
|
|||
LOCAL_EXPORT_C_INCLUDES := core/include
|
||||
LOCAL_SRC_FILES := core/librealm-sync-android-$(TARGET_ARCH_ABI).a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := crypto-$(TARGET_ARCH_ABI)
|
||||
LOCAL_EXPORT_C_INCLUDES := core/openssl-release-1.0.2k-Android-$(TARGET_ARCH_ABI)/include
|
||||
LOCAL_SRC_FILES := core/openssl-release-1.0.2k-Android-$(TARGET_ARCH_ABI)/lib/libcrypto.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
endif
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
@ -19,6 +25,12 @@ LOCAL_MODULE := libjsc
|
|||
LOCAL_EXPORT_C_INCLUDES := jsc
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := ssl-$(TARGET_ARCH_ABI)
|
||||
LOCAL_EXPORT_C_INCLUDES := core/openssl-release-1.0.2k-Android-$(TARGET_ARCH_ABI)/include
|
||||
LOCAL_SRC_FILES := core/openssl-release-1.0.2k-Android-$(TARGET_ARCH_ABI)/lib/libssl.a
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := librealmreact
|
||||
|
||||
|
@ -43,6 +55,7 @@ LOCAL_SRC_FILES += src/object-store/src/impl/epoll/external_commit_helper.cpp
|
|||
LOCAL_SRC_FILES += src/object-store/src/parser/parser.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/parser/query_builder.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/util/format.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/util/uuid.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/binding_callback_thread_observer.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/collection_notifications.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/index_set.cpp
|
||||
|
@ -61,6 +74,8 @@ LOCAL_SRC_FILES += src/object-store/src/sync/sync_manager.cpp
|
|||
LOCAL_SRC_FILES += src/object-store/src/sync/sync_session.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/sync_user.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/sync_config.cpp
|
||||
LOCAL_SRC_FILES += src/object-store/src/sync/partial_sync.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
|
||||
endif
|
||||
|
@ -76,6 +91,7 @@ LOCAL_C_INCLUDES += $(JAVA_HOME)/include
|
|||
LOCAL_C_INCLUDES += $(JAVA_HOME)/include/darwin
|
||||
LOCAL_C_INCLUDES += $(JAVA_HOME)/include/linux
|
||||
LOCAL_C_INCLUDES += core/include
|
||||
LOCAL_C_INCLUDES += core/openssl-release-1.0.2k-Android-$(TARGET_ARCH_ABI)/include
|
||||
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
|
||||
LOCAL_C_INCLUDES += src/object-store/src/sync
|
||||
endif
|
||||
|
@ -84,8 +100,11 @@ LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
|
|||
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
|
||||
LOCAL_STATIC_LIBRARIES := realm-android-sync-$(TARGET_ARCH_ABI)
|
||||
LOCAL_STATIC_LIBRARIES += realm-android-$(TARGET_ARCH_ABI)
|
||||
LOCAL_STATIC_LIBRARIES += ssl-$(TARGET_ARCH_ABI)
|
||||
LOCAL_STATIC_LIBRARIES += crypto-$(TARGET_ARCH_ABI)
|
||||
else
|
||||
LOCAL_STATIC_LIBRARIES := realm-android-$(TARGET_ARCH_ABI)
|
||||
LOCAL_STATIC_LIBRARIES += crypto-$(TARGET_ARCH_ABI)
|
||||
endif
|
||||
|
||||
|
||||
|
|
|
@ -13,11 +13,13 @@ APP_CPPFLAGS += -frtti
|
|||
APP_CPPFLAGS += -fexceptions
|
||||
APP_CPPFLAGS += -DREALM_HAVE_CONFIG
|
||||
APP_CPPFLAGS += -fomit-frame-pointer
|
||||
APP_CPPFLAGS += -fvisibility=hidden
|
||||
|
||||
# Make sure every shared lib includes a .note.gnu.build-id header
|
||||
APP_LDFLAGS := -Wl,--build-id
|
||||
APP_LDFLAGS += -llog
|
||||
APP_LDFLAGS += -landroid
|
||||
APP_LDFLAGS += -fvisibility=hidden
|
||||
|
||||
ifeq ($(strip $(BUILD_TYPE_SYNC)),1)
|
||||
APP_CPPFLAGS += -DREALM_ENABLE_SYNC=1
|
||||
|
|
|
@ -349,6 +349,7 @@
|
|||
"$(SRCROOT)/../../tests/react-test-app/node_modules/react-native/React/**",
|
||||
"$(SRCROOT)/../../examples/ReactExample/node_modules/react-native/React/**",
|
||||
"$(SRCROOT)/../../vendor",
|
||||
"$(SRCROOT)/../../../../ios/Pods/Headers/Public/**"
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = "$(BUILT_PRODUCTS_DIR)/libGCDWebServers.a";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -368,6 +369,7 @@
|
|||
"$(SRCROOT)/../../tests/react-test-app/node_modules/react-native/React/**",
|
||||
"$(SRCROOT)/../../examples/ReactExample/node_modules/react-native/React/**",
|
||||
"$(SRCROOT)/../../vendor",
|
||||
"$(SRCROOT)/../../../../ios/Pods/Headers/Public/**"
|
||||
);
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
|
18
realm.gypi
18
realm.gypi
|
@ -16,11 +16,13 @@
|
|||
}],
|
||||
["use_realm_debug", {
|
||||
"variables": {
|
||||
"debug_library_suffix": "-dbg"
|
||||
"debug_library_suffix": "-dbg",
|
||||
"build_directory": "build.debug",
|
||||
}
|
||||
}, {
|
||||
"variables": {
|
||||
"debug_library_suffix": ""
|
||||
"debug_library_suffix": "",
|
||||
"build_directory": "build.release",
|
||||
}
|
||||
}]
|
||||
],
|
||||
|
@ -42,10 +44,10 @@
|
|||
"conditions": [
|
||||
["prefix!=''", {
|
||||
"all_dependent_settings": {
|
||||
"include_dirs": [ "<(prefix)/src" ],
|
||||
"include_dirs": [ "<(prefix)/src", "<(prefix)/<(build_directory)/src" ],
|
||||
},
|
||||
"direct_dependent_settings": {
|
||||
"library_dirs": [ "<(prefix)/src/realm" ]
|
||||
"library_dirs": [ "<(prefix)/<(build_directory)/src/realm" ]
|
||||
}
|
||||
}, {
|
||||
"dependencies": [ "vendored-realm" ]
|
||||
|
@ -74,15 +76,15 @@
|
|||
},
|
||||
"conditions": [
|
||||
["prefix!=''", {
|
||||
"all_dependent_settings+": {
|
||||
"all_dependent_settings": {
|
||||
"include_dirs": [ "<(prefix)/src" ],
|
||||
},
|
||||
"direct_dependent_settings+": {
|
||||
"direct_dependent_settings": {
|
||||
"library_dirs": [ "<(prefix)/src/realm" ]
|
||||
}
|
||||
}, {
|
||||
"dependencies+": [ "vendored-realm" ]
|
||||
}]
|
||||
"dependencies": [ "vendored-realm" ]
|
||||
}],
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -57,7 +57,7 @@ start_server() {
|
|||
}
|
||||
|
||||
stop_server() {
|
||||
echo stopping server
|
||||
echo stopping server
|
||||
if [[ ${SERVER_PID} -gt 0 ]] ; then
|
||||
echo server is running. killing it
|
||||
kill -9 ${SERVER_PID} || true
|
||||
|
@ -352,12 +352,12 @@ case "$TARGET" in
|
|||
;;
|
||||
"node")
|
||||
npm run check-environment
|
||||
if [ "$(uname)" = 'Darwin' ]; then
|
||||
if [ "$(uname)" = 'Darwin' ]; then
|
||||
echo "downloading server"
|
||||
download_server
|
||||
echo "starting server"
|
||||
start_server
|
||||
|
||||
|
||||
npm_tests_cmd="npm run test"
|
||||
npm install --build-from-source=realm --realm_enable_sync
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
3FCE2A8B1F58BDEF00D4855B /* primitive_list_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3FCE2A891F58BDE500D4855B /* primitive_list_notifier.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 */; };
|
||||
420FB79F1F7FBFE900D43D0F /* partial_sync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 423737AF1F7E333400FAEDFF /* partial_sync.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 */; };
|
||||
|
@ -194,6 +195,8 @@
|
|||
3FCE2A951F58BE1D00D4855B /* sync_permission.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_permission.cpp; path = src/sync/sync_permission.cpp; sourceTree = "<group>"; };
|
||||
3FCE2A981F58BE3600D4855B /* descriptor_ordering.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = descriptor_ordering.hpp; path = "object-store/src/descriptor_ordering.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>"; };
|
||||
423737B01F7E333400FAEDFF /* partial_sync.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = partial_sync.hpp; path = src/sync/partial_sync.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>"; };
|
||||
|
@ -464,6 +467,8 @@
|
|||
02E315CC1DB80DE000555337 /* sync */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
423737AF1F7E333400FAEDFF /* partial_sync.cpp */,
|
||||
423737B01F7E333400FAEDFF /* partial_sync.hpp */,
|
||||
426FCDFF1F7DA2F9005565DC /* sync_config.cpp */,
|
||||
504CF8521EBCAE3600A9A4B6 /* impl */,
|
||||
02E315CD1DB80DF200555337 /* sync_client.hpp */,
|
||||
|
@ -936,6 +941,7 @@
|
|||
5D97DC4E1F7DAB1400B856A4 /* sync_config.cpp in Sources */,
|
||||
504CF8601EBCAE3600A9A4B6 /* sync_file.cpp in Sources */,
|
||||
02E315D21DB80DF200555337 /* sync_file.cpp in Sources */,
|
||||
420FB79F1F7FBFE900D43D0F /* partial_sync.cpp in Sources */,
|
||||
02E315C91DB80DDD00555337 /* sync_manager.cpp in Sources */,
|
||||
504CF8611EBCAE3600A9A4B6 /* sync_metadata.cpp in Sources */,
|
||||
02E315D31DB80DF200555337 /* sync_metadata.cpp in Sources */,
|
||||
|
|
|
@ -51,6 +51,12 @@ struct Arguments {
|
|||
throw std::invalid_argument(util::format("Invalid arguments: at most %1 expected, but %2 supplied.", max, count));
|
||||
}
|
||||
}
|
||||
|
||||
void validate_count(size_t actual) const {
|
||||
if (count != actual) {
|
||||
throw std::invalid_argument(util::format("Invalid arguments: %1 expected, but %s supplied.", actual, count));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -60,7 +66,7 @@ template<typename T>
|
|||
struct PropertyType {
|
||||
using GetterType = void(typename T::Context, typename T::Object, ReturnValue<T> &);
|
||||
using SetterType = void(typename T::Context, typename T::Object, typename T::Value);
|
||||
|
||||
|
||||
typename T::PropertyGetterCallback getter;
|
||||
typename T::PropertySetterCallback setter;
|
||||
};
|
||||
|
@ -95,7 +101,7 @@ template<typename T, typename U, typename V = void>
|
|||
struct ClassDefinition {
|
||||
using Internal = U;
|
||||
using Parent = V;
|
||||
|
||||
|
||||
// Every subclass *must* at least have a name.
|
||||
// std::string const name;
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ class List : public realm::List {
|
|||
|
||||
template<typename T>
|
||||
struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
|
||||
using Type = T;
|
||||
using ContextType = typename T::Context;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
|
@ -81,7 +82,7 @@ struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
|
|||
static void add_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
|
||||
std::string const name = "List";
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
|
@ -95,6 +96,10 @@ struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
|
|||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
{"indexOf", wrap<index_of>},
|
||||
{"min", wrap<compute_aggregate_on_collection<ListClass<T>, AggregateFunc::Min>>},
|
||||
{"max", wrap<compute_aggregate_on_collection<ListClass<T>, AggregateFunc::Max>>},
|
||||
{"sum", wrap<compute_aggregate_on_collection<ListClass<T>, AggregateFunc::Sum>>},
|
||||
{"avg", wrap<compute_aggregate_on_collection<ListClass<T>, AggregateFunc::Avg>>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
|
@ -124,7 +129,7 @@ void ListClass<T>::get_length(ContextType, ObjectType object, ReturnValue &retur
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::get_type(ContextType, ObjectType object, ReturnValue &return_value) {
|
||||
void ListClass<T>::get_type(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(object);
|
||||
return_value.set(string_for_property_type(list->get_type() & ~realm::PropertyType::Flags));
|
||||
}
|
||||
|
@ -229,7 +234,7 @@ void ListClass<T>::splice(ContextType ctx, ObjectType this_object, Arguments arg
|
|||
remove = std::max<long>(Value::to_number(ctx, args[1]), 0);
|
||||
remove = std::min<long>(remove, size - index);
|
||||
}
|
||||
|
||||
|
||||
std::vector<ValueType> removed_objects;
|
||||
removed_objects.reserve(remove);
|
||||
|
||||
|
@ -263,7 +268,7 @@ void ListClass<T>::sorted(ContextType ctx, ObjectType this_object, Arguments arg
|
|||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
return_value.set(ResultsClass<T>::create_instance(ctx, list->sort(ResultsClass<T>::get_keypaths(ctx, args))));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::is_valid(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, ListClass<T>>(this_object)->is_valid());
|
||||
|
@ -284,7 +289,7 @@ void ListClass<T>::add_listener(ContextType ctx, ObjectType this_object, Argumen
|
|||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
ResultsClass<T>::add_listener(ctx, *list, this_object, args);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::remove_listener(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
|
|
229
src/js_realm.hpp
229
src/js_realm.hpp
|
@ -34,12 +34,14 @@
|
|||
#include "js_sync.hpp"
|
||||
#include "sync/sync_config.hpp"
|
||||
#include "sync/sync_manager.hpp"
|
||||
#include "sync/partial_sync.hpp"
|
||||
#endif
|
||||
|
||||
#include "shared_realm.hpp"
|
||||
#include "binding_context.hpp"
|
||||
#include "object_accessor.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "results.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
@ -156,7 +158,7 @@ class RealmClass : public ClassDefinition<T, SharedRealm, ObservableClass<T>> {
|
|||
public:
|
||||
using ObjectDefaultsMap = typename Schema<T>::ObjectDefaultsMap;
|
||||
using ConstructorMap = typename Schema<T>::ConstructorMap;
|
||||
|
||||
|
||||
using WaitHandler = void(std::error_code);
|
||||
using ProgressHandler = void(uint64_t transferred_bytes, uint64_t transferrable_bytes);
|
||||
|
||||
|
@ -180,6 +182,9 @@ public:
|
|||
static void close(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void compact(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void delete_model(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 &);
|
||||
|
@ -213,7 +218,6 @@ public:
|
|||
{"clearTestState", wrap<clear_test_state>},
|
||||
{"copyBundledRealmFiles", wrap<copy_bundled_realm_files>},
|
||||
{"deleteFile", wrap<delete_file>},
|
||||
{"_waitForDownload", wrap<wait_for_download_completion>},
|
||||
};
|
||||
|
||||
PropertyMap<T> const static_properties = {
|
||||
|
@ -236,6 +240,10 @@ public:
|
|||
{"close", wrap<close>},
|
||||
{"compact", wrap<compact>},
|
||||
{"deleteModel", wrap<delete_model>},
|
||||
{"_waitForDownload", wrap<wait_for_download_completion>},
|
||||
#if REALM_ENABLE_SYNC
|
||||
{"_subscribeToObjects", wrap<subscribe_to_objects>},
|
||||
#endif
|
||||
};
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
|
@ -252,6 +260,26 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
static void handleRealmFileException(ContextType ctx, realm::Realm::Config config, const RealmFileException& ex) {
|
||||
switch (ex.kind()) {
|
||||
case RealmFileException::Kind::IncompatibleSyncedRealm: {
|
||||
ObjectType configuration = Object::create_empty(ctx);
|
||||
Object::set_property(ctx, configuration, "path", Value::from_string(ctx, ex.path()));
|
||||
Object::set_property(ctx, configuration, "readOnly", Value::from_boolean(ctx, true));
|
||||
if (!config.encryption_key.empty()) {
|
||||
Object::set_property(ctx, configuration, "encryption_key", Value::from_binary(ctx, BinaryData(&config.encryption_key[0], 64)));
|
||||
}
|
||||
|
||||
ObjectType object = Object::create_empty(ctx);
|
||||
Object::set_property(ctx, object, "name", Value::from_string(ctx, "IncompatibleSyncedRealmError"));
|
||||
Object::set_property(ctx, object, "configuration", configuration);
|
||||
throw Exception<T>(ctx, object);
|
||||
}
|
||||
default:
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static std::string validated_notification_name(ContextType ctx, const ValueType &value) {
|
||||
std::string name = Value::validated_to_string(ctx, value, "notification name");
|
||||
if (name != "change") {
|
||||
|
@ -381,7 +409,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
|||
else if (config.path.empty()) {
|
||||
config.path = js::default_path();
|
||||
}
|
||||
|
||||
|
||||
static const String in_memory_string = "inMemory";
|
||||
ValueType in_memory_value = Object::get_property(ctx, object, in_memory_string);
|
||||
if (!Value::is_undefined(ctx, in_memory_value) && Value::validated_to_boolean(ctx, in_memory_value, "inMemory")) {
|
||||
|
@ -394,6 +422,16 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
|||
config.schema_mode = SchemaMode::Immutable;
|
||||
}
|
||||
|
||||
static const String delete_realm_if_migration_needed_string = "deleteRealmIfMigrationNeeded";
|
||||
ValueType delete_realm_if_migration_needed_value = Object::get_property(ctx, object, delete_realm_if_migration_needed_string);
|
||||
if (!Value::is_undefined(ctx, delete_realm_if_migration_needed_value) && Value::validated_to_boolean(ctx, delete_realm_if_migration_needed_value, "deleteRealmIfMigrationNeeded")) {
|
||||
if (config.schema_mode == SchemaMode::Immutable) {
|
||||
throw std::invalid_argument("Cannot set 'deleteRealmIfMigrationNeeded' when 'readOnly' is set.");
|
||||
}
|
||||
|
||||
config.schema_mode = SchemaMode::ResetFile;
|
||||
}
|
||||
|
||||
static const String schema_string = "schema";
|
||||
ValueType schema_value = Object::get_property(ctx, object, schema_string);
|
||||
if (!Value::is_undefined(ctx, schema_value)) {
|
||||
|
@ -437,6 +475,11 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
|||
ValueType migration_value = Object::get_property(ctx, object, migration_string);
|
||||
if (!Value::is_undefined(ctx, migration_value)) {
|
||||
FunctionType migration_function = Value::validated_to_function(ctx, migration_value, "migration");
|
||||
|
||||
if (config.schema_mode == SchemaMode::ResetFile) {
|
||||
throw std::invalid_argument("Cannot include 'migration' when 'deleteRealmIfMigrationNeeded' is set.");
|
||||
}
|
||||
|
||||
config.migration_function = [=](SharedRealm old_realm, SharedRealm realm, realm::Schema&) {
|
||||
auto old_realm_ptr = new SharedRealm(old_realm);
|
||||
auto realm_ptr = new SharedRealm(realm);
|
||||
|
@ -460,6 +503,12 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
|||
realm_ptr->reset();
|
||||
};
|
||||
}
|
||||
|
||||
static const String cache_string = "_cache";
|
||||
ValueType cache_value = Object::get_property(ctx, object, cache_string);
|
||||
if (!Value::is_undefined(ctx, cache_value)) {
|
||||
config.cache = Value::validated_to_boolean(ctx, cache_value, "_cache");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -482,7 +531,16 @@ SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Co
|
|||
ObjectDefaultsMap && defaults, ConstructorMap && constructors) {
|
||||
config.execution_context = Context<T>::get_execution_context_id(ctx);
|
||||
|
||||
SharedRealm realm = realm::Realm::get_shared_realm(config);
|
||||
SharedRealm realm;
|
||||
try {
|
||||
realm = realm::Realm::get_shared_realm(config);
|
||||
}
|
||||
catch (const RealmFileException& ex) {
|
||||
handleRealmFileException(ctx, config, ex);
|
||||
}
|
||||
catch (...) {
|
||||
throw;
|
||||
}
|
||||
|
||||
GlobalContextType global_context = Context<T>::get_global_context(ctx);
|
||||
if (!realm->m_binding_context) {
|
||||
|
@ -642,34 +700,21 @@ void RealmClass<T>::get_sync_session(ContextType ctx, ObjectType object, ReturnV
|
|||
|
||||
template<typename T>
|
||||
void RealmClass<T>::wait_for_download_completion(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
args.validate_maximum(3);
|
||||
auto config_object = Value::validated_to_object(ctx, args[0]);
|
||||
auto callback_function = Value::validated_to_function(ctx, args[1 + (args.count == 3)]);
|
||||
args.validate_maximum(2);
|
||||
auto callback_function = Value::validated_to_function(ctx, args[0 + (args.count == 2)]);
|
||||
|
||||
ValueType session_callback = Value::from_null(ctx);
|
||||
if (args.count == 3) {
|
||||
session_callback = Value::validated_to_function(ctx, args[1]);
|
||||
if (args.count == 2) {
|
||||
session_callback = Value::validated_to_function(ctx, args[0]);
|
||||
}
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
ValueType sync_config_value = Object::get_property(ctx, config_object, "sync");
|
||||
if (!Value::is_undefined(ctx, sync_config_value)) {
|
||||
realm::Realm::Config config;
|
||||
config.cache = false;
|
||||
static const String encryption_key_string = "encryptionKey";
|
||||
ValueType encryption_key_value = Object::get_property(ctx, config_object, encryption_key_string);
|
||||
if (!Value::is_undefined(ctx, encryption_key_value)) {
|
||||
auto encryption_key = Value::validated_to_binary(ctx, encryption_key_value, "encryptionKey");
|
||||
config.encryption_key.assign(encryption_key.data(), encryption_key.data() + encryption_key.size());
|
||||
}
|
||||
|
||||
Protected<ObjectType> thiz(ctx, this_object);
|
||||
SyncClass<T>::populate_sync_config(ctx, thiz, config_object, config);
|
||||
|
||||
auto realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
if (auto* sync_config = realm->config().sync_config.get()) {
|
||||
Protected<FunctionType> protected_callback(ctx, callback_function);
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
|
||||
EventLoopDispatcher<WaitHandler> wait_handler([=](std::error_code error_code) {
|
||||
HANDLESCOPE
|
||||
if (!error_code) {
|
||||
|
@ -686,75 +731,39 @@ void RealmClass<T>::wait_for_download_completion(ContextType ctx, ObjectType thi
|
|||
callback_arguments[0] = object;
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 1, callback_arguments);
|
||||
}
|
||||
|
||||
// We keep our Realm instance alive until the callback has had a chance to open its own instance.
|
||||
// This allows it to share the sync session that our Realm opened.
|
||||
if (realm)
|
||||
realm->close();
|
||||
});
|
||||
std::function<WaitHandler> waitFunc = std::move(wait_handler);
|
||||
|
||||
std::function<ProgressHandler> progressFunc;
|
||||
|
||||
auto realm = realm::Realm::get_shared_realm(config);
|
||||
if (auto sync_config = config.sync_config)
|
||||
{
|
||||
static const String progressFuncName = "_onDownloadProgress";
|
||||
bool progressFuncDefined = false;
|
||||
if (!Value::is_boolean(ctx, sync_config_value) && !Value::is_undefined(ctx, sync_config_value))
|
||||
{
|
||||
auto sync_config_object = Value::validated_to_object(ctx, sync_config_value);
|
||||
|
||||
ValueType progressFuncValue = Object::get_property(ctx, sync_config_object, progressFuncName);
|
||||
progressFuncDefined = !Value::is_undefined(ctx, progressFuncValue);
|
||||
|
||||
if (progressFuncDefined)
|
||||
{
|
||||
Protected<FunctionType> protected_progressCallback(protected_ctx, Value::validated_to_function(protected_ctx, progressFuncValue));
|
||||
EventLoopDispatcher<ProgressHandler> progress_handler([=](uint64_t transferred_bytes, uint64_t transferrable_bytes) {
|
||||
HANDLESCOPE
|
||||
ValueType callback_arguments[2];
|
||||
callback_arguments[0] = Value::from_number(protected_ctx, transferred_bytes);
|
||||
callback_arguments[1] = Value::from_number(protected_ctx, transferrable_bytes);
|
||||
|
||||
Function<T>::callback(protected_ctx, protected_progressCallback, protected_this, 2, callback_arguments);
|
||||
});
|
||||
|
||||
progressFunc = std::move(progress_handler);
|
||||
std::shared_ptr<SyncUser> user = sync_config->user;
|
||||
if (user && user->state() != SyncUser::State::Error) {
|
||||
if (auto session = user->session_for_on_disk_path(realm->config().path)) {
|
||||
if (!Value::is_null(ctx, session_callback)) {
|
||||
FunctionType session_callback_func = Value::to_function(ctx, session_callback);
|
||||
auto syncSession = create_object<T, SessionClass<T>>(ctx, new WeakSession(session));
|
||||
ValueType callback_arguments[1];
|
||||
callback_arguments[0] = syncSession;
|
||||
Function<T>::callback(protected_ctx, session_callback_func, protected_this, 1, callback_arguments);
|
||||
}
|
||||
|
||||
session->wait_for_download_completion(std::move(wait_handler));
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<SyncUser> user = sync_config->user;
|
||||
if (user && user->state() != SyncUser::State::Error) {
|
||||
if (auto session = user->session_for_on_disk_path(config.path)) {
|
||||
if (!Value::is_null(ctx, session_callback)) {
|
||||
FunctionType session_callback_func = Value::to_function(ctx, session_callback);
|
||||
auto syncSession = create_object<T, SessionClass<T>>(ctx, new WeakSession(session));
|
||||
ValueType callback_arguments[1];
|
||||
callback_arguments[0] = syncSession;
|
||||
Function<T>::callback(protected_ctx, session_callback_func, protected_this, 1, callback_arguments);
|
||||
}
|
||||
|
||||
if (progressFuncDefined) {
|
||||
session->register_progress_notifier(std::move(progressFunc), SyncSession::NotifierType::download, false);
|
||||
}
|
||||
|
||||
session->wait_for_download_completion([=](std::error_code error_code) {
|
||||
realm->close(); //capture and keep realm instance for until here
|
||||
waitFunc(error_code);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ObjectType object = Object::create_empty(protected_ctx);
|
||||
Object::set_property(protected_ctx, object, "message",
|
||||
Value::from_string(protected_ctx, "Cannot asynchronously open synced Realm because the associated session previously experienced a fatal error"));
|
||||
Object::set_property(protected_ctx, object, "errorCode", Value::from_number(protected_ctx, 1));
|
||||
|
||||
ValueType callback_arguments[1];
|
||||
callback_arguments[0] = object;
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 1, callback_arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectType object = Object::create_empty(protected_ctx);
|
||||
Object::set_property(protected_ctx, object, "message",
|
||||
Value::from_string(protected_ctx, "Cannot asynchronously open synced Realm because the associated session previously experienced a fatal error"));
|
||||
Object::set_property(protected_ctx, object, "errorCode", Value::from_number(protected_ctx, 1));
|
||||
|
||||
ValueType callback_arguments[1];
|
||||
callback_arguments[0] = object;
|
||||
Function<T>::callback(protected_ctx, protected_callback, protected_this, 1, callback_arguments);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
static_cast<void>(config_object);
|
||||
#endif
|
||||
|
||||
Function<T>::callback(ctx, callback_function, this_object, 0, nullptr);
|
||||
|
@ -977,5 +986,51 @@ void RealmClass<T>::compact(ContextType ctx, ObjectType this_object, Arguments a
|
|||
return_value.set(realm->compact());
|
||||
}
|
||||
|
||||
#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
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "js_collection.hpp"
|
||||
#include "js_realm_object.hpp"
|
||||
#include "js_util.hpp"
|
||||
|
||||
#include "results.hpp"
|
||||
#include "list.hpp"
|
||||
|
@ -53,6 +54,7 @@ class Results : public realm::Results {
|
|||
|
||||
template<typename T>
|
||||
struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<T>> {
|
||||
using Type = T;
|
||||
using ContextType = typename T::Context;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
|
@ -84,7 +86,9 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||
|
||||
template<typename Fn>
|
||||
static void index_of(ContextType, Fn&, Arguments, ReturnValue &);
|
||||
|
||||
|
||||
static void update(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
// observable
|
||||
static void add_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
@ -94,7 +98,7 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||
static void add_listener(ContextType, U&, ObjectType, Arguments);
|
||||
template<typename U>
|
||||
static void remove_listener(ContextType, U&, ObjectType, Arguments);
|
||||
|
||||
|
||||
std::string const name = "Results";
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
|
@ -102,18 +106,23 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||
{"filtered", wrap<filtered>},
|
||||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
{"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>>},
|
||||
{"avg", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Avg>>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
{"indexOf", wrap<index_of>},
|
||||
{"update", wrap<update>},
|
||||
};
|
||||
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
{"length", {wrap<get_length>, nullptr}},
|
||||
{"type", {wrap<get_type>, nullptr}},
|
||||
{"optional", {wrap<get_optional>, nullptr}},
|
||||
};
|
||||
|
||||
|
||||
IndexPropertyType<T> const index_accessor = {wrap<get_index>, nullptr};
|
||||
};
|
||||
|
||||
|
@ -211,7 +220,6 @@ void ResultsClass<T>::get_optional(ContextType, ObjectType object, ReturnValue &
|
|||
return_value.set(is_nullable(results->get_type()));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::get_index(ContextType ctx, ObjectType object, uint32_t index, ReturnValue &return_value) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(object);
|
||||
|
@ -242,7 +250,7 @@ template<typename T>
|
|||
void ResultsClass<T>::is_valid(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, ResultsClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
template<typename Fn>
|
||||
void ResultsClass<T>::index_of(ContextType ctx, Fn& fn, Arguments args, ReturnValue &return_value) {
|
||||
|
@ -267,6 +275,32 @@ void ResultsClass<T>::index_of(ContextType ctx, Fn& fn, Arguments args, ReturnVa
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::update(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 2);
|
||||
|
||||
std::string property = Value::validated_to_string(ctx, arguments[0], "property");
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
|
||||
auto schema = results->get_object_schema();
|
||||
if (!schema.property_for_name(StringData(property))) {
|
||||
throw std::invalid_argument(util::format("No such property: %1", property));
|
||||
}
|
||||
|
||||
auto realm = results->get_realm();
|
||||
if (!realm->is_in_transaction()) {
|
||||
throw std::runtime_error("Can only 'update' objects within a transaction.");
|
||||
}
|
||||
|
||||
// TODO: This approach just moves the for-loop from JS to C++
|
||||
// Ideally, we'd implement this in OS or Core in an optimized fashion
|
||||
for (auto i = results->size(); i > 0; i--) {
|
||||
auto realm_object = realm::Object(realm, schema, results->get(i - 1));
|
||||
auto obj = RealmObjectClass<T>::create_instance(ctx, realm_object);
|
||||
RealmObjectClass<T>::set_property(ctx, obj, property, arguments[1]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::index_of(ContextType ctx, ObjectType this_object,
|
||||
Arguments args, ReturnValue &return_value) {
|
||||
|
@ -287,7 +321,7 @@ void ResultsClass<T>::add_listener(ContextType ctx, U& collection, ObjectType th
|
|||
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 = collection.add_notification_callback([=](CollectionChangeSet const& change_set, std::exception_ptr exception) {
|
||||
HANDLESCOPE
|
||||
ValueType arguments[] {
|
||||
|
@ -333,6 +367,6 @@ void ResultsClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_obje
|
|||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
results->m_notification_tokens.clear();
|
||||
}
|
||||
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <regex>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
|
@ -40,11 +36,11 @@ namespace realm {
|
|||
namespace js {
|
||||
|
||||
inline realm::SyncManager& syncManagerShared() {
|
||||
static bool configured = []{
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [] {
|
||||
ensure_directory_exists_for_file(default_realm_file_directory());
|
||||
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
|
||||
return true;
|
||||
}();
|
||||
});
|
||||
return SyncManager::shared();
|
||||
}
|
||||
|
||||
|
@ -229,10 +225,21 @@ public:
|
|||
HANDLESCOPE
|
||||
|
||||
auto error_object = Object<T>::create_empty(m_ctx);
|
||||
|
||||
auto error_code = error.error_code.value();
|
||||
if (error.is_client_reset_requested()) {
|
||||
error_code = 7; // FIXME: define a proper constant
|
||||
|
||||
auto config_object = Object<T>::create_empty(m_ctx);
|
||||
Object<T>::set_property(m_ctx, config_object, "path", Value<T>::from_string(m_ctx, error.user_info[SyncError::c_recovery_file_path_key]));
|
||||
Object<T>::set_property(m_ctx, config_object, "readOnly", Value<T>::from_boolean(m_ctx, true));
|
||||
Object<T>::set_property(m_ctx, error_object, "config", config_object);
|
||||
}
|
||||
|
||||
Object<T>::set_property(m_ctx, error_object, "message", Value<T>::from_string(m_ctx, error.message));
|
||||
Object<T>::set_property(m_ctx, error_object, "isFatal", Value<T>::from_boolean(m_ctx, error.is_fatal));
|
||||
Object<T>::set_property(m_ctx, error_object, "category", Value<T>::from_string(m_ctx, error.error_code.category().name()));
|
||||
Object<T>::set_property(m_ctx, error_object, "code", Value<T>::from_number(m_ctx, error.error_code.value()));
|
||||
Object<T>::set_property(m_ctx, error_object, "code", Value<T>::from_number(m_ctx, error_code));
|
||||
|
||||
auto user_info = Object<T>::create_empty(m_ctx);
|
||||
for (auto& kvp : error.user_info) {
|
||||
|
@ -478,9 +485,7 @@ void SessionClass<T>::add_progress_notification(ContextType ctx, FunctionType, O
|
|||
|
||||
progressFunc = std::move(progress_handler);
|
||||
|
||||
|
||||
auto registrationToken = session->register_progress_notifier(std::move(progressFunc), notifierType, is_streaming);
|
||||
|
||||
auto syncSession = create_object<T, SessionClass<T>>(ctx, new WeakSession(session));
|
||||
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete;
|
||||
Object::set_property(ctx, callback_function, "_syncSession", syncSession, attributes);
|
||||
|
@ -525,6 +530,7 @@ public:
|
|||
static FunctionType create_constructor(ContextType);
|
||||
|
||||
static void set_sync_log_level(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void initiate_client_reset(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
// private
|
||||
static std::function<SyncBindSessionHandler> session_bind_callback(ContextType ctx, ObjectType sync_constructor);
|
||||
|
@ -535,6 +541,7 @@ public:
|
|||
|
||||
MethodMap<T> const static_methods = {
|
||||
{"setLogLevel", wrap<set_sync_log_level>},
|
||||
{"initiateClientReset", wrap<initiate_client_reset>},
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -549,6 +556,15 @@ inline typename T::Function SyncClass<T>::create_constructor(ContextType ctx) {
|
|||
return sync_constructor;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::initiate_client_reset(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue & return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
std::string path = Value::validated_to_string(ctx, arguments[0]);
|
||||
if (!SyncManager::shared().immediately_run_file_actions(std::string(path))) {
|
||||
throw std::runtime_error(util::format("Realm was not configured correctly. Client Reset could not be run for Realm at: %1", path));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::set_sync_log_level(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
@ -607,8 +623,10 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
|||
|
||||
std::string raw_realm_url = Object::validated_get_string(ctx, sync_config_object, "url");
|
||||
if (shared_user->token_type() == SyncUser::TokenType::Admin) {
|
||||
static std::regex tilde("/~/");
|
||||
raw_realm_url = std::regex_replace(raw_realm_url, tilde, "/__auth/");
|
||||
size_t pos = raw_realm_url.find("/~/");
|
||||
if (pos != std::string::npos) {
|
||||
raw_realm_url.replace(pos + 1, 1, "__auth");
|
||||
}
|
||||
}
|
||||
|
||||
bool client_validate_ssl = true;
|
||||
|
@ -633,18 +651,24 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
|||
ssl_verify_callback = std::move(ssl_verify_functor);
|
||||
}
|
||||
|
||||
bool is_partial = false;
|
||||
ValueType partial_value = Object::get_property(ctx, sync_config_object, "partial");
|
||||
if (!Value::is_undefined(ctx, partial_value)) {
|
||||
is_partial = Value::validated_to_boolean(ctx, partial_value);
|
||||
}
|
||||
|
||||
// FIXME - use make_shared
|
||||
config.sync_config = std::shared_ptr<SyncConfig>(new SyncConfig{shared_user, raw_realm_url,
|
||||
SyncSessionStopPolicy::AfterChangesUploaded,
|
||||
std::move(bind), std::move(error_handler),
|
||||
nullptr, util::none,
|
||||
client_validate_ssl, ssl_trust_certificate_path,
|
||||
std::move(ssl_verify_callback)});
|
||||
|
||||
std::move(ssl_verify_callback),
|
||||
is_partial});
|
||||
|
||||
|
||||
config.schema_mode = SchemaMode::Additive;
|
||||
config.path = syncManagerShared().path_for_realm(*shared_user, raw_realm_url);
|
||||
config.path = syncManagerShared().path_for_realm(*shared_user, config.sync_config->realm_url());
|
||||
|
||||
if (!config.encryption_key.empty()) {
|
||||
config.sync_config->realm_encryption_key = std::array<char, 64>();
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <realm/binary_data.hpp>
|
||||
#include <realm/string_data.hpp>
|
||||
#include <realm/util/to_string.hpp>
|
||||
#include <realm/util/optional.hpp>
|
||||
#include <realm/mixed.hpp>
|
||||
|
||||
#if defined(__GNUC__) && !(defined(DEBUG) && DEBUG)
|
||||
# define REALM_JS_INLINE inline __attribute__((always_inline))
|
||||
|
@ -136,6 +138,8 @@ struct Value {
|
|||
static ValueType from_nonnull_string(ContextType, const String<T>&);
|
||||
static ValueType from_nonnull_binary(ContextType, BinaryData);
|
||||
static ValueType from_undefined(ContextType);
|
||||
static ValueType from_timestamp(ContextType, Timestamp);
|
||||
static ValueType from_mixed(ContextType, const util::Optional<Mixed> &);
|
||||
|
||||
static ObjectType to_array(ContextType, const ValueType &);
|
||||
static bool to_boolean(ContextType, const ValueType &);
|
||||
|
@ -436,5 +440,37 @@ inline bool Value<T>::is_valid_for_property_type(ContextType context, const Valu
|
|||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename T::Value Value<T>::from_timestamp(typename T::Context ctx, Timestamp ts) {
|
||||
return Object<T>::create_date(ctx, ts.get_seconds() * 1000 + ts.get_nanoseconds() / 1000000);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename T::Value Value<T>::from_mixed(typename T::Context ctx, const util::Optional<Mixed>& mixed) {
|
||||
if (!mixed) {
|
||||
return from_undefined(ctx);
|
||||
}
|
||||
|
||||
Mixed value = *mixed;
|
||||
switch (value.get_type()) {
|
||||
case type_Bool:
|
||||
return from_boolean(ctx, value.get_bool());
|
||||
case type_Int:
|
||||
return from_number(ctx, static_cast<double>(value.get_int()));
|
||||
case type_Float:
|
||||
return from_number(ctx, value.get_float());
|
||||
case type_Double:
|
||||
return from_number(ctx, value.get_double());
|
||||
case type_Timestamp:
|
||||
return from_timestamp(ctx, value.get_timestamp());
|
||||
case type_String:
|
||||
return from_string(ctx, value.get_string().data());
|
||||
case type_Binary:
|
||||
return from_binary(ctx, value.get_binary());
|
||||
default:
|
||||
throw std::invalid_argument("Value not convertible.");
|
||||
}
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -22,11 +22,19 @@
|
|||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "object_schema.hpp"
|
||||
#include "shared_realm.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
enum class AggregateFunc {
|
||||
Min,
|
||||
Max,
|
||||
Sum,
|
||||
Avg
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class RealmDelegate;
|
||||
|
||||
|
@ -75,5 +83,43 @@ static inline void validate_argument_count_at_least(size_t count, size_t expecte
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T, AggregateFunc func>
|
||||
void compute_aggregate_on_collection(typename T::ContextType ctx, typename T::ObjectType this_object,
|
||||
typename T::Arguments args, typename T::ReturnValue &return_value) {
|
||||
|
||||
auto list = get_internal<typename T::Type, T>(this_object);
|
||||
|
||||
size_t column = 0;
|
||||
if (list->get_type() == realm::PropertyType::Object) {
|
||||
const ObjectSchema& object_schema = list->get_object_schema();
|
||||
std::string property_name = T::Value::validated_to_string(ctx, args[0]);
|
||||
const Property* property = object_schema.property_for_name(property_name);
|
||||
if (!property) {
|
||||
throw std::invalid_argument(util::format("Property '%1' does not exist on object '%2'",
|
||||
property_name, object_schema.name));
|
||||
}
|
||||
column = property->table_column;
|
||||
}
|
||||
else {
|
||||
args.validate_maximum(0);
|
||||
}
|
||||
|
||||
util::Optional<Mixed> mixed;
|
||||
switch (func) {
|
||||
case AggregateFunc::Min:
|
||||
return_value.set(list->min(column));
|
||||
break;
|
||||
case AggregateFunc::Max:
|
||||
return_value.set(list->max(column));
|
||||
break;
|
||||
case AggregateFunc::Sum:
|
||||
return_value.set(list->sum(column));
|
||||
break;
|
||||
case AggregateFunc::Avg:
|
||||
return_value.set(list->average(column));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -53,12 +53,26 @@ class ReturnValue<jsc::Types> {
|
|||
void set(uint32_t number) {
|
||||
m_value = JSValueMakeNumber(m_context, number);
|
||||
}
|
||||
void set(const util::Optional<realm::Mixed>& mixed) {
|
||||
m_value = Value<jsc::Types>::from_mixed(m_context, mixed);
|
||||
}
|
||||
void set_null() {
|
||||
m_value = JSValueMakeNull(m_context);
|
||||
}
|
||||
void set_undefined() {
|
||||
m_value = JSValueMakeUndefined(m_context);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(const util::Optional<T>& value) {
|
||||
if (value) {
|
||||
set(*value);
|
||||
}
|
||||
else {
|
||||
set_undefined();
|
||||
}
|
||||
}
|
||||
|
||||
operator JSValueRef() const {
|
||||
return m_value;
|
||||
}
|
||||
|
|
|
@ -64,12 +64,25 @@ class ReturnValue<node::Types> {
|
|||
void set(uint32_t number) {
|
||||
m_value.Set(number);
|
||||
}
|
||||
void set(realm::Mixed mixed) {
|
||||
m_value.Set(Value<node::Types>::from_mixed(nullptr, mixed));
|
||||
}
|
||||
void set_null() {
|
||||
m_value.SetNull();
|
||||
}
|
||||
void set_undefined() {
|
||||
m_value.SetUndefined();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(util::Optional<T> value) {
|
||||
if (value) {
|
||||
set(*value);
|
||||
}
|
||||
else {
|
||||
m_value.SetUndefined();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // js
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 3df8be344da02212e8d4e05b775e1569d2a3d785
|
||||
Subproject commit db790aa652d9c135e7ef4925161119268b7166a8
|
18
src/rpc.cpp
18
src/rpc.cpp
|
@ -360,8 +360,24 @@ json RPCServer::perform_request(std::string name, const json &args) {
|
|||
assert(action);
|
||||
|
||||
m_worker.add_task([=] {
|
||||
return action(args);
|
||||
try {
|
||||
return action(args);
|
||||
}
|
||||
catch (jsc::Exception ex) {
|
||||
json exceptionAsJson = nullptr;
|
||||
try {
|
||||
exceptionAsJson = serialize_json_value(ex);
|
||||
}
|
||||
catch (...) {
|
||||
exceptionAsJson = {{"error", "An exception occured while processing the request. Could not serialize the exception as JSON"}};
|
||||
}
|
||||
return (json){{"error", exceptionAsJson}, {"message", ex.what()}};
|
||||
}
|
||||
catch (std::exception &exception) {
|
||||
return (json){{"error", exception.what()}};
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
Binary file not shown.
|
@ -42,7 +42,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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1004,4 +1004,226 @@ module.exports = {
|
|||
TestCase.assertEqual(list.isValid(), false);
|
||||
TestCase.assertThrowsContaining(() => list.length, 'invalidated');
|
||||
},
|
||||
|
||||
testListAggregateFunctions: function() {
|
||||
const NullableBasicTypesList = {
|
||||
name: 'NullableBasicTypesList',
|
||||
properties: {
|
||||
list: 'NullableBasicTypesObject[]',
|
||||
}
|
||||
};
|
||||
|
||||
const realm = new Realm({schema: [schemas.NullableBasicTypes, NullableBasicTypesList]});
|
||||
const N = 50;
|
||||
const list = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
list.push({
|
||||
intCol: i+1,
|
||||
floatCol: i+1,
|
||||
doubleCol: i+1,
|
||||
dateCol: new Date(i+1)
|
||||
});
|
||||
}
|
||||
|
||||
let object;
|
||||
realm.write(() => {
|
||||
object = realm.create('NullableBasicTypesList', {list: list});
|
||||
});
|
||||
|
||||
TestCase.assertEqual(object.list.length, N);
|
||||
|
||||
// int, float & double columns support all aggregate functions
|
||||
['intCol', 'floatCol', 'doubleCol'].forEach(colName => {
|
||||
TestCase.assertEqual(object.list.min(colName), 1);
|
||||
TestCase.assertEqual(object.list.max(colName), N);
|
||||
TestCase.assertEqual(object.list.sum(colName), N*(N+1)/2);
|
||||
TestCase.assertEqual(object.list.avg(colName), (N+1)/2);
|
||||
});
|
||||
|
||||
// date columns support only 'min' & 'max'
|
||||
TestCase.assertEqual(object.list.min('dateCol').getTime(), new Date(1).getTime());
|
||||
TestCase.assertEqual(object.list.max('dateCol').getTime(), new Date(N).getTime());
|
||||
},
|
||||
|
||||
testListAggregateFunctionsWithNullColumnValues: function() {
|
||||
const NullableBasicTypesList = {
|
||||
name: 'NullableBasicTypesList',
|
||||
properties: {
|
||||
list: 'NullableBasicTypesObject[]',
|
||||
}
|
||||
};
|
||||
|
||||
const realm = new Realm({schema: [schemas.NullableBasicTypes, NullableBasicTypesList]});
|
||||
|
||||
const N = 50;
|
||||
const M = 10;
|
||||
|
||||
const list = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
list.push({
|
||||
intCol: i+1,
|
||||
floatCol: i+1,
|
||||
doubleCol: i+1,
|
||||
dateCol: new Date(i+1)
|
||||
});
|
||||
}
|
||||
|
||||
for (let j = 0; j < M; j++) {
|
||||
list.push({});
|
||||
}
|
||||
|
||||
let object, objectEmptyList;
|
||||
realm.write(() => {
|
||||
object = realm.create('NullableBasicTypesList', {list: list});
|
||||
objectEmptyList = realm.create('NullableBasicTypesList', {list: []});
|
||||
});
|
||||
|
||||
TestCase.assertEqual(object.list.length, N + M);
|
||||
|
||||
// int, float & double columns support all aggregate functions
|
||||
// the M null valued objects should be ignored
|
||||
['intCol', 'floatCol', 'doubleCol'].forEach(colName => {
|
||||
TestCase.assertEqual(object.list.min(colName), 1);
|
||||
TestCase.assertEqual(object.list.max(colName), N);
|
||||
TestCase.assertEqual(object.list.sum(colName), N*(N+1)/2);
|
||||
TestCase.assertEqual(object.list.avg(colName), (N+1)/2);
|
||||
});
|
||||
|
||||
// date columns support only 'min' & 'max'
|
||||
TestCase.assertEqual(object.list.min('dateCol').getTime(), new Date(1).getTime());
|
||||
TestCase.assertEqual(object.list.max('dateCol').getTime(), new Date(N).getTime());
|
||||
|
||||
// call aggregate functions on empty list
|
||||
TestCase.assertEqual(objectEmptyList.list.length, 0);
|
||||
['intCol', 'floatCol', 'doubleCol'].forEach(colName => {
|
||||
TestCase.assertUndefined(objectEmptyList.list.min(colName));
|
||||
TestCase.assertUndefined(objectEmptyList.list.max(colName));
|
||||
TestCase.assertEqual(objectEmptyList.list.sum(colName), 0);
|
||||
TestCase.assertUndefined(objectEmptyList.list.avg(colName));
|
||||
});
|
||||
|
||||
TestCase.assertUndefined(objectEmptyList.list.min('dateCol'));
|
||||
TestCase.assertUndefined(objectEmptyList.list.max('dateCol'));
|
||||
},
|
||||
|
||||
testPrimitiveListAggregateFunctions: function() {
|
||||
const realm = new Realm({schema: [schemas.PrimitiveArrays]});
|
||||
let object;
|
||||
realm.write(() => {
|
||||
object = realm.create('PrimitiveArrays', {
|
||||
int: [1, 2, 3],
|
||||
float: [1.1, 2.2, 3.3],
|
||||
double: [1.11, 2.22, 3.33],
|
||||
date: [DATE1, DATE2, DATE3],
|
||||
|
||||
optInt: [1, null, 2],
|
||||
optFloat: [1.1, null, 3.3],
|
||||
optDouble: [1.11, null, 3.33],
|
||||
optDate: [DATE1, null, DATE3]
|
||||
});
|
||||
});
|
||||
|
||||
for (let prop of ['int', 'float', 'double', 'date', 'optInt', 'optFloat', 'optDouble', 'optDate']) {
|
||||
const list = object[prop];
|
||||
TestCase.assertSimilar(list.type, list.min(), list[0]);
|
||||
TestCase.assertSimilar(list.type, list.max(), list[2]);
|
||||
|
||||
if (list.type === 'date') {
|
||||
TestCase.assertThrowsContaining(() => list.sum(), "Cannot sum 'date' array: operation not supported")
|
||||
TestCase.assertThrowsContaining(() => list.avg(), "Cannot average 'date' array: operation not supported")
|
||||
continue;
|
||||
}
|
||||
|
||||
const sum = list[0] + list[1] + list[2];
|
||||
const avg = sum / (list[1] === null ? 2 : 3);
|
||||
TestCase.assertSimilar(list.type, list.sum(), sum);
|
||||
TestCase.assertSimilar(list.type, list.avg(), avg);
|
||||
}
|
||||
|
||||
TestCase.assertThrowsContaining(() => object.bool.min(), "Cannot min 'bool' array: operation not supported")
|
||||
TestCase.assertThrowsContaining(() => object.int.min("foo"), "Invalid arguments: at most 0 expected, but 1 supplied")
|
||||
},
|
||||
|
||||
testListAggregateFunctionsUnsupported: function() {
|
||||
const NullableBasicTypesList = {
|
||||
name: 'NullableBasicTypesList',
|
||||
properties: {
|
||||
list: {type: 'list', objectType: 'NullableBasicTypesObject'},
|
||||
}
|
||||
};
|
||||
|
||||
const realm = new Realm({schema: [schemas.NullableBasicTypes, NullableBasicTypesList]});
|
||||
|
||||
const N = 5;
|
||||
|
||||
var list = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
list.push({
|
||||
intCol: i+1,
|
||||
floatCol: i+1,
|
||||
doubleCol: i+1,
|
||||
dateCol: new Date(i+1)
|
||||
});
|
||||
}
|
||||
|
||||
let object;
|
||||
realm.write(() => {
|
||||
object = realm.create('NullableBasicTypesList', {list: list});
|
||||
});
|
||||
|
||||
TestCase.assertEqual(object.list.length, N);
|
||||
|
||||
// bool, string & data columns don't support 'min'
|
||||
['bool', 'string', 'data'].forEach(colName => {
|
||||
TestCase.assertThrowsContaining(() => object.list.min(colName + 'Col'),
|
||||
`Cannot min property '${colName}Col': operation not supported for '${colName}' properties`);
|
||||
});
|
||||
|
||||
// bool, string & data columns don't support 'max'
|
||||
['bool', 'string', 'data'].forEach(colName => {
|
||||
TestCase.assertThrowsContaining(() => object.list.max(colName + 'Col'),
|
||||
`Cannot max property '${colName}Col': operation not supported for '${colName}' properties`);
|
||||
});
|
||||
|
||||
// bool, string, date & data columns don't support 'avg'
|
||||
['bool', 'string', 'date', 'data'].forEach(colName => {
|
||||
TestCase.assertThrowsContaining(() => object.list.avg(colName + 'Col'),
|
||||
`Cannot average property '${colName}Col': operation not supported for '${colName}' properties`);
|
||||
});
|
||||
|
||||
// bool, string, date & data columns don't support 'sum'
|
||||
['bool', 'string', 'date', 'data'].forEach(colName => {
|
||||
TestCase.assertThrowsContaining(() => object.list.sum(colName + 'Col'),
|
||||
`Cannot sum property '${colName}Col': operation not supported for '${colName}' properties`);
|
||||
});
|
||||
},
|
||||
|
||||
testListAggregateFunctionsWrongProperty: function() {
|
||||
const realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
let object;
|
||||
realm.write(() => {
|
||||
object = realm.create('PersonList', {list: [
|
||||
{name: 'Ari', age: 10},
|
||||
{name: 'Tim', age: 11},
|
||||
{name: 'Bjarne', age: 12},
|
||||
]});
|
||||
});
|
||||
|
||||
TestCase.assertThrowsContaining(() => object.list.min('foo'),
|
||||
"Property 'foo' does not exist on object 'PersonObject'");
|
||||
TestCase.assertThrowsContaining(() => object.list.max('foo'),
|
||||
"Property 'foo' does not exist on object 'PersonObject'");
|
||||
TestCase.assertThrowsContaining(() => object.list.sum('foo'),
|
||||
"Property 'foo' does not exist on object 'PersonObject'");
|
||||
TestCase.assertThrowsContaining(() => object.list.avg('foo'),
|
||||
"Property 'foo' does not exist on object 'PersonObject'");
|
||||
TestCase.assertThrowsContaining(() => object.list.min(),
|
||||
"JS value must be of type 'string', got (undefined)");
|
||||
TestCase.assertThrowsContaining(() => object.list.max(),
|
||||
"JS value must be of type 'string', got (undefined)");
|
||||
TestCase.assertThrowsContaining(() => object.list.sum(),
|
||||
"JS value must be of type 'string', got (undefined)");
|
||||
TestCase.assertThrowsContaining(() => object.list.avg(),
|
||||
"JS value must be of type 'string', got (undefined)");
|
||||
},
|
||||
};
|
||||
|
|
|
@ -67,20 +67,20 @@ function repeatUntil(fn, predicate) {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
testApplyAndGetGrantedPermissions() {
|
||||
return createUsersWithTestRealms(1)
|
||||
.then(([user]) => {
|
||||
return user.applyPermissions({ userId: '*' }, `/${user.identity}/test`, 'read')
|
||||
.then(repeatUntil(() => user.getGrantedPermissions('any'),
|
||||
permissions => permissions.length > 1))
|
||||
.then(permissions => {
|
||||
TestCase.assertEqual(permissions[1].path, `/${user.identity}/test`);
|
||||
TestCase.assertEqual(permissions[1].mayRead, true);
|
||||
TestCase.assertEqual(permissions[1].mayWrite, false);
|
||||
TestCase.assertEqual(permissions[1].mayManage, false);
|
||||
});
|
||||
});
|
||||
},
|
||||
// testApplyAndGetGrantedPermissions() {
|
||||
// return createUsersWithTestRealms(1)
|
||||
// .then(([user]) => {
|
||||
// return user.applyPermissions({ userId: '*' }, `/${user.identity}/test`, 'read')
|
||||
// .then(repeatUntil(() => user.getGrantedPermissions('any'),
|
||||
// permissions => permissions.length > 1))
|
||||
// .then(permissions => {
|
||||
// TestCase.assertEqual(permissions[1].path, `/${user.identity}/test`);
|
||||
// TestCase.assertEqual(permissions[1].mayRead, true);
|
||||
// TestCase.assertEqual(permissions[1].mayWrite, false);
|
||||
// TestCase.assertEqual(permissions[1].mayManage, false);
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
|
||||
testOfferPermissions() {
|
||||
return createUsersWithTestRealms(2)
|
||||
|
|
|
@ -1041,5 +1041,137 @@ module.exports = {
|
|||
const realm2 = new Realm(config);
|
||||
TestCase.assertEqual(realm2.objects('TestObject').length, 0);
|
||||
realm.close();
|
||||
}
|
||||
},
|
||||
|
||||
testRealmDeleteRealmIfMigrationNeededVersionChanged: function() {
|
||||
const schema = [{
|
||||
name: 'TestObject',
|
||||
properties: {
|
||||
prop0: 'string',
|
||||
prop1: 'int',
|
||||
}
|
||||
}];
|
||||
|
||||
var realm = new Realm({schema: schema});
|
||||
|
||||
realm.write(function() {
|
||||
realm.create('TestObject', ['stringValue', 1]);
|
||||
});
|
||||
|
||||
realm.close();
|
||||
|
||||
|
||||
realm = new Realm({schema: schema, deleteRealmIfMigrationNeeded: true, schemaVersion: 1, migration: undefined });
|
||||
|
||||
// object should be gone as Realm should get deleted
|
||||
TestCase.assertEqual(realm.objects('TestObject').length, 0);
|
||||
|
||||
// create a new object
|
||||
realm.write(function() {
|
||||
realm.create('TestObject', ['stringValue', 1]);
|
||||
});
|
||||
|
||||
realm.close();
|
||||
|
||||
var migrationWasCalled = false;
|
||||
realm = new Realm({schema: schema, deleteRealmIfMigrationNeeded: false, schemaVersion: 2, migration: function(oldRealm, newRealm) {
|
||||
migrationWasCalled = true;
|
||||
}});
|
||||
|
||||
// migration function should get called as deleteRealmIfMigrationNeeded is false
|
||||
TestCase.assertEqual(migrationWasCalled, true);
|
||||
|
||||
// object should be there because Realm shouldn't get deleted
|
||||
TestCase.assertEqual(realm.objects('TestObject').length, 1);
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testRealmDeleteRealmIfMigrationNeededSchemaChanged: function() {
|
||||
const schema = [{
|
||||
name: 'TestObject',
|
||||
properties: {
|
||||
prop0: 'string',
|
||||
prop1: 'int',
|
||||
}
|
||||
}];
|
||||
|
||||
const schema1 = [{
|
||||
name: 'TestObject',
|
||||
properties: {
|
||||
prop0: 'string',
|
||||
prop1: 'int',
|
||||
prop2: 'float',
|
||||
}
|
||||
}];
|
||||
|
||||
const schema2 = [{
|
||||
name: 'TestObject',
|
||||
properties: {
|
||||
prop0: 'string',
|
||||
prop1: 'int',
|
||||
prop2: 'float',
|
||||
prop3: 'double'
|
||||
}
|
||||
}];
|
||||
|
||||
var realm = new Realm({schema: schema});
|
||||
|
||||
realm.write(function() {
|
||||
realm.create('TestObject', {prop0: 'stringValue', prop1: 1});
|
||||
});
|
||||
|
||||
realm.close();
|
||||
|
||||
|
||||
// change schema
|
||||
realm = new Realm({schema: schema1, deleteRealmIfMigrationNeeded: true, migration: undefined});
|
||||
|
||||
// object should be gone as Realm should get deleted
|
||||
TestCase.assertEqual(realm.objects('TestObject').length, 0);
|
||||
|
||||
// create a new object
|
||||
realm.write(function() {
|
||||
realm.create('TestObject', {prop0: 'stringValue', prop1: 1, prop2: 1.0});
|
||||
});
|
||||
|
||||
realm.close();
|
||||
|
||||
|
||||
TestCase.assertThrows(function(e) {
|
||||
// updating schema without changing schemaVersion OR setting deleteRealmIfMigrationNeeded = true should raise an error
|
||||
new Realm({schema: schema2, deleteRealmIfMigrationNeeded: false, migration: function(oldRealm, newRealm) {}});
|
||||
});
|
||||
|
||||
var migrationWasCalled = false;
|
||||
|
||||
// change schema again, but increment schemaVersion
|
||||
realm = new Realm({schema: schema2, deleteRealmIfMigrationNeeded: false, schemaVersion: 1, migration: function(oldRealm, newRealm) {
|
||||
migrationWasCalled = true;
|
||||
}});
|
||||
|
||||
// migration function should get called as deleteRealmIfMigrationNeeded is false
|
||||
TestCase.assertEqual(migrationWasCalled, true);
|
||||
|
||||
// object should be there because Realm shouldn't get deleted
|
||||
TestCase.assertEqual(realm.objects('TestObject').length, 1);
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testRealmDeleteRealmIfMigrationNeededIncompatibleConfig: function() {
|
||||
const schema = [{
|
||||
name: 'TestObject',
|
||||
properties: {
|
||||
prop0: 'string',
|
||||
prop1: 'int',
|
||||
}
|
||||
}];
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
new Realm({schema: schema, deleteRealmIfMigrationNeeded: true, readOnly: true});
|
||||
}, "Cannot set 'deleteRealmIfMigrationNeeded' when 'readOnly' is set.")
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
new Realm({schema: schema, deleteRealmIfMigrationNeeded: true, migration: function(oldRealm, newRealm) {}});
|
||||
}, "Cannot include 'migration' when 'deleteRealmIfMigrationNeeded' is set.")
|
||||
},
|
||||
};
|
||||
|
|
|
@ -455,6 +455,402 @@ module.exports = {
|
|||
});
|
||||
resolve = r;
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
testResultsAggregateFunctions: function() {
|
||||
var realm = new Realm({ schema: [schemas.NullableBasicTypes] });
|
||||
const N = 50;
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
intCol: i+1,
|
||||
floatCol: i+1,
|
||||
doubleCol: i+1,
|
||||
dateCol: new Date(i+1)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var results = realm.objects('NullableBasicTypesObject');
|
||||
TestCase.assertEqual(results.length, N);
|
||||
|
||||
// int, float & double columns support all aggregate functions
|
||||
['intCol', 'floatCol', 'doubleCol'].forEach(colName => {
|
||||
TestCase.assertEqual(results.min(colName), 1);
|
||||
TestCase.assertEqual(results.max(colName), N);
|
||||
TestCase.assertEqual(results.sum(colName), N*(N+1)/2);
|
||||
TestCase.assertEqual(results.avg(colName), (N+1)/2);
|
||||
});
|
||||
|
||||
// date columns support only 'min' & 'max'
|
||||
TestCase.assertEqual(results.min('dateCol').getTime(), new Date(1).getTime());
|
||||
TestCase.assertEqual(results.max('dateCol').getTime(), new Date(N).getTime());
|
||||
},
|
||||
|
||||
testResultsAggregateFunctionsWithNullColumnValues: function() {
|
||||
var realm = new Realm({ schema: [schemas.NullableBasicTypes] });
|
||||
|
||||
const N = 50;
|
||||
const M = 10;
|
||||
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
intCol: i+1,
|
||||
floatCol: i+1,
|
||||
doubleCol: i+1,
|
||||
dateCol: new Date(i+1)
|
||||
});
|
||||
}
|
||||
|
||||
// add some null valued data, which should be ignored by the aggregate functions
|
||||
for(var j = 0; j < M; j++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
intCol: null,
|
||||
floatCol: null,
|
||||
doubleCol: null,
|
||||
dateCol: null
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var results = realm.objects('NullableBasicTypesObject');
|
||||
|
||||
TestCase.assertEqual(results.length, N + M);
|
||||
|
||||
// int, float & double columns support all aggregate functions
|
||||
// the M null valued objects should be ignored
|
||||
['intCol', 'floatCol', 'doubleCol'].forEach(colName => {
|
||||
TestCase.assertEqual(results.min(colName), 1);
|
||||
TestCase.assertEqual(results.max(colName), N);
|
||||
TestCase.assertEqual(results.sum(colName), N*(N+1)/2);
|
||||
TestCase.assertEqual(results.avg(colName), (N+1)/2);
|
||||
});
|
||||
|
||||
// date columns support only 'min' & 'max'
|
||||
TestCase.assertEqual(results.min('dateCol').getTime(), new Date(1).getTime());
|
||||
TestCase.assertEqual(results.max('dateCol').getTime(), new Date(N).getTime());
|
||||
|
||||
// call aggregate functions on empty results
|
||||
var emptyResults = realm.objects('NullableBasicTypesObject').filtered('intCol < 0');
|
||||
TestCase.assertEqual(emptyResults.length, 0);
|
||||
['intCol', 'floatCol', 'doubleCol'].forEach(colName => {
|
||||
TestCase.assertUndefined(emptyResults.min(colName));
|
||||
TestCase.assertUndefined(emptyResults.max(colName));
|
||||
TestCase.assertEqual(emptyResults.sum(colName), 0);
|
||||
TestCase.assertUndefined(emptyResults.avg(colName));
|
||||
});
|
||||
|
||||
TestCase.assertUndefined(emptyResults.min('dateCol'));
|
||||
TestCase.assertUndefined(emptyResults.max('dateCol'));
|
||||
},
|
||||
|
||||
testResultsAggregateFunctionsUnsupported: function() {
|
||||
var realm = new Realm({ schema: [schemas.NullableBasicTypes] });
|
||||
realm.write(() => {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
boolCol: true,
|
||||
stringCol: "hello",
|
||||
dataCol: new ArrayBuffer(12),
|
||||
});
|
||||
});
|
||||
|
||||
var results = realm.objects('NullableBasicTypesObject');
|
||||
|
||||
// bool, string & data columns don't support 'min'
|
||||
['boolCol', 'stringCol', 'dataCol'].forEach(colName => {
|
||||
TestCase.assertThrows(function() {
|
||||
results.min(colName);
|
||||
}
|
||||
)});
|
||||
|
||||
// bool, string & data columns don't support 'max'
|
||||
['boolCol', 'stringCol', 'dataCol'].forEach(colName => {
|
||||
TestCase.assertThrows(function() {
|
||||
results.max(colName);
|
||||
}
|
||||
)});
|
||||
|
||||
// bool, string, date & data columns don't support 'avg'
|
||||
['boolCol', 'stringCol', 'dateCol', 'dataCol'].forEach(colName => {
|
||||
TestCase.assertThrows(function() {
|
||||
results.avg(colName);
|
||||
}
|
||||
)});
|
||||
|
||||
// bool, string, date & data columns don't support 'sum'
|
||||
['boolCol', 'stringCol', 'dateCol', 'dataCol'].forEach(colName => {
|
||||
TestCase.assertThrows(function() {
|
||||
results.sum(colName);
|
||||
}
|
||||
)});
|
||||
},
|
||||
|
||||
testResultsAggregateFunctionsWrongProperty: function() {
|
||||
var realm = new Realm({ schema: [ schemas.TestObject ]});
|
||||
realm.write(() => {
|
||||
realm.create('TestObject', { doubleCol: 42 });
|
||||
});
|
||||
var results = realm.objects('TestObject');
|
||||
TestCase.assertThrows(function() {
|
||||
results.min('foo')
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
results.max('foo')
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
results.sum('foo')
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
results.avg('foo')
|
||||
});
|
||||
},
|
||||
|
||||
testIterator: function() {
|
||||
var realm = new Realm({ schema: [ schemas.TestObject ]});
|
||||
realm.write(() => {
|
||||
realm.create('TestObject', { doubleCol: 2 });
|
||||
realm.create('TestObject', { doubleCol: 3 });
|
||||
});
|
||||
|
||||
var results = realm.objects('TestObject').filtered('doubleCol >= 2');
|
||||
TestCase.assertEqual(results.length, 2);
|
||||
var calls = 0;
|
||||
for(let obj of results) {
|
||||
realm.write(() => {
|
||||
obj.doubleCol = 1;
|
||||
});
|
||||
calls++;
|
||||
}
|
||||
TestCase.assertEqual(results.length, 0);
|
||||
TestCase.assertEqual(calls, 2);
|
||||
},
|
||||
|
||||
testResultsUpdate: function() {
|
||||
const N = 5;
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', { intCol: 10 });
|
||||
}
|
||||
});
|
||||
|
||||
// update should work on a basic result set
|
||||
var results = realm.objects('NullableBasicTypesObject');
|
||||
TestCase.assertEqual(results.length, 5);
|
||||
realm.write(() => {
|
||||
results.update('intCol', 20);
|
||||
});
|
||||
TestCase.assertEqual(results.length, 5);
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('intCol = 20').length, 5);
|
||||
|
||||
// update should work on a filtered result set
|
||||
results = realm.objects('NullableBasicTypesObject').filtered('intCol = 20');
|
||||
realm.write(() => {
|
||||
results.update('intCol', 10);
|
||||
});
|
||||
TestCase.assertEqual(results.length, 0);
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('intCol = 10').length, 5);
|
||||
|
||||
// update should work on a sorted result set
|
||||
results = realm.objects('NullableBasicTypesObject').filtered('intCol == 10').sorted('intCol');
|
||||
realm.write(() => {
|
||||
results.update('intCol', 20);
|
||||
});
|
||||
TestCase.assertEqual(results.length, 0);
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('intCol = 20').length, 5);
|
||||
|
||||
// update should work on a result snapshot
|
||||
results = realm.objects('NullableBasicTypesObject').filtered('intCol == 20').snapshot();
|
||||
realm.write(() => {
|
||||
results.update('intCol', 10);
|
||||
});
|
||||
TestCase.assertEqual(results.length, 5); // snapshot length should not change
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('intCol = 10').length, 5);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultsUpdateDataTypes: function() {
|
||||
const N = 5;
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
boolCol: false,
|
||||
stringCol: 'hello',
|
||||
intCol: 10,
|
||||
floatCol: 10.0,
|
||||
doubleCol: 10.0,
|
||||
dateCol: new Date(10)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const testCases = [
|
||||
// col name, initial filter, initial filter pre-update count, initial filter post-update count, updated value, updated filter, updated filter post-update count
|
||||
[ 'boolCol', 'boolCol = false', N, 0, true, 'boolCol = true', N ],
|
||||
[ 'stringCol', 'stringCol = "hello"', N, 0, 'world', 'stringCol = "world"', N ],
|
||||
[ 'intCol', 'intCol = 10', N, 0, 20, 'intCol = 20', N ],
|
||||
[ 'floatCol', 'floatCol = 10.0', N, 0, 20.0, 'floatCol = 20.0', N ],
|
||||
[ 'doubleCol', 'doubleCol = 10.0', N, 0, 20.0, 'doubleCol = 20.0', N ],
|
||||
];
|
||||
|
||||
testCases.forEach(function(testCase) {
|
||||
var results = realm.objects('NullableBasicTypesObject').filtered(testCase[1]);
|
||||
TestCase.assertEqual(results.length, testCase[2]);
|
||||
|
||||
realm.write(() => {
|
||||
results.update(testCase[0], testCase[4]);
|
||||
});
|
||||
|
||||
TestCase.assertEqual(results.length, testCase[3]);
|
||||
|
||||
results = realm.objects('NullableBasicTypesObject').filtered(testCase[5]);
|
||||
TestCase.assertEqual(results.length, testCase[6]);
|
||||
});
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultUpdateDateColumn: function() {
|
||||
const N = 5;
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
|
||||
// date column
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
dateCol: new Date(1000)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var results = realm.objects('NullableBasicTypesObject').filtered('dateCol = $0', new Date(1000));
|
||||
TestCase.assertEqual(results.length, N);
|
||||
|
||||
realm.write(() => {
|
||||
results.update('dateCol', new Date(2000));
|
||||
});
|
||||
|
||||
TestCase.assertEqual(results.length, 0);
|
||||
results = realm.objects('NullableBasicTypesObject').filtered('dateCol = $0', new Date(2000));
|
||||
TestCase.assertEqual(results.length, N);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultsUpdateDataColumn: function() {
|
||||
const N = 5;
|
||||
|
||||
var RANDOM_DATA = new Uint8Array([
|
||||
0xd8, 0x21, 0xd6, 0xe8, 0x00, 0x57, 0xbc, 0xb2, 0x6a, 0x15, 0x77, 0x30, 0xac, 0x77, 0x96, 0xd9,
|
||||
0x67, 0x1e, 0x40, 0xa7, 0x6d, 0x52, 0x83, 0xda, 0x07, 0x29, 0x9c, 0x70, 0x38, 0x48, 0x4e, 0xff,
|
||||
]);
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
|
||||
// date column
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
dataCol: null
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var results = realm.objects('NullableBasicTypesObject');
|
||||
TestCase.assertEqual(results.length, N);
|
||||
|
||||
realm.write(() => {
|
||||
results.update('dataCol', RANDOM_DATA);
|
||||
});
|
||||
|
||||
for(var i = 0; i < results.length; i++) {
|
||||
TestCase.assertArraysEqual(new Uint8Array(results[i].dataCol), RANDOM_DATA);
|
||||
}
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultsUpdateEmpty() {
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
|
||||
var emptyResults = realm.objects('NullableBasicTypesObject').filtered('stringCol = "hello"');
|
||||
TestCase.assertEqual(emptyResults.length, 0);
|
||||
|
||||
realm.write(() => {
|
||||
emptyResults.update('stringCol', 'no-op');
|
||||
});
|
||||
|
||||
TestCase.assertEqual(emptyResults.length, 0);
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('stringCol = "no-op"').length, 0);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultsUpdateInvalidated() {
|
||||
var realm = new Realm({schema: [schemas.TestObject]});
|
||||
realm.write(function() {
|
||||
for (var i = 10; i > 0; i--) {
|
||||
realm.create('TestObject', [i]);
|
||||
}
|
||||
});
|
||||
|
||||
var resultsVariants = [
|
||||
realm.objects('TestObject'),
|
||||
realm.objects('TestObject').filtered('doubleCol > 1'),
|
||||
realm.objects('TestObject').filtered('doubleCol > 1').sorted('doubleCol'),
|
||||
realm.objects('TestObject').filtered('doubleCol > 1').snapshot()
|
||||
];
|
||||
|
||||
// test isValid
|
||||
resultsVariants.forEach(function(objects) {
|
||||
TestCase.assertEqual(objects.isValid(), true);
|
||||
});
|
||||
|
||||
// close and test update
|
||||
realm.close();
|
||||
realm = new Realm({
|
||||
schemaVersion: 1,
|
||||
schema: [schemas.TestObject, schemas.BasicTypes]
|
||||
});
|
||||
|
||||
resultsVariants.forEach(function(objects) {
|
||||
TestCase.assertEqual(objects.isValid(), false);
|
||||
TestCase.assertThrows(function() { objects.update('doubleCol', 42); });
|
||||
});
|
||||
},
|
||||
|
||||
testResultsUpdateWrongProperty() {
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
|
||||
const N = 5;
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
stringCol: 'hello'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var results = realm.objects('NullableBasicTypesObject').filtered('stringCol = "hello"');
|
||||
TestCase.assertEqual(results.length, N);
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
realm.write(() => {
|
||||
results.update('unknownCol', 'world');
|
||||
});
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
results.update('stringCol', 'world');
|
||||
});
|
||||
|
||||
realm.close();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -252,6 +252,19 @@ exports.NullQueryObject = {
|
|||
]
|
||||
};
|
||||
|
||||
exports.NullableBasicTypes = {
|
||||
name: 'NullableBasicTypesObject',
|
||||
properties: [
|
||||
{name: 'boolCol', type: 'bool?'},
|
||||
{name: 'intCol', type: 'int?'},
|
||||
{name: 'floatCol', type: 'float?'},
|
||||
{name: 'doubleCol', type: 'double?'},
|
||||
{name: 'stringCol', type: 'string?'},
|
||||
{name: 'dateCol', type: 'date?'},
|
||||
{name: 'dataCol', type: 'data?'},
|
||||
]
|
||||
};
|
||||
|
||||
exports.DateObject = {
|
||||
name: 'Date',
|
||||
properties: {
|
||||
|
|
|
@ -34,12 +34,14 @@ function node_require(module) {
|
|||
let tmp;
|
||||
let fs;
|
||||
let execFile;
|
||||
let path;
|
||||
|
||||
if (isNodeProccess) {
|
||||
tmp = node_require('tmp');
|
||||
fs = node_require('fs');
|
||||
execFile = node_require('child_process').execFile;
|
||||
tmp.setGracefulCleanup();
|
||||
path = node_require("path");
|
||||
}
|
||||
|
||||
|
||||
|
@ -76,6 +78,14 @@ function promisifiedLogin(server, username, password) {
|
|||
});
|
||||
}
|
||||
|
||||
function copyFileToTempDir(filename) {
|
||||
let tmpDir = tmp.dirSync();
|
||||
let content = fs.readFileSync(filename);
|
||||
let tmpFile = tmp.fileSync({ dir: tmpDir.name });
|
||||
fs.appendFileSync(tmpFile.fd, content);
|
||||
return tmpFile.name;
|
||||
}
|
||||
|
||||
function runOutOfProcess(nodeJsFilePath) {
|
||||
var nodeArgs = Array.prototype.slice.call(arguments);
|
||||
let tmpDir = tmp.dirSync();
|
||||
|
@ -104,7 +114,6 @@ function runOutOfProcess(nodeJsFilePath) {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
testLocalRealmHasNoSession() {
|
||||
let realm = new Realm();
|
||||
TestCase.assertNull(realm.syncSession);
|
||||
|
@ -240,89 +249,6 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmOpen() {
|
||||
if (!isNodeProccess) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
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 => {
|
||||
const accessTokenRefreshed = this;
|
||||
let successCounter = 0;
|
||||
let progressNotificationCalled = false;
|
||||
let config = {
|
||||
sync: {
|
||||
user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
_onDownloadProgress: (transferred, total) => {
|
||||
progressNotificationCalled = true
|
||||
},
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||
};
|
||||
|
||||
return Realm.open(config)
|
||||
.then(realm => {
|
||||
return realm.syncSession;
|
||||
}).then(session => {
|
||||
TestCase.assertTrue(progressNotificationCalled, "Progress notification not called for Realm.open");
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmOpenAsync() {
|
||||
if (!isNodeProccess) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const username = uuid();
|
||||
const realmName = uuid();
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
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 => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let progressNotificationCalled = false;
|
||||
let config = {
|
||||
sync: { user, url: `realm://localhost:9080/~/${realmName}`,
|
||||
_onDownloadProgress: (transferred, total) => {
|
||||
progressNotificationCalled = true
|
||||
},
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||
};
|
||||
|
||||
Realm.openAsync(config, (error, realm) => {
|
||||
try {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
TestCase.assertTrue(progressNotificationCalled, "Progress notification not called for Realm.openAsync");
|
||||
resolve();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testRealmOpenAsyncNoSchema() {
|
||||
if (!isNodeProccess) {
|
||||
return Promise.resolve();
|
||||
|
@ -461,6 +387,130 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
testIncompatibleSyncedRealmOpen() {
|
||||
let realm = "sync-v1.realm";
|
||||
if (isNodeProccess) {
|
||||
realm = copyFileToTempDir(path.join(process.cwd(), "data", realm));
|
||||
}
|
||||
else {
|
||||
//copy the bundled RN realm files for the test
|
||||
Realm.copyBundledRealmFiles();
|
||||
}
|
||||
|
||||
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
const config = {
|
||||
path: realm,
|
||||
sync: {
|
||||
user,
|
||||
error : err => cosole.log(err),
|
||||
url: 'realm://localhost:9080/~/sync-v1'
|
||||
}
|
||||
};
|
||||
|
||||
Realm.open(config)
|
||||
.then(realm =>
|
||||
_reject("Should fail with IncompatibleSyncedRealmError"))
|
||||
.catch(e => {
|
||||
if (e.name == "IncompatibleSyncedRealmError") {
|
||||
const backupRealm = new Realm(e.configuration);
|
||||
TestCase.assertEqual(backupRealm.objects('Dog').length, 3);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
function printObject(o) {
|
||||
var out = '';
|
||||
for (var p in o) {
|
||||
out += p + ': ' + o[p] + '\n';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
_reject("Failed with unexpected error " + printObject(e));
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testIncompatibleSyncedRealmOpenAsync() {
|
||||
let realm = "sync-v1.realm";
|
||||
if (isNodeProccess) {
|
||||
realm = copyFileToTempDir(path.join(process.cwd(), "data", realm));
|
||||
}
|
||||
else {
|
||||
//copy the bundled RN realm files for the test
|
||||
Realm.copyBundledRealmFiles();
|
||||
}
|
||||
|
||||
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
const config = {
|
||||
path: realm,
|
||||
sync: {
|
||||
user,
|
||||
error : err => cosole.log(err),
|
||||
url: 'realm://localhost:9080/~/sync-v1'
|
||||
}
|
||||
};
|
||||
|
||||
Realm.openAsync(config, (error, realm) => {
|
||||
if (!error) {
|
||||
_reject("Should fail with IncompatibleSyncedRealmError");
|
||||
return;
|
||||
}
|
||||
|
||||
if (error.name == "IncompatibleSyncedRealmError") {
|
||||
const backupRealm = new Realm(error.configuration);
|
||||
TestCase.assertEqual(backupRealm.objects('Dog').length, 3);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
_reject("Failed with unexpected error" + JSON.stringify(error));
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testIncompatibleSyncedRealmConsructor() {
|
||||
let realm = "sync-v1.realm";
|
||||
if (isNodeProccess) {
|
||||
realm = copyFileToTempDir(path.join(process.cwd(), "data", realm));
|
||||
}
|
||||
else {
|
||||
//copy the bundled RN realm files for the test
|
||||
Realm.copyBundledRealmFiles();
|
||||
}
|
||||
|
||||
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
const config = {
|
||||
path: realm,
|
||||
sync: {
|
||||
user,
|
||||
error : err => cosole.log(err),
|
||||
url: 'realm://localhost:9080/~/sync-v1'
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const realm = new Realm(config);
|
||||
_reject("Should fail with IncompatibleSyncedRealmError");
|
||||
}
|
||||
catch (e) {
|
||||
if (e.name == "IncompatibleSyncedRealmError") {
|
||||
const backupRealm = new Realm(e.configuration);
|
||||
TestCase.assertEqual(backupRealm.objects('Dog').length, 3);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
_reject("Failed with unexpected error" + JSON.stringify(e));
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmConstructor() {
|
||||
if (!isNodeProccess) {
|
||||
|
@ -564,7 +614,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmOpen2() {
|
||||
testProgressNotificationsForRealmOpen() {
|
||||
if (!isNodeProccess) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -603,7 +653,7 @@ module.exports = {
|
|||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmOpenAsync2() {
|
||||
testProgressNotificationsForRealmOpenAsync() {
|
||||
if (!isNodeProccess) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -646,4 +696,71 @@ module.exports = {
|
|||
});
|
||||
});
|
||||
},
|
||||
|
||||
testPartialSync() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
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);
|
||||
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");
|
||||
});
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
testClientReset() {
|
||||
// FIXME: try to enable for React Native
|
||||
if (!isNodeProccess) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
var realm;
|
||||
const config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
|
||||
config.sync.error = (sender, error) => {
|
||||
try {
|
||||
TestCase.assertEqual(error.code, 7); // 7 -> client reset
|
||||
TestCase.assertDefined(error.config);
|
||||
TestCase.assertNotEqual(error.config.path, '');
|
||||
const original_path = realm.path;
|
||||
realm.close();
|
||||
Realm.Sync.initiateClientReset(original_path);
|
||||
// copy required objects from Realm at error.config.path
|
||||
resolve();
|
||||
}
|
||||
catch (e) {
|
||||
_reject(e);
|
||||
}
|
||||
};
|
||||
realm = new Realm(config);
|
||||
const session = realm.syncSession;
|
||||
|
||||
TestCase.assertEqual(session.config.error, config.sync.error);
|
||||
session._simulateError(211, 'ClientReset'); // 211 -> divering histories
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
855301CF1E20069D00FF108E /* dates-v3.realm in Resources */ = {isa = PBXBuildFile; fileRef = 855301CD1E20069D00FF108E /* dates-v3.realm */; };
|
||||
855301D01E20069D00FF108E /* dates-v5.realm in Resources */ = {isa = PBXBuildFile; fileRef = 855301CE1E20069D00FF108E /* dates-v5.realm */; };
|
||||
855301D31E2006F700FF108E /* RealmReactTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 855301D11E2006F400FF108E /* RealmReactTests.m */; };
|
||||
A4CEF4BB1F7F862D00BA3B26 /* sync-v1.realm in Resources */ = {isa = PBXBuildFile; fileRef = A4CEF4BA1F7F862D00BA3B26 /* sync-v1.realm */; };
|
||||
E2050A7A5BE14CEA9A9E0722 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B37A7097A134D5CBB4C462A /* libc++.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
@ -253,6 +254,7 @@
|
|||
855301CE1E20069D00FF108E /* dates-v5.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dates-v5.realm"; sourceTree = "<group>"; };
|
||||
855301D11E2006F400FF108E /* RealmReactTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmReactTests.m; path = ReactTests/RealmReactTests.m; sourceTree = "<group>"; };
|
||||
8B37A7097A134D5CBB4C462A /* libc++.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
A4CEF4BA1F7F862D00BA3B26 /* sync-v1.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "sync-v1.realm"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -476,6 +478,7 @@
|
|||
855301CC1E20069D00FF108E /* data */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A4CEF4BA1F7F862D00BA3B26 /* sync-v1.realm */,
|
||||
855301CD1E20069D00FF108E /* dates-v3.realm */,
|
||||
855301CE1E20069D00FF108E /* dates-v5.realm */,
|
||||
);
|
||||
|
@ -805,6 +808,7 @@
|
|||
files = (
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
A4CEF4BB1F7F862D00BA3B26 /* sync-v1.realm in Resources */,
|
||||
855301D01E20069D00FF108E /* dates-v5.realm in Resources */,
|
||||
855301CF1E20069D00FF108E /* dates-v3.realm in Resources */,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue