mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-22 12:30:58 +00:00
Merge branch 'master' of github.com:realm/realm-js into 2.0.x
This commit is contained in:
commit
eeb78d8e1b
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@ -54,6 +54,7 @@
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"name": "Attach to Port",
|
||||
"protocol": "legacy",
|
||||
"address": "localhost",
|
||||
"port": 5858
|
||||
}
|
||||
|
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,14 +1,29 @@
|
||||
2.0.0 Release notes (2017-9-8)
|
||||
2.0.0 Release notes
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* Updating core, sync, object store.
|
||||
|
||||
### Enhancements
|
||||
* Improve performance of the RPC worker for chrome debugging.
|
||||
* Added `Realm.deleteFile` for deleting a Realm (#363).
|
||||
* None
|
||||
|
||||
### Bug fixes
|
||||
* Adding missing TypeScript declation (#1283).
|
||||
* None
|
||||
|
||||
1.12.0 Release notes (2017-9-14)
|
||||
=============================================================
|
||||
|
||||
### Enhancements
|
||||
* Improve performance of the RPC worker for chrome debugging.
|
||||
* Added Progress API `realm.syncSession.addProgressNotification` and `realm.syncSession.removeProgressNotification`
|
||||
* Added additional parameter for `Realm.open` and `Realm.openAsync` for download progress notifications
|
||||
* Added `Realm.deleteFile` for deleting a Realm (#363).
|
||||
* Added `Realm.deleteModel` for deleting a Realm model in a migration (#573).
|
||||
* Added support for in-memory Realms.
|
||||
* `Realm.Sync.User.login`, `Realm.Sync.User.register`, and `Realm.Sync.User.registerWithProvider` return Promises and deprecate the callback style for them. Callbacks will continue to work for backward compatibility.
|
||||
|
||||
### Bug fixes
|
||||
* Adding missing TypeScript definitions; Permissions (#1283), `setFeatureToken()`, and instructions (#1298).
|
||||
* Removed `loginWithProvider` from TypeScript definition files. This API never existed and was incorrectly added.
|
||||
|
||||
1.11.1 Release notes (2017-9-1)
|
||||
=============================================================
|
||||
|
@ -87,4 +87,5 @@ To finish adding your new function, you will have to add your function a few pla
|
||||
|
||||
* In `lib/index.d.ts` you add the TypeScript declaration
|
||||
* Documentation is added in `docs/realm.js`
|
||||
* Add your function to `lib/browser/index.js` in order to enable it in the Chrome Debugger
|
||||
* Add your function to `lib/browser/index.js` in order to enable it in the Chrome Debugger
|
||||
* Add an entry to `CHANGELOG.md` if applicable (Breaking changes/Enhancements/Bug fixes)
|
||||
|
@ -95,7 +95,7 @@ class Realm {
|
||||
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully
|
||||
* synchronized before it is available.
|
||||
* @param {Realm~Configuration} config
|
||||
* @returns {Promise} - a promise that will be resolved with the realm instance when it's available.
|
||||
* @returns {ProgressPromise} - a promise that will be resolved with the realm instance when it's available.
|
||||
*/
|
||||
static open(config) {}
|
||||
|
||||
@ -104,9 +104,10 @@ class Realm {
|
||||
* synchronized before it is available.
|
||||
* @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.
|
||||
*/
|
||||
static openAsync(config, callback) {}
|
||||
static openAsync(config, callback, progressCallback) {}
|
||||
|
||||
/**
|
||||
* Closes this Realm so it may be re-opened with a newer schema version.
|
||||
@ -132,6 +133,12 @@ class Realm {
|
||||
*/
|
||||
delete(object) {}
|
||||
|
||||
/**
|
||||
* Deletes a Realm model, including all of its objects.
|
||||
* @param {string} name - the model name
|
||||
*/
|
||||
deleteModel(name) {}
|
||||
|
||||
/**
|
||||
* **WARNING:** This will delete **all** objects in the Realm!
|
||||
*/
|
||||
@ -271,6 +278,11 @@ Realm.defaultPath;
|
||||
* will be skipped if another process is accessing it.
|
||||
* @property {string} [path={@link Realm.defaultPath}] - The path to the file where the
|
||||
* Realm database should be stored.
|
||||
* @property {boolean} [inMemory=false] - Specifies if this Realm should be opened in-memory. This
|
||||
* still requires a path (can be the default path) to identify the Realm so other processes can
|
||||
* open the same Realm. The file will also be used as swap space if the Realm becomes bigger than
|
||||
* what fits in memory, but it is not persistent and will be removed when the last instance
|
||||
* is closed.
|
||||
* @property {boolean} [readOnly=false] - Specifies if this Realm should be opened as read-only.
|
||||
* @property {Array<Realm~ObjectClass|Realm~ObjectSchema>} [schema] - Specifies all the
|
||||
* object types in this Realm. **Required** when first creating a Realm at this `path`.
|
||||
@ -279,7 +291,11 @@ Realm.defaultPath;
|
||||
* @property {Object} [sync] - Sync configuration parameters with the following
|
||||
* child properties:
|
||||
* - `user` - A `User` object obtained by calling `Realm.Sync.User.login`
|
||||
* - `url` - A `string` which contains a valid Realm Sync url
|
||||
* - `url` - A `string` which contains a valid Realm Sync url
|
||||
* - `error` - A callback function which is called in error situations
|
||||
* - `validate_ssl` - Indicating if SSL certificates must be validated
|
||||
* - `ssl_trust_certificate_path` - A path where to find trusted SSL certificates
|
||||
* The `error` callback can take up to four optional arguments: `message`, `isFatal`, `category`, and `code`.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
31
docs/sync.js
31
docs/sync.js
@ -135,9 +135,10 @@ class User {
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @param {function(error, user)} callback - called with the following arguments:
|
||||
* @param {function(error, user)} [callback] - called with the following arguments:
|
||||
* - `error` - an Error object is provided on failure
|
||||
* - `user` - a valid User object on success
|
||||
* @returns {void|Promise<User>} Returns a promise with a user if the callback was not specified
|
||||
*/
|
||||
static login(server, username, password, callback) {}
|
||||
|
||||
@ -148,9 +149,10 @@ class User {
|
||||
* @param {string} options.provider - The provider type
|
||||
* @param {string} options.providerToken - The access token for the given provider
|
||||
* @param {object} [options.userInfo] - A map containing additional data required by the provider
|
||||
* @param {function(error, User)} callback - called with the following arguments:
|
||||
* @param {function(error, User)} [callback] - an optional callback called with the following arguments:
|
||||
* - `error` - an Error object is provided on failure
|
||||
* - `user` - a valid User object on success
|
||||
* @return {void|Promise<User>} Returns a promise with a user if the callback was not specified
|
||||
*/
|
||||
static registerWithProvider(server, options, callback) {}
|
||||
|
||||
@ -159,9 +161,10 @@ class User {
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @param {function(error, user)} callback - called with the following arguments:
|
||||
* @param {function(error, user)} [callback] - called with the following arguments:
|
||||
* - `error` - an Error object is provided on failure
|
||||
* - `user` - a valid User object on success
|
||||
* @return {void|Promise<User>} Returns a promise with a user if the callback was not specified
|
||||
*/
|
||||
static register(server, username, password, callback) {}
|
||||
|
||||
@ -334,6 +337,28 @@ class Session {
|
||||
* @type {string}
|
||||
*/
|
||||
get state() {}
|
||||
|
||||
/**
|
||||
* Register a progress notification callback on a session object
|
||||
* @param {string} direction - The progress direction to register for.
|
||||
* Can be either:
|
||||
* - `download` - report download progress
|
||||
* - `upload` - report upload progress
|
||||
* @param {string} mode - The progress notification mode to use for the registration.
|
||||
* Can be either:
|
||||
* - `reportIndefinitely` - the registration will stay active until the callback is unregistered
|
||||
* - `forCurrentlyOutstandingWork` - the registration will be active until only the currently transferable bytes are synced
|
||||
* @param {callback(transferred, transferable)} callback - called with the following arguments:
|
||||
* - `transferred` - the current number of bytes already transferred
|
||||
* - `transferable` - the total number of transferable bytes (the number of bytes already transferred plus the number of bytes pending transfer)
|
||||
*/
|
||||
addProgressNotification(direction, mode, progressCallback) {}
|
||||
|
||||
/** Unregister a progress notification callback that was previously registered with addProgressNotification.
|
||||
* Calling the function multiple times with the same callback is ignored.
|
||||
* @param {callback(transferred, transferable)} callback - a previously registered progress callback
|
||||
*/
|
||||
removeProgressNotification(progressCallback) {}
|
||||
}
|
||||
|
||||
|
||||
|
@ -55,6 +55,7 @@ function setupRealm(realm, realmId) {
|
||||
'empty',
|
||||
'path',
|
||||
'readOnly',
|
||||
'inMemory',
|
||||
'schema',
|
||||
'schemaVersion',
|
||||
'syncSession',
|
||||
@ -131,6 +132,7 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
||||
// Mutating methods:
|
||||
util.createMethods(Realm.prototype, objectTypes.REALM, [
|
||||
'delete',
|
||||
'deleteModel',
|
||||
'deleteAll',
|
||||
'write',
|
||||
'compact',
|
||||
@ -187,7 +189,8 @@ Object.defineProperties(Realm, {
|
||||
},
|
||||
},
|
||||
_waitForDownload: {
|
||||
value: function(_config, callback) {
|
||||
value: function(_config, sessionCallback, callback) {
|
||||
sessionCallback();
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
@ -22,7 +22,9 @@ import { keys, objectTypes } from './constants';
|
||||
import { getterForProperty, createMethods } from './util';
|
||||
import { deserialize } from './rpc';
|
||||
|
||||
export default class Session { }
|
||||
export default class Session {
|
||||
|
||||
}
|
||||
|
||||
Object.defineProperties(Session.prototype, {
|
||||
url: { get: getterForProperty('url') },
|
||||
@ -31,7 +33,9 @@ Object.defineProperties(Session.prototype, {
|
||||
|
||||
createMethods(Session.prototype, objectTypes.SESSION, [
|
||||
'_refreshAccessToken',
|
||||
'_simulateError'
|
||||
'_simulateError',
|
||||
'addProgressNotification',
|
||||
'removeProgressNotification'
|
||||
]);
|
||||
|
||||
export function createSession(realmId, info) {
|
||||
|
@ -40,7 +40,7 @@ export function createMethod(type, name, mutates) {
|
||||
let id = this[keys.id];
|
||||
|
||||
if (!realmId || !id) {
|
||||
throw new TypeError(name + ' method was not called a Realm object!');
|
||||
throw new TypeError(name + ' method was not called on a Realm object!');
|
||||
}
|
||||
if (this[keys.type] !== type) {
|
||||
throw new TypeError(name + ' method was called on an object of the wrong type!');
|
||||
|
@ -19,7 +19,12 @@
|
||||
'use strict';
|
||||
|
||||
function AuthError(problem) {
|
||||
Error.call(this, problem.title);
|
||||
const error = Error.call(this, problem.title);
|
||||
|
||||
this.name = 'AuthError';
|
||||
this.message = error.message;
|
||||
this.stack = error.stack;
|
||||
|
||||
Object.assign(this, problem);
|
||||
}
|
||||
|
||||
|
@ -43,28 +43,52 @@ module.exports = function(realmConstructor) {
|
||||
//Add async open API
|
||||
Object.defineProperties(realmConstructor, getOwnPropertyDescriptors({
|
||||
open(config) {
|
||||
return new Promise((resolve, reject) => {
|
||||
realmConstructor._waitForDownload(config, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
let syncedRealm = new this(config);
|
||||
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented
|
||||
setTimeout(() => { resolve(syncedRealm); }, 1);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
let syncSession;
|
||||
let promise = new Promise((resolve, reject) => {
|
||||
realmConstructor._waitForDownload(config,
|
||||
(session) => {
|
||||
syncSession = session;
|
||||
},
|
||||
(error) => {
|
||||
if (error) {
|
||||
setTimeout(() => { reject(error); }, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
else {
|
||||
try {
|
||||
let syncedRealm = new this(config);
|
||||
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented
|
||||
setTimeout(() => { resolve(syncedRealm); }, 1);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
promise.progress = (callback) => {
|
||||
if (syncSession) {
|
||||
syncSession.addProgressNotification('download', 'forCurrentlyOutstandingWork', callback);
|
||||
}
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
return promise;
|
||||
},
|
||||
|
||||
openAsync(config, callback) {
|
||||
realmConstructor._waitForDownload(config, (error) => {
|
||||
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) {
|
||||
callback(error);
|
||||
setTimeout(() => { callback(error); }, 1);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
@ -72,7 +96,7 @@ module.exports = function(realmConstructor) {
|
||||
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented
|
||||
setTimeout(() => { callback(null, syncedRealm); }, 1);
|
||||
} catch (e) {
|
||||
callback(e);
|
||||
setTimeout(() => { callback(e); }, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
53
lib/index.d.ts
vendored
53
lib/index.d.ts
vendored
@ -80,6 +80,7 @@ declare namespace Realm {
|
||||
shouldCompactOnLaunch?: (totalBytes: number, usedBytes: number) => boolean;
|
||||
path?: string;
|
||||
readOnly?: boolean;
|
||||
inMemory?: boolean;
|
||||
schema?: ObjectClass[] | ObjectSchema[];
|
||||
schemaVersion?: number;
|
||||
sync?: Realm.Sync.SyncConfiguration;
|
||||
@ -264,10 +265,25 @@ declare namespace Realm.Sync {
|
||||
readonly server: string;
|
||||
readonly token: string;
|
||||
static adminUser(adminToken: string, server?: string): User;
|
||||
|
||||
/**
|
||||
* @deprecated, to be removed in future versions
|
||||
*/
|
||||
static login(server: string, username: string, password: string, callback: (error: any, user: User) => void): void;
|
||||
static loginWithProvider(server: string, provider: string, providerToken: string, callback: (error: any, user: User) => void): void;
|
||||
static login(server: string, username: string, password: string): Promise<Realm.Sync.User>;
|
||||
|
||||
/**
|
||||
* @deprecated, to be removed in future versions
|
||||
*/
|
||||
static register(server: string, username: string, password: string, callback: (error: any, user: User) => void): void;
|
||||
static register(server: string, username: string, password: string): Promise<Realm.Sync.User>;
|
||||
|
||||
/**
|
||||
* @deprecated, to be removed in versions
|
||||
*/
|
||||
static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }, callback: (error: Error | null, user: User | null) => void): void;
|
||||
static registerWithProvider(server: string, options: { provider: string, providerToken: string, userInfo: any }): Promise<Realm.Sync.User>;
|
||||
|
||||
logout(): void;
|
||||
openManagementRealm(): Realm;
|
||||
retrieveAccount(provider: string, username: string): Promise<Account>;
|
||||
@ -325,13 +341,20 @@ declare namespace Realm.Sync {
|
||||
expiresAt?: Date;
|
||||
}
|
||||
|
||||
type ErrorCallback = (message?: string, isFatal?: boolean, category?: string, code?: number) => void;
|
||||
|
||||
interface SyncConfiguration {
|
||||
user: User;
|
||||
url: string;
|
||||
validate_ssl?: boolean;
|
||||
ssl_trust_certificate_path?: string;
|
||||
error?: ErrorCallback;
|
||||
}
|
||||
|
||||
type ProgressNotificationCallback = (transferred: number, transferable: number) => void;
|
||||
type ProgressDirection = 'download' | 'upload';
|
||||
type ProgressMode = 'reportIndefinitely' | 'forCurrentlyOutstandingWork';
|
||||
|
||||
/**
|
||||
* Session
|
||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Sync.Session.html }
|
||||
@ -341,6 +364,9 @@ declare namespace Realm.Sync {
|
||||
readonly state: 'invalid' | 'active' | 'inactive';
|
||||
readonly url: string;
|
||||
readonly user: User;
|
||||
|
||||
addProgressNotification(direction: ProgressDirection, mode: ProgressMode, progressCallback: ProgressNotificationCallback): void;
|
||||
removeProgressNotification(progressCallback: ProgressNotificationCallback): void;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -367,10 +393,15 @@ 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 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'
|
||||
type: 'INSERT' | 'SET' | 'DELETE' | 'CLEAR' | 'LIST_SET' | 'LIST_INSERT' | 'LIST_ERASE' | 'LIST_CLEAR' | 'ADD_TYPE' | 'ADD_PROPERTIES' | 'CHANGE_IDENTITY' | 'SWAP_IDENTITY'
|
||||
object_type: string,
|
||||
identity: string,
|
||||
values: any | undefined
|
||||
@ -402,6 +433,11 @@ declare namespace Realm.Sync {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface ProgressPromise extends Promise<Realm> {
|
||||
progress(callback: Realm.Sync.ProgressNotificationCallback) : Promise<Realm>
|
||||
}
|
||||
|
||||
declare class Realm {
|
||||
static defaultPath: string;
|
||||
|
||||
@ -422,17 +458,21 @@ declare class Realm {
|
||||
*/
|
||||
static schemaVersion(path: string, encryptionKey?: ArrayBuffer | ArrayBufferView): number;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully synchronized before it is available.
|
||||
* @param {Configuration} config
|
||||
*/
|
||||
static open(config: Realm.Configuration): Promise<Realm>
|
||||
static open(config: Realm.Configuration): ProgressPromise;
|
||||
/**
|
||||
* @deprecated in favor of `Realm.open`
|
||||
* Open a realm asynchronously with a callback. If the realm is synced, it will be fully synchronized before it is available.
|
||||
* @param {Configuration} config
|
||||
* @param {Function} callback will be called when the realm is ready.
|
||||
* @param {ProgressNotificationCallback} progressCallback? a progress notification callback for 'download' direction and 'forCurrentlyOutstandingWork' mode
|
||||
*/
|
||||
static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void): void
|
||||
static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void, progressCallback?: Realm.Sync.ProgressNotificationCallback): void
|
||||
|
||||
/**
|
||||
* Delete the Realm file for the given configuration.
|
||||
@ -469,6 +509,11 @@ declare class Realm {
|
||||
*/
|
||||
delete(object: Realm.Object | Realm.Object[] | Realm.List<any> | Realm.Results<any> | any): void;
|
||||
|
||||
/**
|
||||
* @returns void
|
||||
*/
|
||||
deleteModel(name: string): void;
|
||||
|
||||
/**
|
||||
* @returns void
|
||||
*/
|
||||
|
@ -86,7 +86,7 @@ function getSpecialPurposeRealm(user, realmName, schema) {
|
||||
setTimeout(() => {}, 1);
|
||||
|
||||
if (error) {
|
||||
reject(error);
|
||||
setTimeout(() => reject(error), 1);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
@ -95,7 +95,7 @@ function getSpecialPurposeRealm(user, realmName, schema) {
|
||||
//FIXME: RN hangs here. Remove when node's makeCallback alternative is implemented (#1255)
|
||||
setTimeout(() => resolve(syncedRealm), 1);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
setTimeout(() => reject(e), 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -28,7 +28,7 @@ function node_require(module) {
|
||||
function checkTypes(args, types) {
|
||||
args = Array.prototype.slice.call(args);
|
||||
for (var i = 0; i < types.length; ++i) {
|
||||
if (typeof args[i] !== types[i]) {
|
||||
if (args.length > i && typeof args[i] !== types[i]) {
|
||||
throw new TypeError('param ' + i + ' must be of type ' + types[i]);
|
||||
}
|
||||
}
|
||||
@ -121,6 +121,15 @@ function refreshAccessToken(user, localRealmPath, realmUrl) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* The base authentication method. It fires a JSON POST to the server parameter plus the auth url
|
||||
* For example, if the server parameter is `http://myapp.com`, this url will post to `http://myapp.com/auth`
|
||||
* @param {object} userConstructor
|
||||
* @param {string} server the http or https server url
|
||||
* @param {object} json the json to post to the auth endpoint
|
||||
* @param {Function} callback an optional callback with an error and user parameter
|
||||
* @returns {Promise} only returns a promise if the callback parameter was omitted
|
||||
*/
|
||||
function _authenticate(userConstructor, server, json, callback) {
|
||||
json.app_id = '';
|
||||
const url = auth_url(server);
|
||||
@ -130,21 +139,32 @@ function _authenticate(userConstructor, server, json, callback) {
|
||||
headers: postHeaders,
|
||||
open_timeout: 5000
|
||||
};
|
||||
performFetch(url, options)
|
||||
|
||||
const promise = performFetch(url, options)
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
return response.json().then((body) => callback(new AuthError(body)));
|
||||
return response.json().then((body) => Promise.reject(new AuthError(body)));
|
||||
} else {
|
||||
return response.json().then(function (body) {
|
||||
// TODO: validate JSON
|
||||
const token = body.refresh_token.token;
|
||||
const identity = body.refresh_token.token_data.identity;
|
||||
const isAdmin = body.refresh_token.token_data.is_admin;
|
||||
callback(undefined, userConstructor.createUser(server, identity, token, false, isAdmin));
|
||||
})
|
||||
return userConstructor.createUser(server, identity, token, false, isAdmin);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
promise.then(user => {
|
||||
callback(null, user);
|
||||
})
|
||||
.catch(callback);
|
||||
.catch(err => {
|
||||
callback(err);
|
||||
});
|
||||
} else {
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
const staticMethods = {
|
||||
@ -167,20 +187,34 @@ const staticMethods = {
|
||||
|
||||
register(server, username, password, callback) {
|
||||
checkTypes(arguments, ['string', 'string', 'string', 'function']);
|
||||
_authenticate(this, server, {
|
||||
const json = {
|
||||
provider: 'password',
|
||||
user_info: { password: password, register: true },
|
||||
data: username
|
||||
}, callback);
|
||||
};
|
||||
|
||||
if (callback) {
|
||||
const message = "register(..., callback) is now deprecated in favor of register(): Promise<User>. This function argument will be removed in future versions.";
|
||||
(console.warn || console.log).call(console, message);
|
||||
}
|
||||
|
||||
return _authenticate(this, server, json, callback);
|
||||
},
|
||||
|
||||
login(server, username, password, callback) {
|
||||
checkTypes(arguments, ['string', 'string', 'string', 'function']);
|
||||
_authenticate(this, server, {
|
||||
const json = {
|
||||
provider: 'password',
|
||||
user_info: { password: password },
|
||||
data: username
|
||||
}, callback);
|
||||
};
|
||||
|
||||
if (callback) {
|
||||
const message = "login(..., callback) is now deprecated in favor of login(): Promise<User>. This function argument will be removed in future versions.";
|
||||
(console.warn || console.log).call(console, message);
|
||||
}
|
||||
|
||||
return _authenticate(this, server, json, callback);
|
||||
},
|
||||
|
||||
registerWithProvider(server, options, callback) {
|
||||
@ -198,16 +232,21 @@ const staticMethods = {
|
||||
checkTypes(arguments, ['string', 'object', 'function']);
|
||||
}
|
||||
|
||||
let reqOptions = {
|
||||
let json = {
|
||||
provider: options.provider,
|
||||
data: options.providerToken,
|
||||
};
|
||||
|
||||
if (options.userInfo) {
|
||||
reqOptions.user_info = options.userInfo;
|
||||
json.user_info = options.userInfo;
|
||||
}
|
||||
|
||||
_authenticate(this, server, reqOptions, callback);
|
||||
if (callback) {
|
||||
const message = "registerWithProvider(..., callback) is now deprecated in favor of registerWithProvider(): Promise<User>. This function argument will be removed in future versions.";
|
||||
(console.warn || console.log).call(console, message);
|
||||
}
|
||||
|
||||
return _authenticate(this, server, json, callback);
|
||||
},
|
||||
|
||||
_refreshAccessToken: refreshAccessToken
|
||||
@ -236,13 +275,11 @@ const instanceMethods = {
|
||||
},
|
||||
retrieveAccount(provider, provider_id) {
|
||||
checkTypes(arguments, ['string', 'string']);
|
||||
|
||||
const url = url_parse(this.server);
|
||||
url.set('pathname', `/api/providers/${provider}/accounts/${provider_id}`);
|
||||
const headers = {
|
||||
Authorization: this.token
|
||||
};
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
headers,
|
||||
|
@ -48,7 +48,11 @@ download_server() {
|
||||
}
|
||||
|
||||
start_server() {
|
||||
./object-server-for-testing/start-object-server.command &
|
||||
#disabled ROS logging
|
||||
sh ./object-server-for-testing/start-object-server.command &> /dev/null &
|
||||
|
||||
#enabled ROS logging
|
||||
#sh ./object-server-for-testing/start-object-server.command &
|
||||
SERVER_PID=$!
|
||||
}
|
||||
|
||||
|
@ -989,7 +989,11 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
<<<<<<< HEAD
|
||||
CURRENT_PROJECT_VERSION = 2.0.0;
|
||||
=======
|
||||
CURRENT_PROJECT_VERSION = 1.12.0;
|
||||
>>>>>>> b3ff7ada624f9fcb6d6ad6abf19e862059513857
|
||||
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@ -1053,7 +1057,11 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
<<<<<<< HEAD
|
||||
CURRENT_PROJECT_VERSION = 2.0.0;
|
||||
=======
|
||||
CURRENT_PROJECT_VERSION = 1.12.0;
|
||||
>>>>>>> b3ff7ada624f9fcb6d6ad6abf19e862059513857
|
||||
CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh";
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
|
@ -180,13 +180,14 @@ public:
|
||||
static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void close(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void compact(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
static void delete_model(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
// properties
|
||||
static void get_empty(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_path(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_schema_version(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_schema(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_in_memory(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_read_only(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_is_in_transaction(ContextType, ObjectType, ReturnValue &);
|
||||
#if REALM_ENABLE_SYNC
|
||||
@ -235,6 +236,7 @@ public:
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
{"close", wrap<close>},
|
||||
{"compact", wrap<compact>},
|
||||
{"deleteModel", wrap<delete_model>},
|
||||
};
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
@ -242,6 +244,7 @@ public:
|
||||
{"path", {wrap<get_path>, nullptr}},
|
||||
{"schemaVersion", {wrap<get_schema_version>, nullptr}},
|
||||
{"schema", {wrap<get_schema>, nullptr}},
|
||||
{"inMemory", {wrap<get_in_memory>, nullptr}},
|
||||
{"readOnly", {wrap<get_read_only>, nullptr}},
|
||||
{"isInTransaction", {wrap<get_is_in_transaction>, nullptr}},
|
||||
#if REALM_ENABLE_SYNC
|
||||
@ -379,6 +382,12 @@ 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")) {
|
||||
config.in_memory = true;
|
||||
}
|
||||
|
||||
static const String read_only_string = "readOnly";
|
||||
ValueType read_only_value = Object::get_property(ctx, object, read_only_string);
|
||||
@ -559,6 +568,17 @@ void RealmClass<T>::delete_file(ContextType ctx, FunctionType, ObjectType this_o
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::delete_model(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
ValueType value = arguments[0];
|
||||
|
||||
SharedRealm& realm = *get_internal<T, RealmClass<T>>(this_object);
|
||||
|
||||
std::string model_name = Value::validated_to_string(ctx, value, "deleteModel");
|
||||
ObjectStore::delete_data_for_object(realm->read_group(), model_name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::get_default_path(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
return_value.set(realm::js::default_path());
|
||||
@ -594,6 +614,11 @@ void RealmClass<T>::get_schema(ContextType ctx, ObjectType object, ReturnValue &
|
||||
return_value.set(Schema<T>::object_for_schema(ctx, schema));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::get_in_memory(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, RealmClass<T>>(object)->get()->config().in_memory);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::get_read_only(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, RealmClass<T>>(object)->get()->config().immutable());
|
||||
@ -619,8 +644,14 @@ void RealmClass<T>::get_sync_session(ContextType ctx, ObjectType object, ReturnV
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 2);
|
||||
auto callback_function = Value::validated_to_function(ctx, arguments[1]);
|
||||
validate_argument_count(argc, 2, 3);
|
||||
auto config_object = Value::validated_to_object(ctx, arguments[0]);
|
||||
auto callback_function = Value::validated_to_function(ctx, arguments[argc - 1]);
|
||||
|
||||
ValueType session_callback = Value::from_null(ctx);
|
||||
if (argc == 3) {
|
||||
session_callback = Value::validated_to_function(ctx, arguments[1]);
|
||||
}
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
auto config_object = Value::validated_to_object(ctx, arguments[0]);
|
||||
@ -694,6 +725,14 @@ void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType,
|
||||
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);
|
||||
}
|
||||
|
122
src/js_sync.hpp
122
src/js_sync.hpp
@ -175,6 +175,7 @@ class SessionClass : public ClassDefinition<T, WeakSession> {
|
||||
|
||||
public:
|
||||
std::string const name = "Session";
|
||||
using ProgressHandler = void(uint64_t transferred_bytes, uint64_t transferrable_bytes);
|
||||
|
||||
static FunctionType create_constructor(ContextType);
|
||||
|
||||
@ -185,6 +186,8 @@ public:
|
||||
|
||||
static void simulate_error(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void refresh_access_token(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void add_progress_notification(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &);
|
||||
static void remove_progress_notification(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &);
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
{"config", {wrap<get_config>, nullptr}},
|
||||
@ -195,7 +198,9 @@ public:
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
{"_simulateError", wrap<simulate_error>},
|
||||
{"_refreshAccessToken", wrap<refresh_access_token>}
|
||||
{"_refreshAccessToken", wrap<refresh_access_token>},
|
||||
{"addProgressNotification", wrap<add_progress_notification>},
|
||||
{"removeProgressNotification", wrap<remove_progress_notification>},
|
||||
};
|
||||
};
|
||||
|
||||
@ -323,7 +328,83 @@ void SessionClass<T>::refresh_access_token(ContextType ctx, FunctionType, Object
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class SyncClass : public ClassDefinition<T, void *> {
|
||||
void SessionClass<T>::add_progress_notification(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 3);
|
||||
|
||||
if (auto session = get_internal<T, SessionClass<T>>(this_object)->lock()) {
|
||||
|
||||
std::string direction = Value::validated_to_string(ctx, arguments[0], "direction");
|
||||
std::string mode = Value::validated_to_string(ctx, arguments[1], "mode");
|
||||
SyncSession::NotifierType notifierType;
|
||||
if (direction == "download") {
|
||||
notifierType = SyncSession::NotifierType::download;
|
||||
}
|
||||
else if (direction == "upload") {
|
||||
notifierType = SyncSession::NotifierType::upload;
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument("Invalid argument 'direction'. Only 'download' and 'upload' progress notification directions are supported");
|
||||
}
|
||||
|
||||
bool is_streaming = false;
|
||||
if (mode == "reportIndefinitely") {
|
||||
is_streaming = true;
|
||||
}
|
||||
else if (mode == "forCurrentlyOutstandingWork") {
|
||||
is_streaming = false;
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument("Invalid argument 'mode'. Only 'reportIndefinitely' and 'forCurrentlyOutstandingWork' progress notification modes are supported");
|
||||
}
|
||||
|
||||
auto callback_function = Value::validated_to_function(ctx, arguments[2], "callback");
|
||||
|
||||
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));
|
||||
std::function<ProgressHandler> progressFunc;
|
||||
|
||||
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_callback, typename T::Object(), 2, callback_arguments);
|
||||
});
|
||||
|
||||
progressFunc = std::move(progress_handler);
|
||||
|
||||
|
||||
auto registrationToken = session->register_progress_notifier(std::move(progressFunc), notifierType, false);
|
||||
|
||||
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);
|
||||
Object::set_property(ctx, callback_function, "_registrationToken", Value::from_number(protected_ctx, registrationToken), attributes);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SessionClass<T>::remove_progress_notification(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
auto callback_function = Value::validated_to_function(ctx, arguments[0], "callback");
|
||||
auto syncSessionProp = Object::get_property(ctx, callback_function, "_syncSession");
|
||||
if (Value::is_undefined(ctx, syncSessionProp) || Value::is_null(ctx, syncSessionProp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto syncSession = Value::validated_to_object(ctx, syncSessionProp);
|
||||
auto registrationToken = Object::get_property(ctx, callback_function, "_registrationToken");
|
||||
|
||||
if (auto session = get_internal<T, SessionClass<T>>(syncSession)->lock()) {
|
||||
auto reg = Value::validated_to_number(ctx, registrationToken);
|
||||
session->unregister_progress_notifier(reg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class SyncClass : public ClassDefinition<T, void*> {
|
||||
using GlobalContextType = typename T::GlobalContext;
|
||||
using ContextType = typename T::Context;
|
||||
using FunctionType = typename T::Function;
|
||||
@ -343,6 +424,7 @@ public:
|
||||
static void set_sync_log_level(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
// private
|
||||
static std::function<SyncBindSessionHandler> session_bind_callback(ContextType ctx, ObjectType sync_constructor);
|
||||
static void populate_sync_config(ContextType, ObjectType realm_constructor, ObjectType config_object, Realm::Config&);
|
||||
|
||||
// static properties
|
||||
@ -382,6 +464,24 @@ void SyncClass<T>::set_sync_log_level(ContextType ctx, FunctionType, ObjectType
|
||||
realm::SyncManager::shared().set_log_level(log_level_2);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::function<SyncBindSessionHandler> SyncClass<T>::session_bind_callback(ContextType ctx, ObjectType sync_constructor)
|
||||
{
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
Protected<ObjectType> protected_sync_constructor(ctx, sync_constructor);
|
||||
return EventLoopDispatcher<SyncBindSessionHandler>([protected_ctx, protected_sync_constructor](const std::string& path, const realm::SyncConfig& config, std::shared_ptr<SyncSession>) {
|
||||
HANDLESCOPE
|
||||
ObjectType user_constructor = Object::validated_get_object(protected_ctx, protected_sync_constructor, "User");
|
||||
FunctionType refreshAccessToken = Object::validated_get_function(protected_ctx, user_constructor, "_refreshAccessToken");
|
||||
|
||||
ValueType arguments[3];
|
||||
arguments[0] = create_object<T, UserClass<T>>(protected_ctx, new SharedUser(config.user));
|
||||
arguments[1] = Value::from_string(protected_ctx, path);
|
||||
arguments[2] = Value::from_string(protected_ctx, config.realm_url);
|
||||
Function::call(protected_ctx, refreshAccessToken, 3, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constructor, ObjectType config_object, Realm::Config& config)
|
||||
{
|
||||
@ -391,21 +491,8 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
||||
} else if (!Value::is_undefined(ctx, sync_config_value)) {
|
||||
auto sync_config_object = Value::validated_to_object(ctx, sync_config_value);
|
||||
|
||||
ObjectType sync_constructor = Object::validated_get_object(ctx, realm_constructor, std::string("Sync"));
|
||||
Protected<ObjectType> protected_sync(ctx, sync_constructor);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
EventLoopDispatcher<SyncBindSessionHandler> bind([protected_ctx, protected_sync](const std::string& path, const realm::SyncConfig& config, std::shared_ptr<SyncSession>) {
|
||||
HANDLESCOPE
|
||||
ObjectType user_constructor = Object::validated_get_object(protected_ctx, protected_sync, std::string("User"));
|
||||
FunctionType refreshAccessToken = Object::validated_get_function(protected_ctx, user_constructor, std::string("_refreshAccessToken"));
|
||||
|
||||
ValueType arguments[3];
|
||||
arguments[0] = create_object<T, UserClass<T>>(protected_ctx, new SharedUser(config.user));
|
||||
arguments[1] = Value::from_string(protected_ctx, path.c_str());
|
||||
arguments[2] = Value::from_string(protected_ctx, config.realm_url.c_str());
|
||||
Function::call(protected_ctx, refreshAccessToken, 3, arguments);
|
||||
});
|
||||
ObjectType sync_constructor = Object::validated_get_object(ctx, realm_constructor, "Sync");
|
||||
auto bind = session_bind_callback(ctx, sync_constructor);
|
||||
|
||||
std::function<SyncSessionErrorHandler> error_handler;
|
||||
ValueType error_func = Object::get_property(ctx, sync_config_object, "error");
|
||||
@ -455,6 +542,5 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
@ -2,33 +2,49 @@
|
||||
This script creates 3 new objects into a new realm. These are objects are validated to exists by the download api tests.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
console.log("download-api-helper started");
|
||||
const username = process.argv[2];
|
||||
const realmName = process.argv[3];
|
||||
const realmModule = process.argv[4];
|
||||
|
||||
var Realm = require(realmModule);
|
||||
Realm.Sync.User.register('http://localhost:9080', username, 'password', (error, user) => {
|
||||
|
||||
function createObjects(user) {
|
||||
const config = {
|
||||
sync: { user,
|
||||
url: `realm://localhost:9080/~/${realmName}`,
|
||||
error: err => console.log(err)
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
var realm = new Realm(config);
|
||||
|
||||
realm.write(() => {
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
realm.create('Dog', { name: `Lassy ${i}` });
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Dogs count " + realm.objects('Dog').length);
|
||||
setTimeout(() => process.exit(0), 3000);
|
||||
}
|
||||
|
||||
Realm.Sync.User.register('http://localhost:9080', username, 'password', (error, registeredUser) => {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
process.exit(-2);
|
||||
} else {
|
||||
const config = {
|
||||
sync: { user, url: `realm://localhost:9080/~/${realmName}`, error: err => console.log(err) },
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }]
|
||||
};
|
||||
|
||||
var realm = new Realm(config);
|
||||
|
||||
realm.write(() => {
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
realm.create('Dog', { name: `Lassy ${i}` });
|
||||
const registrationError = JSON.stringify(error);
|
||||
Realm.Sync.User.login('http://localhost:9080', username, 'password', (err, loggedUser) => {
|
||||
if (err) {
|
||||
const loginError = JSON.stringify(err);
|
||||
console.error("download-api-helper failed:\n User.register() error:\n" + err + "\n" + registrationError + "\n User.login() error:\n" + loginError);
|
||||
process.exit(-2);
|
||||
}
|
||||
else {
|
||||
createObjects(loggedUser);
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Dogs count " + realm.objects('Dog').length);
|
||||
setTimeout(() => process.exit(0), 3000);
|
||||
}
|
||||
else {
|
||||
createObjects(registeredUser);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
@ -158,4 +158,170 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
testDeleteModelMigration: 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: [], schemaVersion: 1});
|
||||
TestCase.assertEqual(realm.schema.length, 0); // no models
|
||||
realm.close(); // this won't delete the model
|
||||
|
||||
realm = new Realm({schema: schema, schemaVersion: 2});
|
||||
TestCase.assertEqual(realm.objects('TestObject').length, 1); // the model objects are still there
|
||||
realm.close();
|
||||
|
||||
// now delete the model explicitly, which should delete the objects too
|
||||
realm = new Realm({schema: [], schemaVersion: 3, migration: function(oldRealm, newRealm) {
|
||||
newRealm.deleteModel('TestObject');
|
||||
}});
|
||||
|
||||
TestCase.assertEqual(realm.schema.length, 0); // no models
|
||||
|
||||
realm.close();
|
||||
|
||||
realm = new Realm({schema: schema, schemaVersion: 4});
|
||||
|
||||
TestCase.assertEqual(realm.objects('TestObject').length, 0);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testDeleteModelInSchema: 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();
|
||||
|
||||
|
||||
// now delete the model explicitly, but it should remain as it's still in the schema
|
||||
// only the rows should get deleted
|
||||
realm = new Realm({schema: schema, schemaVersion: 1, migration: function(oldRealm, newRealm) {
|
||||
newRealm.deleteModel('TestObject');
|
||||
}});
|
||||
|
||||
TestCase.assertEqual(realm.schema.length, 1); // model should remain
|
||||
TestCase.assertEqual(realm.objects('TestObject').length, 0); // objects should be gone
|
||||
|
||||
realm.close();
|
||||
|
||||
realm = new Realm({schema: schema, schemaVersion: 2});
|
||||
TestCase.assertEqual(realm.objects('TestObject').length, 0);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testDeleteModelIgnoreNotExisting: 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();
|
||||
|
||||
// non-existing models should be ignore on delete
|
||||
realm = new Realm({schema: schema, schemaVersion: 1, migration: function(oldRealm, newRealm) {
|
||||
newRealm.deleteModel('NonExistingModel');
|
||||
}});
|
||||
|
||||
realm.close();
|
||||
|
||||
realm = new Realm({schema: schema, schemaVersion: 2});
|
||||
TestCase.assertEqual(realm.objects('TestObject').length, 1);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testDeleteModelWithRelationship: function() {
|
||||
const ShipSchema = {
|
||||
name: 'Ship',
|
||||
properties: {
|
||||
ship_name: 'string',
|
||||
captain: 'Captain'
|
||||
}
|
||||
};
|
||||
|
||||
const CaptainSchema = {
|
||||
name: 'Captain',
|
||||
properties: {
|
||||
captain_name: 'string',
|
||||
ships: { type: 'linkingObjects', objectType: 'Ship', property: 'captain' }
|
||||
}
|
||||
};
|
||||
|
||||
var realm = new Realm({schema: [ShipSchema, CaptainSchema]});
|
||||
|
||||
realm.write(function() {
|
||||
realm.create('Ship', {
|
||||
ship_name: 'My Ship',
|
||||
captain: {
|
||||
captain_name: 'John Doe'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
TestCase.assertEqual(realm.objects('Captain').length, 1);
|
||||
TestCase.assertEqual(realm.objects('Ship').length, 1);
|
||||
TestCase.assertEqual(realm.objects('Ship')[0].captain.captain_name, "John Doe");
|
||||
TestCase.assertEqual(realm.objects('Captain')[0].ships[0].ship_name, "My Ship");
|
||||
|
||||
realm.close();
|
||||
|
||||
realm = new Realm({schema: [ShipSchema, CaptainSchema], schemaVersion: 1, migration: function(oldRealm, newRealm) {
|
||||
TestCase.assertThrows(function(e) {
|
||||
// deleting a model which is target of linkingObjects results in an exception
|
||||
newRealm.deleteModel('Captain');
|
||||
console.log(e);
|
||||
}, "Table is target of cross-table link columns");
|
||||
}});
|
||||
|
||||
TestCase.assertEqual(realm.objects('Captain').length, 1);
|
||||
TestCase.assertEqual(realm.objects('Ship').length, 1);
|
||||
|
||||
realm.close();
|
||||
|
||||
realm = new Realm({schema: [ShipSchema, CaptainSchema], schemaVersion: 2, migration: function(oldRealm, newRealm) {
|
||||
// deleting a model which isn't target of linkingObjects works fine
|
||||
newRealm.deleteModel('Ship');
|
||||
}});
|
||||
|
||||
TestCase.assertEqual(realm.objects('Captain').length, 1);
|
||||
TestCase.assertEqual(realm.objects('Ship').length, 0);
|
||||
TestCase.assertEqual(realm.objects('Captain')[0].ships.length, 0);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
};
|
||||
|
@ -59,6 +59,7 @@ module.exports = {
|
||||
return createUsersWithTestRealms(1)
|
||||
.then(([user]) => {
|
||||
return user.applyPermissions({ userId: '*' }, `/${user.identity}/test`, 'read')
|
||||
.then(wait(100))
|
||||
.then(() => user.getGrantedPermissions('any'))
|
||||
.then(permissions => {
|
||||
TestCase.assertEqual(permissions[1].path, `/${user.identity}/test`);
|
||||
|
@ -161,6 +161,36 @@ module.exports = {
|
||||
}]});
|
||||
}, "Property 'InvalidObject.link' declared as origin of linking objects property 'InvalidObject.linkingObjects' links to type 'IntObject'")
|
||||
},
|
||||
|
||||
testRealmConstructorInMemory: function() {
|
||||
// open in-memory realm instance
|
||||
const realm1 = new Realm({inMemory: true, schema: [schemas.TestObject]});
|
||||
realm1.write(function() {
|
||||
realm1.create('TestObject', [1])
|
||||
});
|
||||
TestCase.assertEqual(realm1.inMemory, true);
|
||||
|
||||
// open a second instance of the same realm and check that they share data
|
||||
const realm2 = new Realm({inMemory: true});
|
||||
const objects = realm2.objects('TestObject');
|
||||
TestCase.assertEqual(objects.length, 1);
|
||||
TestCase.assertEqual(objects[0].doubleCol, 1.0);
|
||||
TestCase.assertEqual(realm2.inMemory, true);
|
||||
|
||||
// Close both realms (this should delete the realm since there are no more
|
||||
// references to it.
|
||||
realm1.close();
|
||||
realm2.close();
|
||||
|
||||
// Open the same in-memory realm again and verify that it is now empty
|
||||
const realm3 = new Realm({inMemory: true});
|
||||
TestCase.assertEqual(realm3.schema.length, 0);
|
||||
|
||||
// try to open the same realm in persistent mode (should fail as you cannot mix modes)
|
||||
TestCase.assertThrows(function() {
|
||||
const realm4 = new Realm({});
|
||||
});
|
||||
},
|
||||
|
||||
testRealmConstructorReadOnly: function() {
|
||||
var realm = new Realm({schema: [schemas.TestObject]});
|
||||
|
@ -26,7 +26,7 @@ const Realm = require('realm');
|
||||
const TestCase = require('./asserts');
|
||||
|
||||
const isNodeProccess = (typeof process === 'object' && process + '' === '[object process]');
|
||||
console.log("isnode " + isNodeProccess + " typeof " + (typeof(process) === 'object'));
|
||||
|
||||
function node_require(module) {
|
||||
return require(module);
|
||||
}
|
||||
@ -84,13 +84,23 @@ function runOutOfProcess(nodeJsFilePath) {
|
||||
fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' });
|
||||
nodeArgs[0] = tmpFile.name;
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = execFile('node', nodeArgs, { cwd: tmpDir.name }, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
reject(new Error(`Error executing ${nodeJsFilePath} Error: ${error}`));
|
||||
try {
|
||||
console.log('runOutOfProcess command\n node ' + nodeArgs.join(" "));
|
||||
const child = execFile('node', nodeArgs, { cwd: tmpDir.name }, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error("runOutOfProcess failed\n" + error);
|
||||
reject(new Error(`Running ${nodeJsFilePath} failed. error: ${error}`));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('runOutOfProcess success\n' + stdout);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@ -150,7 +160,7 @@ module.exports = {
|
||||
const realmName = uuid();
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
||||
const accessTokenRefreshed = this;
|
||||
@ -186,9 +196,9 @@ module.exports = {
|
||||
const realmName = uuid();
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const accessTokenRefreshed = this;
|
||||
let successCounter = 0;
|
||||
@ -239,9 +249,9 @@ module.exports = {
|
||||
const realmName = uuid();
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
const accessTokenRefreshed = this;
|
||||
let successCounter = 0;
|
||||
let progressNotificationCalled = false;
|
||||
@ -275,9 +285,9 @@ module.exports = {
|
||||
const realmName = uuid();
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let progressNotificationCalled = false;
|
||||
let config = {
|
||||
@ -322,9 +332,9 @@ module.exports = {
|
||||
const realmName = uuid();
|
||||
const expectedObjectsCount = 3;
|
||||
|
||||
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
return runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
|
||||
.then(() => {
|
||||
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
|
||||
return Realm.Sync.User.login('http://localhost:9080', username, 'password').then(user => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const accessTokenRefreshed = this;
|
||||
let successCounter = 0;
|
||||
@ -429,7 +439,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
testErrorHandling() {
|
||||
return promisifiedRegister('http://localhost:9080', uuid(), 'password').then(user => {
|
||||
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
const config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
|
||||
config.sync.error = (sender, error) => {
|
||||
@ -449,5 +459,191 @@ module.exports = {
|
||||
session._simulateError(123, 'simulated error');
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
testProgressNotificationsForRealmConstructor() {
|
||||
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 => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let config = {
|
||||
sync: {
|
||||
user,
|
||||
url: `realm://localhost:9080/~/${realmName}`
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||
};
|
||||
|
||||
let realm = new Realm(config);
|
||||
const progressCallback = (transferred, total) => {
|
||||
resolve();
|
||||
};
|
||||
|
||||
realm.syncSession.addProgressNotification('download', 'reportIndefinitely', progressCallback);
|
||||
|
||||
setTimeout(function() {
|
||||
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsUnregisterForRealmConstructor() {
|
||||
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 => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let config = {
|
||||
sync: {
|
||||
user,
|
||||
url: `realm://localhost:9080/~/${realmName}`
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||
};
|
||||
|
||||
let realm = new Realm(config);
|
||||
let unregisterFunc;
|
||||
|
||||
let writeDataFunc = () => {
|
||||
realm.write(() => {
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
realm.create('Dog', { name: `Lassy ${i}` });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let syncFinished = false;
|
||||
let failOnCall = false;
|
||||
const progressCallback = (transferred, total) => {
|
||||
if (failOnCall) {
|
||||
reject(new Error("Progress callback should not be called after removeProgressNotification"));
|
||||
}
|
||||
|
||||
syncFinished = transferred === total;
|
||||
|
||||
//unregister and write some new data.
|
||||
if (syncFinished) {
|
||||
failOnCall = true;
|
||||
unregisterFunc();
|
||||
|
||||
//use second callback to wait for sync finished
|
||||
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', (x, y) => {
|
||||
if (x === y) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
writeDataFunc();
|
||||
}
|
||||
};
|
||||
|
||||
realm.syncSession.addProgressNotification('upload', 'reportIndefinitely', progressCallback);
|
||||
|
||||
unregisterFunc = () => {
|
||||
realm.syncSession.removeProgressNotification(progressCallback);
|
||||
};
|
||||
|
||||
writeDataFunc();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmOpen2() {
|
||||
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 => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let config = {
|
||||
sync: {
|
||||
user,
|
||||
url: `realm://localhost:9080/~/${realmName}`
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||
};
|
||||
|
||||
let progressCalled = false;
|
||||
Realm.open(config)
|
||||
.progress((transferred, total) => {
|
||||
progressCalled = true;
|
||||
})
|
||||
.then(() => {
|
||||
TestCase.assertTrue(progressCalled);
|
||||
resolve();
|
||||
})
|
||||
.catch((e) => reject(e));
|
||||
|
||||
setTimeout(function() {
|
||||
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testProgressNotificationsForRealmOpenAsync2() {
|
||||
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 => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let config = {
|
||||
sync: {
|
||||
user,
|
||||
url: `realm://localhost:9080/~/${realmName}`
|
||||
},
|
||||
schema: [{ name: 'Dog', properties: { name: 'string' } }],
|
||||
};
|
||||
|
||||
let progressCalled = false;
|
||||
|
||||
Realm.openAsync(config,
|
||||
(error, realm) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
TestCase.assertTrue(progressCalled);
|
||||
resolve();
|
||||
},
|
||||
(transferred, total) => {
|
||||
progressCalled = true;
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
reject("Progress Notifications API failed to call progress callback for Realm constructor");
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
|
@ -10,10 +10,12 @@
|
||||
"needle": "^1.3.0",
|
||||
"terminate": "^1.0.8",
|
||||
"tmp": "^0.0.30",
|
||||
"url-parse": "^1.1.7"
|
||||
"url-parse": "^1.1.7",
|
||||
"typescript": "^2.5.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jasmine spec/unit_tests.js",
|
||||
"test-sync-integration": "jasmine spec/sync_integration_tests.js"
|
||||
"check-typescript" : "tsc --noEmit --alwaysStrict ./../lib/index.d.ts",
|
||||
"test": "npm run check-typescript && jasmine spec/unit_tests.js",
|
||||
"test-sync-integration": "npm run check-typescript && jasmine spec/sync_integration_tests.js"
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
'use strict';
|
||||
const isNodeProccess = (typeof process === 'object' && process + '' === '[object process]');
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
@ -28,7 +29,11 @@ const Realm = require('realm');
|
||||
const RealmTests = require('../js');
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
||||
const isDebuggerAttached = typeof v8debug === 'object';
|
||||
let isDebuggerAttached = typeof v8debug === 'object';
|
||||
if (!isDebuggerAttached && isNodeProccess) {
|
||||
isDebuggerAttached = /--debug|--inspect/.test(process.execArgv.join(' '));
|
||||
}
|
||||
|
||||
if (isDebuggerAttached) {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000000;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user