mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-21 20:10:43 +00:00
Sync and fine grained notifications
This commit is contained in:
parent
c8e4dc39c0
commit
9d0df0de3d
7
.dir-locals.el
Normal file
7
.dir-locals.el
Normal file
@ -0,0 +1,7 @@
|
||||
;; Project specific Emacs settings
|
||||
((nil . ((c-basic-offset . 4)
|
||||
(indent-tabs-mode . nil)
|
||||
(fill-column . 80)
|
||||
(c-file-style . "ellemtel")
|
||||
(c-file-offsets . ((innamespace . 0)))
|
||||
(show-trailing-whitespace . t))))
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -98,6 +98,7 @@ build/
|
||||
# node.js
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
/compiled/
|
||||
|
||||
# Android/IJ
|
||||
/android/
|
||||
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -6,5 +6,5 @@
|
||||
url = https://github.com/realm/realm-jsdoc.git
|
||||
[submodule "src/object-store"]
|
||||
path = src/object-store
|
||||
url = https://github.com/realm/realm-object-store.git
|
||||
branch = js
|
||||
url = git@github.com:realm/realm-object-store-private.git
|
||||
branch = js-sync
|
||||
|
11
README.md
11
README.md
@ -41,7 +41,7 @@ Prerequisites:
|
||||
First clone this repository:
|
||||
|
||||
```
|
||||
git clone https://github.com/realm/realm-js.git
|
||||
git clone https://github.com/realm/realm-js-private.git
|
||||
```
|
||||
|
||||
Then in the cloned directory:
|
||||
@ -60,6 +60,15 @@ To build for Android:
|
||||
- `./gradlew publishAndroid`
|
||||
- The compiled version of the Android module is here: `<project-root>/android`
|
||||
|
||||
To build for Node:
|
||||
- `REALM_CORE_PREFIX=/path/to/realm-core REALM_SYNC_PREFIX=/path/to/realm-sync npm install`
|
||||
|
||||
The `REALM_*_PREFIX` paths need to be absolute. Make sure to run `sh build.sh build-node` in both core and sync. Optionally export `REALMJS_USE_DEBUG_CORE=true` to link against the debug version of the realm binaries.
|
||||
|
||||
To build the Developer Edition of the module, pass `--developer_edition` to `npm install` or change the default value in `binding.gyp`.
|
||||
|
||||
`node-pre-gyp` is used for packaging. Run `scripts/build-node-pre-gyp.sh` with the same arguments and environment variables as `npm install` on all platforms (e.g. Linux and Darwin). Then, edit `package.json` to remove the `--build-from-source` option from the install script and run `npm pack`. Lastly, either create a fat package by merging all the tarballs created so far into one, or upload the `node-pre-gyp` tarballs to the CDN the `binary` section of `package.json` points to.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project adheres to the Contributor Covenant [code of conduct](https://realm.io/conduct/).
|
||||
|
@ -5,12 +5,14 @@
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
|
||||
"40F53A12E4AE40C654358321B91166ABD3E910A6" : 0,
|
||||
"F6F96CA34C5878B0A9123C7C37855491A5E599DA" : 0
|
||||
"F6F96CA34C5878B0A9123C7C37855491A5E599DA" : 0,
|
||||
"8F3C415DA79CDA7D23734F285B95F9F9A3C0CB81" : 0
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "5EE721F9-041C-4877-9E73-A925C9DB080A",
|
||||
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
|
||||
"40F53A12E4AE40C654358321B91166ABD3E910A6" : "realm-js\/",
|
||||
"F6F96CA34C5878B0A9123C7C37855491A5E599DA" : "realm-js\/vendor\/GCDWebServer\/"
|
||||
"F6F96CA34C5878B0A9123C7C37855491A5E599DA" : "realm-js\/vendor\/GCDWebServer\/",
|
||||
"8F3C415DA79CDA7D23734F285B95F9F9A3C0CB81" : "realm-js\/src\/object-store\/"
|
||||
},
|
||||
"DVTSourceControlWorkspaceBlueprintNameKey" : "Realm",
|
||||
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
|
||||
@ -21,6 +23,11 @@
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "40F53A12E4AE40C654358321B91166ABD3E910A6"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/realm\/realm-object-store.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8F3C415DA79CDA7D23734F285B95F9F9A3C0CB81"
|
||||
},
|
||||
{
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/swisspol\/GCDWebServer.git",
|
||||
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
|
||||
|
105
binding.gyp
Normal file
105
binding.gyp
Normal file
@ -0,0 +1,105 @@
|
||||
{
|
||||
"includes": [
|
||||
"src/node/gyp/target_defaults.gypi",
|
||||
"src/node/gyp/realm.gyp"
|
||||
],
|
||||
"targets": [
|
||||
{
|
||||
"variables": {
|
||||
"developer_edition%": "0"
|
||||
},
|
||||
"target_name": "realm",
|
||||
"dependencies": [
|
||||
"object-store"
|
||||
],
|
||||
"sources": [
|
||||
"src/node/node_sync_logger.cpp",
|
||||
"src/node/node_init.cpp",
|
||||
"src/node/platform.cpp",
|
||||
"src/js_realm.cpp"
|
||||
],
|
||||
"include_dirs": [
|
||||
"src"
|
||||
],
|
||||
"defines": [ "REALM_DEVELOPER_EDITION=<(developer_edition)" ],
|
||||
"link_settings": {
|
||||
"ldflags": [
|
||||
"-Wl,--exclude-libs=ALL"
|
||||
]
|
||||
},
|
||||
"xcode_settings": {
|
||||
"OTHER_LDFLAGS": [ "-Xlinker -unexported_symbol -Xlinker '*'" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"variables": {
|
||||
"object-store-include-dirs": [
|
||||
"src/object-store/src",
|
||||
"src/object-store/src/impl",
|
||||
"src/object-store/src/impl/apple",
|
||||
"src/object-store/src/parser",
|
||||
"src/object-store/external/pegtl"
|
||||
]
|
||||
},
|
||||
"target_name": "object-store",
|
||||
"dependencies": [ "realm-sync" ], # sync also includes core
|
||||
"type": "static_library",
|
||||
"include_dirs": [ "<@(object-store-include-dirs)" ],
|
||||
"sources": [
|
||||
"src/object-store/src/collection_notifications.cpp",
|
||||
"src/object-store/src/index_set.cpp",
|
||||
"src/object-store/src/list.cpp",
|
||||
"src/object-store/src/object_schema.cpp",
|
||||
"src/object-store/src/object_store.cpp",
|
||||
"src/object-store/src/results.cpp",
|
||||
"src/object-store/src/schema.cpp",
|
||||
"src/object-store/src/shared_realm.cpp",
|
||||
"src/object-store/src/sync_manager.cpp",
|
||||
"src/object-store/src/sync_session.cpp",
|
||||
"src/object-store/src/thread_confined.cpp",
|
||||
"src/object-store/src/global_notifier.cpp",
|
||||
"src/object-store/src/impl/collection_change_builder.cpp",
|
||||
"src/object-store/src/impl/collection_notifier.cpp",
|
||||
"src/object-store/src/impl/handover.cpp",
|
||||
"src/object-store/src/impl/list_notifier.cpp",
|
||||
"src/object-store/src/impl/realm_coordinator.cpp",
|
||||
"src/object-store/src/impl/results_notifier.cpp",
|
||||
"src/object-store/src/impl/transact_log_handler.cpp",
|
||||
"src/object-store/src/impl/weak_realm_notifier.cpp",
|
||||
"src/object-store/src/parser/parser.cpp",
|
||||
"src/object-store/src/parser/query_builder.cpp",
|
||||
"src/object-store/src/util/format.cpp",
|
||||
"src/object-store/src/util/thread_id.cpp"
|
||||
],
|
||||
"conditions": [
|
||||
["OS=='linux'", {
|
||||
"sources": [
|
||||
"src/object-store/src/impl/android/external_commit_helper.cpp",
|
||||
]
|
||||
}],
|
||||
["OS=='mac'", {
|
||||
"sources": [
|
||||
"src/object-store/src/impl/apple/external_commit_helper.cpp"
|
||||
]
|
||||
}]
|
||||
],
|
||||
"all_dependent_settings": {
|
||||
"include_dirs": [ "<@(object-store-include-dirs)" ]
|
||||
},
|
||||
"export_dependent_settings": [
|
||||
"<@(_dependencies)" # re-export settings related to linking the realm binaries
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name": "action_after_build",
|
||||
"type": "none",
|
||||
"dependencies": [ "<(module_name)" ],
|
||||
"copies": [
|
||||
{
|
||||
"files": [ "<(PRODUCT_DIR)/<(module_name).node" ],
|
||||
"destination": "<(module_path)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -189,6 +189,10 @@ Realm.defaultPath;
|
||||
* object types in this Realm. **Required** when first creating a Realm at this `path`.
|
||||
* @property {number} [schemaVersion] - **Required** (and must be incremented) after
|
||||
* changing the `schema`.
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
|
93
docs/sync.js
Normal file
93
docs/sync.js
Normal file
@ -0,0 +1,93 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @memberof Realm
|
||||
*/
|
||||
class Sync {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a global listener function.
|
||||
* @param {string} local_path - The path to the directory where realm files are stored [deprecated]
|
||||
* @param {string} server_url - The sync server to listen to
|
||||
* @param {SyncUser} admin_user - An admin user obtained by calling `new Realm.Sync.User.Admin`
|
||||
* @param {function(realm_name)} filter_callback - Return true to recieve changes for the given realm
|
||||
* @param {function(realm_name, realm, change_set)} change_callback - called on any realm changes with
|
||||
* the following arguments:
|
||||
* - `realm_name` - path of the Realm on which changes occurred
|
||||
* - `realm` - a `Realm` object for the changed Realm
|
||||
* - `change_set` - a dictionary of object names to arays of indexes indicating the indexes of objects of each type
|
||||
* which have been added, removed, or modified
|
||||
*/
|
||||
Sync.setGlobalListener = function(local_path, server_url, admin_user, filter_callback, change_callback) {};
|
||||
|
||||
/**
|
||||
* Set the sync log level.
|
||||
* @param {string} log_level
|
||||
*/
|
||||
Sync.setLogLevel = function(log_level) {};
|
||||
|
||||
/**
|
||||
* @typedef Realm.Sync~LogLevel
|
||||
* @type {("error"|"info"|"debug")}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class for logging in and managing Sync users.
|
||||
* @memberof Realm.Sync
|
||||
*/
|
||||
class User {
|
||||
/**
|
||||
* Login a sync user with username and password.
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @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
|
||||
*/
|
||||
login(server, username, password, callback) {}
|
||||
/**
|
||||
* Login a sync user using an external login provider.
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} provider - The provider type
|
||||
* @param {string} providerToken - The access token for the given provider
|
||||
* @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
|
||||
*/
|
||||
loginWithProvider(server, provider, providerToken, callback) {}
|
||||
/**
|
||||
* Create a new user using the username/password provider
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} username
|
||||
* @param {string} password
|
||||
* @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
|
||||
*/
|
||||
create(server, username, password, callback) {}
|
||||
/**
|
||||
* Create an admin user for the given authentication server with an existing token
|
||||
* @param {string} server - authentication server
|
||||
* @param {string} adminToken - existing admin token
|
||||
* @return {User} - admin user populated with the given token and server
|
||||
*/
|
||||
adminUser(server, adminToken) {}
|
||||
}
|
@ -34,6 +34,7 @@ TodoList.schema = {
|
||||
name: 'TodoList',
|
||||
properties: {
|
||||
name: 'string',
|
||||
creationDate: 'date',
|
||||
items: {type: 'list', objectType: 'Todo'},
|
||||
},
|
||||
};
|
||||
|
@ -38,15 +38,18 @@ export default class TodoApp extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
let todoLists = realm.objects('TodoList');
|
||||
if (todoLists.length < 1) {
|
||||
// This is a Results object, which will live-update.
|
||||
this.todoLists = realm.objects('TodoList').sorted('creationDate');
|
||||
if (this.todoLists.length < 1) {
|
||||
realm.write(() => {
|
||||
realm.create('TodoList', {name: 'Todo List'});
|
||||
realm.create('TodoList', {name: 'Todo List', creationDate: new Date()});
|
||||
});
|
||||
}
|
||||
this.todoLists.addListener((name, changes) => {
|
||||
console.log("changed: " + JSON.stringify(changes));
|
||||
});
|
||||
console.log("registered listener");
|
||||
|
||||
// This is a Results object, which will live-update.
|
||||
this.todoLists = todoLists;
|
||||
|
||||
// Bind all the methods that we will be passing as props.
|
||||
this.renderScene = this.renderScene.bind(this);
|
||||
@ -79,7 +82,6 @@ export default class TodoApp extends React.Component {
|
||||
component: TodoListView,
|
||||
passProps: {
|
||||
ref: 'listView',
|
||||
items: this.todoLists,
|
||||
extraItems: extraItems,
|
||||
onPressItem: this._onPressTodoList,
|
||||
},
|
||||
@ -105,7 +107,8 @@ export default class TodoApp extends React.Component {
|
||||
}
|
||||
|
||||
renderScene(route) {
|
||||
return <route.component {...route.passProps} />
|
||||
console.log(this.todoLists);
|
||||
return <route.component items={this.todoLists} {...route.passProps} />
|
||||
}
|
||||
|
||||
_addNewTodoItem(list) {
|
||||
@ -128,7 +131,7 @@ export default class TodoApp extends React.Component {
|
||||
}
|
||||
|
||||
realm.write(() => {
|
||||
realm.create('TodoList', {name: ''});
|
||||
realm.create('TodoList', {name: '', creationDate: new Date()});
|
||||
});
|
||||
|
||||
this._setEditingRow(items.length - 1);
|
||||
|
46
examples/sync.js
Normal file
46
examples/sync.js
Normal file
@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
var Realm = require('..');
|
||||
|
||||
var filename = "sync.realm";
|
||||
var syncUrl = "realm://127.0.0.1/nodejs/sync.realm";
|
||||
var syncUserToken = "eyJpZGVudGl0eSI6Im5vZGVqcy1kZW1vIn0K";
|
||||
|
||||
function Foo() {}
|
||||
Foo.schema = {
|
||||
name: 'Foo',
|
||||
properties: {
|
||||
name: Realm.Types.STRING,
|
||||
number: Realm.Types.INT,
|
||||
}
|
||||
};
|
||||
|
||||
var realm = new Realm({
|
||||
path: filename,
|
||||
syncUrl: syncUrl,
|
||||
syncUserToken: syncUserToken,
|
||||
schema: [Foo]
|
||||
});
|
||||
|
||||
console.log('Starting...');
|
||||
|
||||
var prompt = require('prompt');
|
||||
prompt.start();
|
||||
|
||||
var run = true;
|
||||
var my_prompt = function() {
|
||||
prompt.get(['command'], function (err, result) {
|
||||
if (err) { return onErr(err); }
|
||||
console.log('Command: ' + result.command);
|
||||
if (result.command == 'exit') {
|
||||
run = false;
|
||||
}
|
||||
|
||||
if (run) {
|
||||
my_prompt();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
my_prompt();
|
||||
|
16
lib/index.js
16
lib/index.js
@ -18,7 +18,10 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var arrayMethods = require('./collection-methods');
|
||||
function node_require(module) {
|
||||
return require(module);
|
||||
}
|
||||
|
||||
var realmConstructor;
|
||||
|
||||
if (typeof Realm != 'undefined') {
|
||||
@ -31,14 +34,19 @@ if (typeof Realm != 'undefined') {
|
||||
// eslint-disable-next-line
|
||||
} else if (typeof process == 'object' && (('' + process) == '[object process]' || typeof jest == 'object')) {
|
||||
// Prevent React Native packager from seeing this module.
|
||||
var bindings = 'bindings';
|
||||
realmConstructor = require(bindings)('realm').Realm;
|
||||
var binary = node_require('node-pre-gyp');
|
||||
var path = node_require('path');
|
||||
var binding_path = binary.find(path.resolve(path.join(__dirname,'../package.json')));
|
||||
realmConstructor = require(binding_path).Realm;
|
||||
} else {
|
||||
throw new Error('Missing Realm constructor - please ensure RealmReact framework is included!');
|
||||
}
|
||||
|
||||
// Add the specified Array methods to the Collection prototype.
|
||||
Object.defineProperties(realmConstructor.Collection.prototype, arrayMethods);
|
||||
Object.defineProperties(realmConstructor.Collection.prototype, require('./collection-methods'));
|
||||
|
||||
// Add sync methods
|
||||
realmConstructor.Sync.User = require('./sync').User;
|
||||
|
||||
// TODO: Remove this now useless object.
|
||||
var types = Object.freeze({
|
||||
|
143
lib/sync.js
Normal file
143
lib/sync.js
Normal file
@ -0,0 +1,143 @@
|
||||
'use strict';
|
||||
|
||||
function node_require(module) {
|
||||
return require(module);
|
||||
}
|
||||
|
||||
var post;
|
||||
if (typeof fetch != 'undefined') {
|
||||
post = function(options, callback) {
|
||||
options.method = 'POST';
|
||||
fetch(options.url, options)
|
||||
.then((response) => {
|
||||
if (response.status != 200) {
|
||||
callback(undefined, {statusCode: response.status});
|
||||
}
|
||||
else {
|
||||
return response.text();
|
||||
}
|
||||
})
|
||||
.then((body) => {
|
||||
callback(undefined, {statusCode: 200}, body)
|
||||
})
|
||||
.catch((error) => {
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
post = node_require('request').post;
|
||||
}
|
||||
|
||||
const url = require("url");
|
||||
|
||||
const postHeaders = {
|
||||
'content-type': 'application/json;charset=utf-8',
|
||||
'accept': 'application/json'
|
||||
};
|
||||
|
||||
function _authenticate(server, json, callback) {
|
||||
json.app_id = '';
|
||||
var options = {
|
||||
url: server + 'auth',
|
||||
body: JSON.stringify(json),
|
||||
headers: postHeaders
|
||||
};
|
||||
post(options, function(error, response, body) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
callback(error);
|
||||
}
|
||||
else if (response.statusCode != 200) {
|
||||
console.log('Bad response: ' + response.statusCode);
|
||||
callback(new Error('Bad response: ' + response.statusCode));
|
||||
}
|
||||
else {
|
||||
var rjson = JSON.parse(body);
|
||||
// TODO: validate JSON
|
||||
|
||||
const token = rjson.refresh_token.token;
|
||||
const identity = rjson.refresh_token.token_data.identity;
|
||||
callback(undefined, new User(server, identity, token));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function User(server, identity, token) {
|
||||
this.server = server;
|
||||
this.identity = identity;
|
||||
this.token = token;
|
||||
this.isAdmin = false;
|
||||
|
||||
User.activeUsers[identity] = this;
|
||||
}
|
||||
|
||||
User.adminUser = function(server, token) {
|
||||
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
var user = new User(server, uuid, token);
|
||||
user.isAdmin = true;
|
||||
return user;
|
||||
}
|
||||
|
||||
User.activeUsers = {};
|
||||
|
||||
User.login = function(server, username, password, callback) {
|
||||
_authenticate(server, {
|
||||
provider: 'password',
|
||||
user_info: { password: password },
|
||||
data: username
|
||||
}, callback);
|
||||
}
|
||||
|
||||
User.loginWithProvider = function(server, provider, providerToken, callback) {
|
||||
_authenticate(server, {
|
||||
provider: provider,
|
||||
data: providerToken
|
||||
}, callback);
|
||||
}
|
||||
|
||||
User.create = function(server, username, password, callback) {
|
||||
_authenticate(server, {
|
||||
provider: 'password',
|
||||
user_info: { password: password, register: true },
|
||||
data: username
|
||||
}, callback);
|
||||
}
|
||||
|
||||
User.authenticateRealm = function(fileUrl, realmUrl, callback) {
|
||||
var options = {
|
||||
url: this.server + 'auth',
|
||||
body: JSON.stringify({
|
||||
data: this.token,
|
||||
path: url.parse(realmUrl).path,
|
||||
provider: 'realm',
|
||||
app_id: ''
|
||||
}),
|
||||
headers: postHeaders
|
||||
};
|
||||
post(options, function(error, response, body) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
callback(error);
|
||||
}
|
||||
else if (response.statusCode != 200) {
|
||||
console.log('Bad response: ' + response.statusCode + body);
|
||||
callback(new Error('Bad response: ' + response.statusCode));
|
||||
}
|
||||
else {
|
||||
var json = JSON.parse(body);
|
||||
// TODO: validate JSON
|
||||
|
||||
callback(undefined, {
|
||||
token: json.access_token.token,
|
||||
file_url: url.parse(fileUrl).path,
|
||||
resolved_realm_url: 'realm://' + url.parse(realmUrl).host + json.access_token.token_data.path
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports['User'] = User;
|
21
package.json
21
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "realm",
|
||||
"description": "Realm is a mobile database: an alternative to SQLite and key-value stores",
|
||||
"version": "0.14.3",
|
||||
"version": "0.14.3-6",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://realm.io",
|
||||
"keywords": [
|
||||
@ -35,7 +35,8 @@
|
||||
"react-native",
|
||||
"scripts",
|
||||
"src",
|
||||
"vendor"
|
||||
"vendor",
|
||||
"binding.gyp"
|
||||
],
|
||||
"scripts": {
|
||||
"get-version": "echo $npm_package_version",
|
||||
@ -44,20 +45,20 @@
|
||||
"jsdoc": "rm -rf docs/output && jsdoc -c docs/conf.json",
|
||||
"lint": "eslint",
|
||||
"test": "scripts/test.sh",
|
||||
"prepublish": "scripts/prepublish.sh"
|
||||
"install": "node-pre-gyp install --build-from-source"
|
||||
},
|
||||
"dependencies": {
|
||||
"bindings": "^1.2.1",
|
||||
"nan": "^2.3.3",
|
||||
"node-gyp": "^3.3.1",
|
||||
"sync-request": "^3.0.1"
|
||||
"node-pre-gyp": "^0.6.30",
|
||||
"request": "^2.74.0",
|
||||
"sync-request": "^3.0.1",
|
||||
"url": "^0.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^6.0.4",
|
||||
"eslint": "^2.10.2",
|
||||
"eslint-plugin-react": "^5.1.1",
|
||||
"jsdoc": "^3.4.0",
|
||||
"mockery": "^1.7.0",
|
||||
"semver": "^5.1.0"
|
||||
},
|
||||
"rnpm": {
|
||||
@ -71,5 +72,11 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
},
|
||||
"binary": {
|
||||
"module_name": "realm",
|
||||
"module_path": "./compiled/{node_abi}_{platform}_{arch}/",
|
||||
"host": "https://static.realm.io",
|
||||
"remote_path": "/node-pre-gyp"
|
||||
}
|
||||
}
|
||||
|
46
scripts/build-node-pre-gyp.sh
Executable file
46
scripts/build-node-pre-gyp.sh
Executable file
@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
|
||||
node_versions=${@:-4.4.7 5.12.0 6.5.0}
|
||||
|
||||
topdir=$(cd $(dirname "$0")/..; pwd)
|
||||
|
||||
die() {
|
||||
echo $1
|
||||
exit 1
|
||||
}
|
||||
|
||||
. ${topdir}/dependencies.list
|
||||
|
||||
mkdir -p ${topdir}/out
|
||||
: ${NVM_DIR=$topdir/.nvm}
|
||||
|
||||
if [ ! -d "$NVM_DIR" ]; then
|
||||
(
|
||||
git clone https://github.com/creationix/nvm.git "$NVM_DIR"
|
||||
cd "$NVM_DIR"
|
||||
git checkout `git describe --abbrev=0 --tags --match "v[0-9]*" origin`
|
||||
)
|
||||
fi
|
||||
|
||||
if [ -f "$NVM_DIR/nvm.sh" ]; then
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
else
|
||||
# we must be on mac and nvm was installed with brew
|
||||
# TODO: change the mac slaves to use manual nvm installation
|
||||
. "$(brew --prefix nvm)/nvm.sh"
|
||||
fi
|
||||
|
||||
for node_version in ${node_versions}; do
|
||||
(
|
||||
rm -rf node_modules build
|
||||
|
||||
nvm install ${node_version} || die "Could not install nodejs v${node_version}"
|
||||
nvm use ${node_version} || die "Could not load nodejs v${node_version}"
|
||||
|
||||
npm install "$EXTRA_NPM_ARGUMENTS" || die "Could not build module"
|
||||
#./scripts/test.sh node || die "Unit tests for nodejs v${node_version} failed"
|
||||
./node_modules/.bin/node-pre-gyp package || die "Could not package module"
|
||||
cp build/stage/node-pre-gyp/*.tar.gz ${topdir}/out/
|
||||
)
|
||||
done
|
||||
|
@ -4,7 +4,7 @@ set -e
|
||||
set -o pipefail
|
||||
|
||||
# Set to "latest" for the latest build.
|
||||
: ${REALM_CORE_VERSION:=1.5.0}
|
||||
: ${REALM_CORE_VERSION:=2.0.0-rc4}
|
||||
|
||||
if [ "$1" = '--version' ]; then
|
||||
echo "$REALM_CORE_VERSION"
|
||||
|
@ -154,21 +154,16 @@ case "$TARGET" in
|
||||
cat tests.xml
|
||||
;;
|
||||
"node")
|
||||
npm install
|
||||
scripts/download-core.sh node
|
||||
src/node/build-node.sh $CONFIGURATION
|
||||
|
||||
# Change to a temp directory.
|
||||
cd "$(mktemp -q -d -t realm.node.XXXXXX)"
|
||||
trap "rm -rf '$PWD'" EXIT
|
||||
|
||||
node "$SRCROOT/tests"
|
||||
pushd "$SRCROOT/tests"
|
||||
npm install
|
||||
npm test
|
||||
popd
|
||||
;;
|
||||
"test-runners")
|
||||
npm install
|
||||
scripts/download-core.sh node
|
||||
src/node/build-node.sh $CONFIGURATION
|
||||
|
||||
for runner in ava mocha jest; do
|
||||
pushd "$SRCROOT/tests/test-runners/$runner"
|
||||
npm install
|
||||
@ -181,6 +176,27 @@ case "$TARGET" in
|
||||
cmake -DCMAKE_BUILD_TYPE=$CONFIGURATION .
|
||||
make run-tests
|
||||
;;
|
||||
"download-object-server")
|
||||
. dependencies.list
|
||||
|
||||
object_server_bundle="realm-object-server-bundled_node_darwin-$REALM_OBJECT_SERVER_VERSION.tar.gz"
|
||||
curl -f -L "https://static.realm.io/downloads/object-server/$object_server_bundle" -o "$object_server_bundle"
|
||||
rm -rf tests/sync-bundle
|
||||
mkdir -p tests/sync-bundle
|
||||
tar -C tests/sync-bundle -xf "$object_server_bundle"
|
||||
rm "$object_server_bundle"
|
||||
|
||||
echo -e "enterprise:\n skip_setup: true\n" >> "tests/sync-bundle/object-server/configuration.yml"
|
||||
touch "tests/sync-bundle/object-server/do_not_open_browser"
|
||||
;;
|
||||
"object-server-integration")
|
||||
echo -e "yes\n" | ./tests/sync-bundle/reset-server-realms.command
|
||||
|
||||
pushd "$SRCROOT/tests"
|
||||
npm install
|
||||
npm run test-sync
|
||||
popd
|
||||
;;
|
||||
*)
|
||||
echo "Invalid target '${TARGET}'"
|
||||
exit 1
|
||||
|
@ -7,6 +7,9 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
020229861D9EEB39000F0C4F /* global_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 020229661D9DBF02000F0C4F /* global_notifier.cpp */; };
|
||||
020229871D9EEB39000F0C4F /* sync_metadata.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 020229801D9EEAF2000F0C4F /* sync_metadata.cpp */; };
|
||||
020229881D9EEB39000F0C4F /* sync_session.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 020229821D9EEAF2000F0C4F /* sync_session.cpp */; };
|
||||
02409DC21BCF11D6005F3B3E /* RealmJSCoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */; };
|
||||
02414B881CE68CA200A8669F /* dates-v5.realm in Resources */ = {isa = PBXBuildFile; fileRef = 02414B871CE68CA200A8669F /* dates-v5.realm */; };
|
||||
02414BA51CE6ABCF00A8669F /* collection_change_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B991CE6AAEF00A8669F /* collection_change_builder.cpp */; };
|
||||
@ -14,6 +17,8 @@
|
||||
02414BA71CE6ABCF00A8669F /* list_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B9D1CE6AAEF00A8669F /* list_notifier.cpp */; };
|
||||
02414BA81CE6ABCF00A8669F /* results_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B9F1CE6AAEF00A8669F /* results_notifier.cpp */; };
|
||||
02414BA91CE6ABCF00A8669F /* collection_notifications.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02414B961CE6AADD00A8669F /* collection_notifications.cpp */; };
|
||||
02443E391D8AF936007B0DF4 /* sync_manager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02443E361D8AF8FB007B0DF4 /* sync_manager.cpp */; };
|
||||
02443E421D8AFB74007B0DF4 /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02443E401D8AFB5F007B0DF4 /* weak_realm_notifier.cpp */; };
|
||||
0270BC821B7D020100010E03 /* RealmJSTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0270BC7B1B7D020100010E03 /* RealmJSTests.mm */; };
|
||||
027A23131CD3E379000543AE /* libRealmJS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */; };
|
||||
02D041F71CE11159000E4250 /* dates-v3.realm in Resources */ = {isa = PBXBuildFile; fileRef = 02D041F61CE11159000E4250 /* dates-v3.realm */; };
|
||||
@ -29,9 +34,9 @@
|
||||
02F59ECA1C88F190007F774C /* parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC61C88F190007F774C /* parser.cpp */; };
|
||||
02F59ECB1C88F190007F774C /* query_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EC81C88F190007F774C /* query_builder.cpp */; };
|
||||
02F59ED41C88F1B6007F774C /* external_commit_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ECF1C88F1B6007F774C /* external_commit_helper.cpp */; };
|
||||
02F59ED51C88F1B6007F774C /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59ED11C88F1B6007F774C /* weak_realm_notifier.cpp */; };
|
||||
02F59EE21C88F2BB007F774C /* realm_coordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */; };
|
||||
02F59EE31C88F2BB007F774C /* transact_log_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */; };
|
||||
5D25F5A11D6284FD00EBBB30 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F63FF3301C16434400B3B8E0 /* libz.tbd */; };
|
||||
5DC74A781D623C9800D77A4F /* handover.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DC74A751D623C8700D77A4F /* handover.cpp */; };
|
||||
5DC74A791D623CA200D77A4F /* handover.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DC74A751D623C8700D77A4F /* handover.cpp */; };
|
||||
5DC74A7A1D623CA800D77A4F /* thread_confined.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5DC74A721D623C7A00D77A4F /* thread_confined.cpp */; };
|
||||
@ -53,7 +58,6 @@
|
||||
F61378791C18EAC5008BFC51 /* js in Resources */ = {isa = PBXBuildFile; fileRef = F61378781C18EAAC008BFC51 /* js */; };
|
||||
F620F0581CB766DA0082977B /* node_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F620F0571CB766DA0082977B /* node_init.cpp */; };
|
||||
F620F0751CB9F60C0082977B /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F620F0741CB9F60C0082977B /* CoreFoundation.framework */; };
|
||||
F63117F01CEB0D5F00ECB2DE /* weak_realm_notifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F63117EE1CEB0D5900ECB2DE /* weak_realm_notifier.cpp */; };
|
||||
F63FF2C61C12469E00B3B8E0 /* jsc_init.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048011C0428DF00ABDED4 /* jsc_init.cpp */; };
|
||||
F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 029048071C0428DF00ABDED4 /* js_realm.cpp */; };
|
||||
F63FF2CD1C12469E00B3B8E0 /* rpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0290480F1C0428DF00ABDED4 /* rpc.cpp */; };
|
||||
@ -98,6 +102,12 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
020229661D9DBF02000F0C4F /* global_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = global_notifier.cpp; path = src/global_notifier.cpp; sourceTree = "<group>"; };
|
||||
020229671D9DBF02000F0C4F /* global_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = global_notifier.hpp; path = src/global_notifier.hpp; sourceTree = "<group>"; };
|
||||
020229801D9EEAF2000F0C4F /* sync_metadata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_metadata.cpp; path = src/sync_metadata.cpp; sourceTree = "<group>"; };
|
||||
020229811D9EEAF2000F0C4F /* sync_metadata.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_metadata.hpp; path = src/sync_metadata.hpp; sourceTree = "<group>"; };
|
||||
020229821D9EEAF2000F0C4F /* sync_session.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_session.cpp; path = src/sync_session.cpp; sourceTree = "<group>"; };
|
||||
020229831D9EEAF2000F0C4F /* sync_session.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_session.hpp; path = src/sync_session.hpp; sourceTree = "<group>"; };
|
||||
02409DC11BCF11D6005F3B3E /* RealmJSCoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RealmJSCoreTests.m; path = ios/RealmJSCoreTests.m; sourceTree = "<group>"; };
|
||||
02414B871CE68CA200A8669F /* dates-v5.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "dates-v5.realm"; sourceTree = "<group>"; };
|
||||
02414B961CE6AADD00A8669F /* collection_notifications.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = collection_notifications.cpp; path = src/collection_notifications.cpp; sourceTree = "<group>"; };
|
||||
@ -110,6 +120,12 @@
|
||||
02414B9E1CE6AAEF00A8669F /* list_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = list_notifier.hpp; sourceTree = "<group>"; };
|
||||
02414B9F1CE6AAEF00A8669F /* results_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = results_notifier.cpp; sourceTree = "<group>"; };
|
||||
02414BA01CE6AAEF00A8669F /* results_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = results_notifier.hpp; sourceTree = "<group>"; };
|
||||
02443E351D8AF8FB007B0DF4 /* sync_config.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_config.hpp; path = src/sync_config.hpp; sourceTree = "<group>"; };
|
||||
02443E361D8AF8FB007B0DF4 /* sync_manager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sync_manager.cpp; path = src/sync_manager.cpp; sourceTree = "<group>"; };
|
||||
02443E371D8AF8FB007B0DF4 /* sync_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = sync_manager.hpp; path = src/sync_manager.hpp; sourceTree = "<group>"; };
|
||||
02443E3A1D8AFAD1007B0DF4 /* sync_client.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sync_client.hpp; sourceTree = "<group>"; };
|
||||
02443E401D8AFB5F007B0DF4 /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = weak_realm_notifier.cpp; sourceTree = "<group>"; };
|
||||
0250D9C01D7647E00012C20C /* js_sync.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_sync.hpp; sourceTree = "<group>"; };
|
||||
025678951CAB392000FB8501 /* jsc_types.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = jsc_types.hpp; sourceTree = "<group>"; };
|
||||
0270BC5A1B7CFC1300010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0270BC781B7D020100010E03 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ios/Info.plist; sourceTree = "<group>"; };
|
||||
@ -127,6 +143,7 @@
|
||||
029048101C0428DF00ABDED4 /* rpc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rpc.hpp; sourceTree = "<group>"; };
|
||||
029048351C042A3C00ABDED4 /* platform.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = platform.hpp; sourceTree = "<group>"; };
|
||||
029048381C042A8F00ABDED4 /* platform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platform.mm; sourceTree = "<group>"; };
|
||||
0290934A1CEFA9170009769E /* js_observable.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = js_observable.hpp; sourceTree = "<group>"; };
|
||||
02A3C7A41BC4341500B1A7BE /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
02B58CBC1AE99CEC009B348C /* RealmJSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmJSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
02B58CCD1AE99D4D009B348C /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
@ -157,14 +174,12 @@
|
||||
02F59EC91C88F190007F774C /* query_builder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = query_builder.hpp; sourceTree = "<group>"; };
|
||||
02F59ECF1C88F1B6007F774C /* external_commit_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = external_commit_helper.cpp; sourceTree = "<group>"; };
|
||||
02F59ED01C88F1B6007F774C /* external_commit_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = external_commit_helper.hpp; sourceTree = "<group>"; };
|
||||
02F59ED11C88F1B6007F774C /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = weak_realm_notifier.cpp; sourceTree = "<group>"; };
|
||||
02F59ED21C88F1B6007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
|
||||
02F59EDA1C88F2BA007F774C /* external_commit_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = external_commit_helper.hpp; sourceTree = "<group>"; };
|
||||
02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = realm_coordinator.cpp; sourceTree = "<group>"; };
|
||||
02F59EDC1C88F2BB007F774C /* realm_coordinator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = realm_coordinator.hpp; sourceTree = "<group>"; };
|
||||
02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transact_log_handler.cpp; sourceTree = "<group>"; };
|
||||
02F59EDE1C88F2BB007F774C /* transact_log_handler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = transact_log_handler.hpp; sourceTree = "<group>"; };
|
||||
02F59EDF1C88F2BB007F774C /* weak_realm_notifier_base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier_base.hpp; sourceTree = "<group>"; };
|
||||
02F59EDF1C88F2BB007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
|
||||
02F59EE01C88F2BB007F774C /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
|
||||
5DC74A721D623C7A00D77A4F /* thread_confined.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = thread_confined.cpp; path = src/thread_confined.cpp; sourceTree = "<group>"; };
|
||||
5DC74A731D623C7A00D77A4F /* thread_confined.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = thread_confined.hpp; path = src/thread_confined.hpp; sourceTree = "<group>"; };
|
||||
@ -204,15 +219,10 @@
|
||||
F6267BC91CADC30000AC36B1 /* js_util.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = js_util.hpp; sourceTree = "<group>"; };
|
||||
F6267BCA1CADC49200AC36B1 /* node_dummy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = node_dummy.cpp; sourceTree = "<group>"; };
|
||||
F62BF8FB1CAC71780022BCDC /* libRealmNode.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libRealmNode.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F63117EE1CEB0D5900ECB2DE /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = weak_realm_notifier.cpp; sourceTree = "<group>"; };
|
||||
F63117EF1CEB0D5900ECB2DE /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
|
||||
F63118431CEBA7B700ECB2DE /* external_commit_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = external_commit_helper.cpp; sourceTree = "<group>"; };
|
||||
F63118441CEBA7B700ECB2DE /* external_commit_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = external_commit_helper.hpp; sourceTree = "<group>"; };
|
||||
F63118451CEBA7B700ECB2DE /* weak_realm_notifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = weak_realm_notifier.cpp; sourceTree = "<group>"; };
|
||||
F63118461CEBA7B700ECB2DE /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
|
||||
F631184A1CEBA7D800ECB2DE /* external_commit_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = external_commit_helper.cpp; sourceTree = "<group>"; };
|
||||
F631184B1CEBA7D800ECB2DE /* external_commit_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = external_commit_helper.hpp; sourceTree = "<group>"; };
|
||||
F631184C1CEBA7D800ECB2DE /* weak_realm_notifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = weak_realm_notifier.hpp; sourceTree = "<group>"; };
|
||||
F63FF2B11C1241E500B3B8E0 /* libRealmJS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRealmJS.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F63FF2F01C16405C00B3B8E0 /* libGCDWebServers.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libGCDWebServers.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F63FF2FD1C1642BB00B3B8E0 /* GCDWebServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDWebServer.h; sourceTree = "<group>"; };
|
||||
@ -266,6 +276,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5D25F5A11D6284FD00EBBB30 /* libz.tbd in Frameworks */,
|
||||
027A23131CD3E379000543AE /* libRealmJS.a in Frameworks */,
|
||||
02D8D1F71B601984006DB49D /* JavaScriptCore.framework in Frameworks */,
|
||||
);
|
||||
@ -297,11 +308,13 @@
|
||||
F6874A441CAD2ACD00EEEE36 /* JSC */,
|
||||
F62BF9001CAC72C40022BCDC /* Node */,
|
||||
F62A35141C18E783004A917D /* Object Store */,
|
||||
0290934A1CEFA9170009769E /* js_observable.hpp */,
|
||||
F60102F71CBDA6D400EC01BA /* js_collection.hpp */,
|
||||
029048041C0428DF00ABDED4 /* js_list.hpp */,
|
||||
029048061C0428DF00ABDED4 /* js_realm_object.hpp */,
|
||||
029048071C0428DF00ABDED4 /* js_realm.cpp */,
|
||||
029048081C0428DF00ABDED4 /* js_realm.hpp */,
|
||||
0250D9C01D7647E00012C20C /* js_sync.hpp */,
|
||||
0290480A1C0428DF00ABDED4 /* js_results.hpp */,
|
||||
0290480C1C0428DF00ABDED4 /* js_schema.hpp */,
|
||||
F620F0521CAF0B600082977B /* js_class.hpp */,
|
||||
@ -414,6 +427,8 @@
|
||||
02F59EAE1C88F17D007F774C /* binding_context.hpp */,
|
||||
02F59EAF1C88F17D007F774C /* index_set.cpp */,
|
||||
02F59EB01C88F17D007F774C /* index_set.hpp */,
|
||||
020229661D9DBF02000F0C4F /* global_notifier.cpp */,
|
||||
020229671D9DBF02000F0C4F /* global_notifier.hpp */,
|
||||
02F59EB11C88F17D007F774C /* list.cpp */,
|
||||
02F59EB21C88F17D007F774C /* list.hpp */,
|
||||
02F59EB31C88F17D007F774C /* object_accessor.hpp */,
|
||||
@ -430,6 +445,13 @@
|
||||
02F59EBE1C88F17D007F774C /* shared_realm.hpp */,
|
||||
5DC74A721D623C7A00D77A4F /* thread_confined.cpp */,
|
||||
5DC74A731D623C7A00D77A4F /* thread_confined.hpp */,
|
||||
02443E351D8AF8FB007B0DF4 /* sync_config.hpp */,
|
||||
02443E361D8AF8FB007B0DF4 /* sync_manager.cpp */,
|
||||
02443E371D8AF8FB007B0DF4 /* sync_manager.hpp */,
|
||||
020229801D9EEAF2000F0C4F /* sync_metadata.cpp */,
|
||||
020229811D9EEAF2000F0C4F /* sync_metadata.hpp */,
|
||||
020229821D9EEAF2000F0C4F /* sync_session.cpp */,
|
||||
020229831D9EEAF2000F0C4F /* sync_session.hpp */,
|
||||
);
|
||||
name = "Object Store";
|
||||
path = "object-store";
|
||||
@ -464,7 +486,6 @@
|
||||
F63118421CEBA7A100ECB2DE /* android */,
|
||||
F63117EB1CEB0C1B00ECB2DE /* apple */,
|
||||
F63118491CEBA7BD00ECB2DE /* generic */,
|
||||
F63117ED1CEB0CC600ECB2DE /* node */,
|
||||
02414B991CE6AAEF00A8669F /* collection_change_builder.cpp */,
|
||||
02414B9A1CE6AAEF00A8669F /* collection_change_builder.hpp */,
|
||||
02414B9B1CE6AAEF00A8669F /* collection_notifier.cpp */,
|
||||
@ -478,9 +499,11 @@
|
||||
02F59EDA1C88F2BA007F774C /* external_commit_helper.hpp */,
|
||||
02F59EDB1C88F2BA007F774C /* realm_coordinator.cpp */,
|
||||
02F59EDC1C88F2BB007F774C /* realm_coordinator.hpp */,
|
||||
02443E3A1D8AFAD1007B0DF4 /* sync_client.hpp */,
|
||||
02F59EDD1C88F2BB007F774C /* transact_log_handler.cpp */,
|
||||
02F59EDE1C88F2BB007F774C /* transact_log_handler.hpp */,
|
||||
02F59EDF1C88F2BB007F774C /* weak_realm_notifier_base.hpp */,
|
||||
02F59EDF1C88F2BB007F774C /* weak_realm_notifier.hpp */,
|
||||
02443E401D8AFB5F007B0DF4 /* weak_realm_notifier.cpp */,
|
||||
02F59EE01C88F2BB007F774C /* weak_realm_notifier.hpp */,
|
||||
);
|
||||
name = impl;
|
||||
@ -492,8 +515,6 @@
|
||||
children = (
|
||||
02F59ECF1C88F1B6007F774C /* external_commit_helper.cpp */,
|
||||
02F59ED01C88F1B6007F774C /* external_commit_helper.hpp */,
|
||||
02F59ED11C88F1B6007F774C /* weak_realm_notifier.cpp */,
|
||||
02F59ED21C88F1B6007F774C /* weak_realm_notifier.hpp */,
|
||||
);
|
||||
path = apple;
|
||||
sourceTree = "<group>";
|
||||
@ -510,22 +531,11 @@
|
||||
path = src/parser;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F63117ED1CEB0CC600ECB2DE /* node */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F63117EE1CEB0D5900ECB2DE /* weak_realm_notifier.cpp */,
|
||||
F63117EF1CEB0D5900ECB2DE /* weak_realm_notifier.hpp */,
|
||||
);
|
||||
path = node;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F63118421CEBA7A100ECB2DE /* android */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F63118431CEBA7B700ECB2DE /* external_commit_helper.cpp */,
|
||||
F63118441CEBA7B700ECB2DE /* external_commit_helper.hpp */,
|
||||
F63118451CEBA7B700ECB2DE /* weak_realm_notifier.cpp */,
|
||||
F63118461CEBA7B700ECB2DE /* weak_realm_notifier.hpp */,
|
||||
);
|
||||
path = android;
|
||||
sourceTree = "<group>";
|
||||
@ -535,7 +545,6 @@
|
||||
children = (
|
||||
F631184A1CEBA7D800ECB2DE /* external_commit_helper.cpp */,
|
||||
F631184B1CEBA7D800ECB2DE /* external_commit_helper.hpp */,
|
||||
F631184C1CEBA7D800ECB2DE /* weak_realm_notifier.hpp */,
|
||||
);
|
||||
path = generic;
|
||||
sourceTree = "<group>";
|
||||
@ -849,7 +858,6 @@
|
||||
F6E931BC1CFEAE340016AF14 /* collection_notifier.cpp in Sources */,
|
||||
F60102DC1CBB96C900EC01BA /* query_builder.cpp in Sources */,
|
||||
F60102DD1CBB96CC00EC01BA /* external_commit_helper.cpp in Sources */,
|
||||
F63117F01CEB0D5F00ECB2DE /* weak_realm_notifier.cpp in Sources */,
|
||||
F60102E11CBB96DD00EC01BA /* transact_log_handler.cpp in Sources */,
|
||||
F6E931BE1CFEAE3A0016AF14 /* results_notifier.cpp in Sources */,
|
||||
F60102D71CBB96B800EC01BA /* object_store.cpp in Sources */,
|
||||
@ -870,6 +878,11 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
020229861D9EEB39000F0C4F /* global_notifier.cpp in Sources */,
|
||||
020229871D9EEB39000F0C4F /* sync_metadata.cpp in Sources */,
|
||||
020229881D9EEB39000F0C4F /* sync_session.cpp in Sources */,
|
||||
02443E421D8AFB74007B0DF4 /* weak_realm_notifier.cpp in Sources */,
|
||||
02443E391D8AF936007B0DF4 /* sync_manager.cpp in Sources */,
|
||||
02E008D51D10ABB600F3AA37 /* format.cpp in Sources */,
|
||||
5DC74A7A1D623CA800D77A4F /* thread_confined.cpp in Sources */,
|
||||
02414BA51CE6ABCF00A8669F /* collection_change_builder.cpp in Sources */,
|
||||
@ -887,7 +900,6 @@
|
||||
02F59ECA1C88F190007F774C /* parser.cpp in Sources */,
|
||||
02F59EC01C88F17D007F774C /* list.cpp in Sources */,
|
||||
02F59EBF1C88F17D007F774C /* index_set.cpp in Sources */,
|
||||
02F59ED51C88F1B6007F774C /* weak_realm_notifier.cpp in Sources */,
|
||||
F63FF2C91C12469E00B3B8E0 /* js_realm.cpp in Sources */,
|
||||
02F59EC51C88F17D007F774C /* shared_realm.cpp in Sources */,
|
||||
02F59ECB1C88F190007F774C /* query_builder.cpp in Sources */,
|
||||
@ -1145,12 +1157,21 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
REALM_DEBUG,
|
||||
REALM_HAVE_CONFIG,
|
||||
__ASSERTMACROS__,
|
||||
REALM_ENABLE_SYNC,
|
||||
);
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(inherited)",
|
||||
"-isystem",
|
||||
../core/include,
|
||||
"../../realm-core/ios-lib/include",
|
||||
"-isystem",
|
||||
"../../realm-sync/src",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../core/librealm-ios-dbg.a";
|
||||
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../../realm-core/ios-lib/librealm-ios-dbg.a $(SRCROOT)/../../realm-sync/src/realm/librealm-sync-iphonesimulator-dbg.a";
|
||||
PRODUCT_NAME = RealmJS;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
@ -1159,12 +1180,19 @@
|
||||
F63FF2B91C1241E500B3B8E0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
REALM_HAVE_CONFIG,
|
||||
__ASSERTMACROS__,
|
||||
REALM_ENABLE_SYNC,
|
||||
);
|
||||
OTHER_CPLUSPLUSFLAGS = (
|
||||
"$(inherited)",
|
||||
"-isystem",
|
||||
../core/include,
|
||||
"../../realm-core/ios-lib/include",
|
||||
"-isystem",
|
||||
"../../realm-sync/src",
|
||||
);
|
||||
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../core/librealm-ios.a";
|
||||
OTHER_LIBTOOLFLAGS = "$(SRCROOT)/../../realm-core/ios-lib/librealm-ios.a $(SRCROOT)/../../realm-sync/src/realm/librealm-sync-iphonesimulator.a";
|
||||
PRODUCT_NAME = RealmJS;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
|
@ -5,22 +5,6 @@
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F62BF8FA1CAC71780022BCDC"
|
||||
BuildableName = "libRealmNode.dylib"
|
||||
BlueprintName = "RealmNode"
|
||||
ReferencedContainer = "container:RealmJS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
@ -29,24 +13,6 @@
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "YES"
|
||||
customWorkingDirectory = "$(TEMP_DIR)"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<PathRunnable
|
||||
runnableDebuggingMode = "0"
|
||||
FilePath = "/usr/local/bin/node">
|
||||
</PathRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
@ -56,9 +22,27 @@
|
||||
ReferencedContainer = "container:RealmJS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "YES"
|
||||
customWorkingDirectory = "~/src/realm/sync/realm-js"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<PathRunnable
|
||||
runnableDebuggingMode = "0"
|
||||
FilePath = "/usr/local/bin/node">
|
||||
</PathRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "$(SRCROOT)/../tests"
|
||||
argument = "test.js"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
@ -71,15 +55,6 @@
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F62BF8FA1CAC71780022BCDC"
|
||||
BuildableName = "libRealmNode.dylib"
|
||||
BlueprintName = "RealmNode"
|
||||
ReferencedContainer = "container:RealmJS.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
|
@ -19,6 +19,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "js_class.hpp"
|
||||
#include "js_types.hpp"
|
||||
#include "js_observable.hpp"
|
||||
|
||||
#include "collection_notifications.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
@ -27,9 +31,40 @@ namespace js {
|
||||
class Collection {};
|
||||
|
||||
template<typename T>
|
||||
struct CollectionClass : ClassDefinition<T, Collection> {
|
||||
struct CollectionClass : ClassDefinition<T, Collection, ObservableClass<T>> {
|
||||
using ContextType = typename T::Context;
|
||||
using ValueType = typename T::Value;
|
||||
using ObjectType = typename T::Object;
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
|
||||
std::string const name = "Collection";
|
||||
|
||||
static inline ValueType create_collection_change_set(ContextType ctx, const CollectionChangeSet &change_set);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
typename T::Value CollectionClass<T>::create_collection_change_set(ContextType ctx, const CollectionChangeSet &change_set)
|
||||
{
|
||||
ObjectType object = Object::create_empty(ctx);
|
||||
std::vector<ValueType> deletions, insertions, modifications;
|
||||
for (auto index : change_set.deletions.as_indexes()) {
|
||||
deletions.push_back(Value::from_number(ctx, index));
|
||||
}
|
||||
Object::set_property(ctx, object, "deletions", Object::create_array(ctx, deletions));
|
||||
|
||||
for (auto index : change_set.insertions.as_indexes()) {
|
||||
insertions.push_back(Value::from_number(ctx, index));
|
||||
}
|
||||
Object::set_property(ctx, object, "insertions", Object::create_array(ctx, insertions));
|
||||
|
||||
for (auto index : change_set.modifications.as_indexes()) {
|
||||
modifications.push_back(Value::from_number(ctx, index));
|
||||
}
|
||||
Object::set_property(ctx, object, "modifications", Object::create_array(ctx, modifications));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
@ -33,10 +33,20 @@ namespace realm {
|
||||
namespace js {
|
||||
|
||||
template<typename T>
|
||||
struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
|
||||
class List : public realm::List {
|
||||
public:
|
||||
List(std::shared_ptr<Realm> r, const ObjectSchema& s, LinkViewRef l) noexcept : realm::List(r, l) {}
|
||||
List(const realm::List &l) : realm::List(l) {}
|
||||
|
||||
std::vector<std::pair<Protected<typename T::Function>, NotificationToken>> m_notification_tokens;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
|
||||
using ContextType = typename T::Context;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
using FunctionType = typename T::Function;
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
@ -58,7 +68,12 @@ struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
|
||||
static void filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
||||
|
||||
|
||||
// observable
|
||||
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
std::string const name = "List";
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
@ -71,6 +86,9 @@ struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
|
||||
{"filtered", wrap<filtered>},
|
||||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
};
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
@ -82,7 +100,7 @@ struct ListClass : ClassDefinition<T, realm::List, CollectionClass<T>> {
|
||||
|
||||
template<typename T>
|
||||
typename T::Object ListClass<T>::create_instance(ContextType ctx, realm::List list) {
|
||||
return create_object<T, ListClass<T>>(ctx, new realm::List(std::move(list)));
|
||||
return create_object<T, ListClass<T>>(ctx, new realm::js::List<T>(std::move(list)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -230,6 +248,55 @@ template<typename T>
|
||||
void ListClass<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, ListClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
auto token = list->add_notification_callback([=](CollectionChangeSet change_set, std::exception_ptr exception) {
|
||||
typename T::HandleScope scope;
|
||||
|
||||
ValueType arguments[2];
|
||||
arguments[0] = static_cast<ObjectType>(protected_this);
|
||||
arguments[1] = CollectionClass<T>::create_collection_change_set(protected_ctx, change_set);
|
||||
Function<T>::call(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
list->m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
auto protected_function = Protected<FunctionType>(ctx, callback);
|
||||
|
||||
auto iter = list->m_notification_tokens.begin();
|
||||
typename Protected<FunctionType>::Comparator compare;
|
||||
while (iter != list->m_notification_tokens.end()) {
|
||||
if(compare(iter->first, protected_function)) {
|
||||
iter = list->m_notification_tokens.erase(iter);
|
||||
}
|
||||
else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
auto list = get_internal<T, ListClass<T>>(this_object);
|
||||
list->m_notification_tokens.clear();
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
35
src/js_observable.hpp
Normal file
35
src/js_observable.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "js_class.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
// Empty class that merely serves as useful type for now.
|
||||
class Observable {};
|
||||
|
||||
template<typename T>
|
||||
struct ObservableClass : ClassDefinition<T, Observable> {
|
||||
std::string const name = "Observable";
|
||||
};
|
||||
|
||||
} // js
|
||||
} // realm
|
@ -28,11 +28,14 @@
|
||||
#include "js_list.hpp"
|
||||
#include "js_results.hpp"
|
||||
#include "js_schema.hpp"
|
||||
#include "js_observable.hpp"
|
||||
#include "js_sync.hpp"
|
||||
|
||||
#include "shared_realm.hpp"
|
||||
#include "binding_context.hpp"
|
||||
#include "object_accessor.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "sync_config.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
@ -99,8 +102,10 @@ class RealmDelegate : public BindingContext {
|
||||
Protected<GlobalContextType> m_context;
|
||||
std::list<Protected<FunctionType>> m_notifications;
|
||||
std::weak_ptr<realm::Realm> m_realm;
|
||||
|
||||
|
||||
void notify(const char *notification_name) {
|
||||
typename T::HandleScope scope;
|
||||
|
||||
SharedRealm realm = m_realm.lock();
|
||||
if (!realm) {
|
||||
throw std::runtime_error("Realm no longer exists");
|
||||
@ -124,7 +129,7 @@ void set_default_path(std::string path);
|
||||
void delete_all_realms();
|
||||
|
||||
template<typename T>
|
||||
class RealmClass : public ClassDefinition<T, SharedRealm> {
|
||||
class RealmClass : public ClassDefinition<T, SharedRealm, ObservableClass<T>> {
|
||||
using GlobalContextType = typename T::GlobalContext;
|
||||
using ContextType = typename T::Context;
|
||||
using FunctionType = typename T::Function;
|
||||
@ -162,7 +167,7 @@ public:
|
||||
static void schema_version(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void clear_test_state(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void copy_bundled_realm_files(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
|
||||
// static properties
|
||||
static void get_default_path(ContextType, ObjectType, ReturnValue &);
|
||||
static void set_default_path(ContextType, ObjectType, ValueType value);
|
||||
@ -254,12 +259,14 @@ inline typename T::Function RealmClass<T>::create_constructor(ContextType ctx) {
|
||||
FunctionType list_constructor = ObjectWrap<T, ListClass<T>>::create_constructor(ctx);
|
||||
FunctionType results_constructor = ObjectWrap<T, ResultsClass<T>>::create_constructor(ctx);
|
||||
FunctionType realm_object_constructor = ObjectWrap<T, RealmObjectClass<T>>::create_constructor(ctx);
|
||||
FunctionType sync_constructor = SyncClass<T>::create_constructor(ctx);
|
||||
|
||||
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete;
|
||||
Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes);
|
||||
Object::set_property(ctx, realm_constructor, "List", list_constructor, attributes);
|
||||
Object::set_property(ctx, realm_constructor, "Results", results_constructor, attributes);
|
||||
Object::set_property(ctx, realm_constructor, "Object", realm_object_constructor, attributes);
|
||||
Object::set_property(ctx, realm_constructor, "Sync", sync_constructor, attributes);
|
||||
|
||||
return realm_constructor;
|
||||
}
|
||||
@ -376,6 +383,8 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
|
||||
std::string encryption_key = NativeAccessor::to_binary(ctx, encryption_key_value);
|
||||
config.encryption_key = std::vector<char>(encryption_key.begin(), encryption_key.end());
|
||||
}
|
||||
|
||||
SyncClass<T>::populate_sync_config(ctx, object, config);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -31,10 +31,25 @@ namespace realm {
|
||||
namespace js {
|
||||
|
||||
template<typename T>
|
||||
struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
|
||||
class Results : public realm::Results {
|
||||
public:
|
||||
Results(Results const& r) : realm::Results(r) {};
|
||||
Results(realm::Results const& r) : realm::Results(r) {};
|
||||
Results(Results&&) = default;
|
||||
Results& operator=(Results&&) = default;
|
||||
Results& operator=(Results const&) = default;
|
||||
|
||||
using realm::Results::Results;
|
||||
|
||||
std::vector<std::pair<Protected<typename T::Function>, NotificationToken>> m_notification_tokens;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<T>> {
|
||||
using ContextType = typename T::Context;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
using FunctionType = typename T::Function;
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
@ -56,6 +71,11 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
|
||||
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
||||
|
||||
// observable
|
||||
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
std::string const name = "Results";
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
@ -63,6 +83,9 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
|
||||
{"filtered", wrap<filtered>},
|
||||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
};
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
@ -74,13 +97,13 @@ struct ResultsClass : ClassDefinition<T, realm::Results, CollectionClass<T>> {
|
||||
|
||||
template<typename T>
|
||||
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, realm::Results results) {
|
||||
return create_object<T, ResultsClass<T>>(ctx, new realm::Results(std::move(results)));
|
||||
return create_object<T, ResultsClass<T>>(ctx, new realm::js::Results<T>(std::move(results)));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename T::Object ResultsClass<T>::create_instance(ContextType ctx, SharedRealm realm, const ObjectSchema &object_schema) {
|
||||
auto table = ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
|
||||
return create_object<T, ResultsClass<T>>(ctx, new realm::Results(realm, *table));
|
||||
return create_object<T, ResultsClass<T>>(ctx, new realm::js::Results<T>(realm, *table));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@ -155,8 +178,8 @@ typename T::Object ResultsClass<T>::create_sorted(ContextType ctx, const U &coll
|
||||
}
|
||||
|
||||
auto table = realm::ObjectStore::table_for_object_type(realm->read_group(), object_schema.name);
|
||||
auto results = new realm::Results(realm, collection.get_query(),
|
||||
{*table, std::move(columns), std::move(ascending)});
|
||||
auto results = new realm::js::Results<T>(realm, collection.get_query(),
|
||||
{*table, std::move(columns), std::move(ascending)});
|
||||
return create_object<T, ResultsClass<T>>(ctx, results);
|
||||
}
|
||||
|
||||
@ -209,6 +232,55 @@ template<typename T>
|
||||
void ResultsClass<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, ResultsClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
Protected<FunctionType> protected_callback(ctx, callback);
|
||||
Protected<ObjectType> protected_this(ctx, this_object);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
auto token = results->add_notification_callback([=](CollectionChangeSet change_set, std::exception_ptr exception) {
|
||||
typename T::HandleScope scope;
|
||||
|
||||
ValueType arguments[2];
|
||||
arguments[0] = static_cast<ObjectType>(protected_this);
|
||||
arguments[1] = CollectionClass<T>::create_collection_change_set(protected_ctx, change_set);
|
||||
Function<T>::call(protected_ctx, protected_callback, protected_this, 2, arguments);
|
||||
});
|
||||
results->m_notification_tokens.emplace_back(protected_callback, std::move(token));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
auto protected_function = Protected<FunctionType>(ctx, callback);
|
||||
|
||||
auto iter = results->m_notification_tokens.begin();
|
||||
typename Protected<FunctionType>::Comparator compare;
|
||||
while (iter != results->m_notification_tokens.end()) {
|
||||
if(compare(iter->first, protected_function)) {
|
||||
iter = results->m_notification_tokens.erase(iter);
|
||||
}
|
||||
else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
results->m_notification_tokens.clear();
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
226
src/js_sync.hpp
Normal file
226
src/js_sync.hpp
Normal file
@ -0,0 +1,226 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include "js_class.hpp"
|
||||
#include "js_collection.hpp"
|
||||
#include "sync_manager.hpp"
|
||||
#include "sync_config.hpp"
|
||||
#include "sync_session.hpp"
|
||||
#include "realm/util/logger.hpp"
|
||||
#include "realm/util/uri.hpp"
|
||||
|
||||
#if REALM_PLATFORM_NODE
|
||||
#include "node/node_sync_logger.hpp"
|
||||
#endif
|
||||
|
||||
#if !REALM_DEVELOPER_EDITION
|
||||
#include "js_enterprise.hpp"
|
||||
#endif
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
template<typename T>
|
||||
class SyncClass : public ClassDefinition<T, void *> {
|
||||
using GlobalContextType = typename T::GlobalContext;
|
||||
using ContextType = typename T::Context;
|
||||
using FunctionType = typename T::Function;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
using String = js::String<T>;
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using Function = js::Function<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
using NativeAccessor = realm::NativeAccessor<ValueType, ContextType>;
|
||||
|
||||
public:
|
||||
std::string const name = "Sync";
|
||||
|
||||
static FunctionType create_constructor(ContextType);
|
||||
|
||||
static void set_sync_log_level(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void set_verify_servers_ssl_certificate(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
#if REALM_PLATFORM_NODE
|
||||
static void set_sync_logger(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
#endif
|
||||
|
||||
// private
|
||||
static void refresh_access_token(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void populate_sync_config(ContextType, ObjectType config_object, Realm::Config&);
|
||||
|
||||
// static properties
|
||||
static void get_is_developer_edition(ContextType, ObjectType, ReturnValue &);
|
||||
|
||||
MethodMap<T> const static_methods = {
|
||||
{"refreshAccessToken", wrap<refresh_access_token>},
|
||||
{"setLogLevel", wrap<set_sync_log_level>},
|
||||
{"setVerifyServersSslCertificate", wrap<set_verify_servers_ssl_certificate>},
|
||||
#if REALM_PLATFORM_NODE
|
||||
{"setSyncLogger", wrap<set_sync_logger>},
|
||||
#endif
|
||||
};
|
||||
|
||||
PropertyMap<T> const static_properties {
|
||||
{"isDeveloperEdition", {wrap<get_is_developer_edition>, nullptr}}
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline typename T::Function SyncClass<T>::create_constructor(ContextType ctx) {
|
||||
FunctionType sync_constructor = ObjectWrap<T, SyncClass<T>>::create_constructor(ctx);
|
||||
|
||||
Protected<ValueType> refresh(ctx, Object::validated_get_function(ctx, sync_constructor, std::string("refreshAccessToken")));
|
||||
Protected<ObjectType> protected_sync(ctx, sync_constructor);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
realm::SyncManager::shared().set_login_function([=](const std::string& path, const realm::SyncConfig& config) {
|
||||
typename T::HandleScope handle_scope;
|
||||
|
||||
FunctionType user_constructor = Object::validated_get_function(protected_ctx, protected_sync, std::string("User"));
|
||||
FunctionType authenticate = Object::validated_get_function(protected_ctx, user_constructor, "authenticateRealm");
|
||||
ObjectType users = Object::validated_get_object(protected_ctx, user_constructor, std::string("activeUsers"));
|
||||
ObjectType user = Object::validated_get_object(protected_ctx, users, config.user_tag.c_str(), "Invalid user identity");
|
||||
|
||||
if (Object::validated_get_boolean(protected_ctx, user, "isAdmin")) {
|
||||
std::string token = Object::validated_get_string(protected_ctx, user, "token");
|
||||
|
||||
// FIXME: This log-in callback is called while the object store still holds some sync-related locks.
|
||||
// Notify the object store of the access token asynchronously to avoid the deadlock that would result
|
||||
// from reentering the object store here.
|
||||
auto thread = std::thread([path, config, token]{
|
||||
auto session = SyncManager::shared().get_existing_active_session(path);
|
||||
session->refresh_access_token(token, config.realm_url);
|
||||
});
|
||||
thread.detach();
|
||||
}
|
||||
else {
|
||||
ValueType arguments[3];
|
||||
arguments[0] = Value::from_string(protected_ctx, path.c_str());
|
||||
arguments[1] = Value::from_string(protected_ctx, config.realm_url.c_str());
|
||||
arguments[2] = refresh;
|
||||
Function::call(protected_ctx, authenticate, user, 3, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
realm::SyncManager::shared().set_error_handler([=](int error_code, std::string message) {
|
||||
std::cout << error_code << " " << message << std::endl;
|
||||
});
|
||||
|
||||
#if REALM_PLATFORM_NODE
|
||||
// AtExit is called before the V8 VM is disposed. We use it to clean the captured JS objects in the login function.
|
||||
// Should they be destructed after the VM is disposed, there will be a segmentation fault during node's shutdown.
|
||||
::node::AtExit([](void*) {
|
||||
realm::SyncManager::shared().set_login_function(nullptr);
|
||||
});
|
||||
#endif
|
||||
|
||||
#if !REALM_DEVELOPER_EDITION
|
||||
SyncEnterpriseClass<T>::add_methods(ctx, sync_constructor);
|
||||
#endif
|
||||
|
||||
return sync_constructor;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::set_sync_log_level(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
std::string log_level = Value::validated_to_string(ctx, arguments[0]);
|
||||
std::istringstream in(log_level); // Throws
|
||||
in.imbue(std::locale::classic()); // Throws
|
||||
in.unsetf(std::ios_base::skipws);
|
||||
util::Logger::Level log_level_2 = util::Logger::Level();
|
||||
in >> log_level_2; // Throws
|
||||
if (!in || !in.eof())
|
||||
throw std::runtime_error("Bad log level");
|
||||
realm::SyncManager::shared().set_log_level(log_level_2);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::set_verify_servers_ssl_certificate(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
bool verify_servers_ssl_certificate = Value::validated_to_boolean(ctx, arguments[0]);
|
||||
realm::SyncManager::shared().set_client_should_validate_ssl(verify_servers_ssl_certificate);
|
||||
}
|
||||
|
||||
#if REALM_HAVE_NODE_SYNC_LOGGER
|
||||
template<typename T>
|
||||
void SyncClass<T>::set_sync_logger(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
auto callback = Value::validated_to_function(ctx, arguments[0]);
|
||||
node::SyncLoggerFactory *factory = new node::SyncLoggerFactory(ctx, this_object, callback); // Throws
|
||||
realm::SyncManager::shared().set_logger_factory(*factory);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::refresh_access_token(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 2);
|
||||
|
||||
static const String token_string = "token";
|
||||
static const String file_url_string = "file_url";
|
||||
static const String realm_url_string = "resolved_realm_url";
|
||||
|
||||
ObjectType json_arguments = Value::validated_to_object(ctx, arguments[1]);
|
||||
std::string token = Object::validated_get_string(ctx, json_arguments, token_string);
|
||||
std::string file_url = Object::validated_get_string(ctx, json_arguments, file_url_string);
|
||||
std::string realm_url = Object::validated_get_string(ctx, json_arguments, realm_url_string);
|
||||
|
||||
if (auto session = SyncManager::shared().get_existing_active_session(file_url)) {
|
||||
session->refresh_access_token(token, realm_url);
|
||||
return_value.set(true);
|
||||
}
|
||||
else {
|
||||
return_value.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType config_object, Realm::Config& config) {
|
||||
ValueType sync_config_value = Object::get_property(ctx, config_object, "sync");
|
||||
if (!Value::is_undefined(ctx, sync_config_value)) {
|
||||
auto sync_config_object = Value::validated_to_object(ctx, sync_config_value);
|
||||
|
||||
ObjectType user = Object::validated_get_object(ctx, sync_config_object, "user");
|
||||
config.sync_config = std::shared_ptr<SyncConfig>(
|
||||
new SyncConfig(Object::validated_get_string(ctx, user, "identity"),
|
||||
Object::validated_get_string(ctx, sync_config_object, "url"),
|
||||
{}, SyncSessionStopPolicy::AfterChangesUploaded)
|
||||
);
|
||||
config.schema_mode = SchemaMode::Additive;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::get_is_developer_edition(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
#if REALM_DEVELOPER_EDITION
|
||||
return_value.set(true);
|
||||
#else
|
||||
return_value.set(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
@ -133,6 +133,11 @@ struct Function {
|
||||
using ValueType = typename T::Value;
|
||||
|
||||
static ValueType call(ContextType, const FunctionType &, const ObjectType &, size_t, const ValueType[]);
|
||||
template<size_t N> static ValueType call(ContextType ctx, const FunctionType &function,
|
||||
const ObjectType &this_object, const ValueType (&arguments)[N])
|
||||
{
|
||||
return call(ctx, function, this_object, N, arguments);
|
||||
}
|
||||
static ValueType call(ContextType ctx, const FunctionType &function, size_t argument_count, const ValueType arguments[]) {
|
||||
return call(ctx, function, {}, argument_count, arguments);
|
||||
}
|
||||
@ -247,6 +252,10 @@ class Protected {
|
||||
bool operator!=(const ValueType &) const;
|
||||
bool operator==(const Protected<ValueType> &) const;
|
||||
bool operator!=(const Protected<ValueType> &) const;
|
||||
|
||||
struct Comparator {
|
||||
bool operator()(const Protected<ValueType>& a, const Protected<ValueType>& b) const;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -47,6 +47,12 @@ class Protected<JSGlobalContextRef> {
|
||||
operator bool() const {
|
||||
return m_context != nullptr;
|
||||
}
|
||||
|
||||
struct Comparator {
|
||||
bool operator() (const Protected<JSGlobalContextRef>& a, const Protected<JSGlobalContextRef>& b) const {
|
||||
return a.m_context == b.m_context;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template<>
|
||||
@ -75,6 +81,21 @@ class Protected<JSValueRef> {
|
||||
operator bool() const {
|
||||
return m_value != nullptr;
|
||||
}
|
||||
|
||||
struct Comparator {
|
||||
bool operator() (const Protected<JSValueRef>& a, const Protected<JSValueRef>& b) const {
|
||||
if (a.m_context != b.m_context) {
|
||||
return false;
|
||||
}
|
||||
return JSValueIsStrictEqual(a.m_context, a.m_value, b.m_value);
|
||||
}
|
||||
};
|
||||
|
||||
Protected<JSValueRef>& operator=(Protected<JSValueRef> other) {
|
||||
std::swap(m_context, other.m_context);
|
||||
std::swap(m_value, other.m_value);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
@ -89,6 +110,11 @@ class Protected<JSObjectRef> : public Protected<JSValueRef> {
|
||||
JSValueRef value = static_cast<JSValueRef>(*this);
|
||||
return (JSObjectRef)value;
|
||||
}
|
||||
|
||||
Protected<JSObjectRef>& operator=(Protected<JSObjectRef> other) {
|
||||
std::swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // js
|
||||
|
@ -34,6 +34,7 @@ struct Types {
|
||||
using Object = JSObjectRef;
|
||||
using String = JSStringRef;
|
||||
using Function = JSObjectRef;
|
||||
using HandleScope = void *;
|
||||
|
||||
using ConstructorCallback = JSObjectCallAsConstructorCallback;
|
||||
using FunctionCallback = JSObjectCallAsFunctionCallback;
|
||||
|
@ -1,85 +0,0 @@
|
||||
{
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "realm",
|
||||
"sources": [
|
||||
"node_init.cpp",
|
||||
"platform.cpp",
|
||||
"../js_realm.cpp",
|
||||
"../object-store/src/collection_notifications.cpp",
|
||||
"../object-store/src/index_set.cpp",
|
||||
"../object-store/src/list.cpp",
|
||||
"../object-store/src/object_schema.cpp",
|
||||
"../object-store/src/object_store.cpp",
|
||||
"../object-store/src/results.cpp",
|
||||
"../object-store/src/schema.cpp",
|
||||
"../object-store/src/shared_realm.cpp",
|
||||
"../object-store/src/thread_confined.cpp",
|
||||
"../object-store/src/impl/collection_change_builder.cpp",
|
||||
"../object-store/src/impl/collection_notifier.cpp",
|
||||
"../object-store/src/impl/handover.cpp",
|
||||
"../object-store/src/impl/list_notifier.cpp",
|
||||
"../object-store/src/impl/realm_coordinator.cpp",
|
||||
"../object-store/src/impl/results_notifier.cpp",
|
||||
"../object-store/src/impl/transact_log_handler.cpp",
|
||||
"../object-store/src/impl/node/weak_realm_notifier.cpp",
|
||||
"../object-store/src/parser/parser.cpp",
|
||||
"../object-store/src/parser/query_builder.cpp",
|
||||
"../object-store/src/util/format.cpp",
|
||||
"../object-store/src/util/thread_id.cpp"
|
||||
],
|
||||
"include_dirs": [
|
||||
"..",
|
||||
"../object-store/src",
|
||||
"../object-store/src/impl",
|
||||
"../object-store/src/impl/apple",
|
||||
"../object-store/src/parser",
|
||||
"../object-store/external/pegtl",
|
||||
"../../core-node/include",
|
||||
"<!(node -e 'require(\"nan\")')"
|
||||
],
|
||||
"library_dirs": [
|
||||
"$(srcdir)/../../core-node"
|
||||
],
|
||||
"defines": [
|
||||
"REALM_HAVE_CONFIG",
|
||||
"REALM_PLATFORM_NODE=1"
|
||||
],
|
||||
"cflags_cc": [
|
||||
"-fexceptions",
|
||||
"-frtti",
|
||||
"-std=c++14",
|
||||
"-Wno-missing-field-initializers",
|
||||
"-Wno-return-type"
|
||||
],
|
||||
"libraries": ["-lrealm-node"],
|
||||
"xcode_settings": {
|
||||
"CLANG_CXX_LANGUAGE_STANDARD": "c++14",
|
||||
"CLANG_CXX_LIBRARY": "libc++",
|
||||
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
|
||||
"GCC_ENABLE_CPP_RTTI": "YES",
|
||||
"MACOSX_DEPLOYMENT_TARGET": "10.8",
|
||||
"OTHER_LDFLAGS": ["-framework", "Foundation"]
|
||||
},
|
||||
"conditions": [
|
||||
[
|
||||
"OS=='linux'", {
|
||||
"sources": [
|
||||
"../object-store/src/impl/android/external_commit_helper.cpp",
|
||||
]
|
||||
}
|
||||
],
|
||||
["OS=='mac'", {
|
||||
"sources": [
|
||||
"../object-store/src/impl/apple/external_commit_helper.cpp"
|
||||
]
|
||||
}]
|
||||
],
|
||||
"configurations": {
|
||||
"Debug": {
|
||||
"defines": ["DEBUG=1"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o pipefail
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
CONFIGURATION="${1:-"Release"}"
|
||||
PATH="$(cd ../../node_modules/.bin && pwd):$PATH"
|
||||
|
||||
if [ -s "${HOME}/.nvm/nvm.sh" ]; then
|
||||
. "${HOME}/.nvm/nvm.sh"
|
||||
nvm use 5.4.0 || true
|
||||
fi
|
||||
|
||||
node-gyp configure
|
||||
|
||||
# Being explicit about debug mode rather than relying on defaults.
|
||||
if [[ $CONFIGURATION == 'Debug' ]]; then
|
||||
node-gyp build --debug
|
||||
else
|
||||
node-gyp build --no-debug
|
||||
fi
|
||||
|
||||
# Link to the appropriate module in the build directory.
|
||||
cd ../..
|
||||
mkdir -p build
|
||||
ln -fs "../src/node/build/$CONFIGURATION/realm.node" build/
|
89
src/node/gyp/realm.gyp
Normal file
89
src/node/gyp/realm.gyp
Normal file
@ -0,0 +1,89 @@
|
||||
{
|
||||
"variables": {
|
||||
"use_realm_debug": "<!(echo $REALMJS_USE_DEBUG_CORE)",
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"target_name": "realm-core",
|
||||
"type": "none",
|
||||
"direct_dependent_settings": {
|
||||
"conditions": [
|
||||
["use_realm_debug!=''", {
|
||||
"libraries": [ "-lrealm-node-dbg" ],
|
||||
"defines": [ "REALM_DEBUG=1" ]
|
||||
}, {
|
||||
"libraries": [ "-lrealm-node" ]
|
||||
}]
|
||||
]
|
||||
},
|
||||
"all_dependent_settings": {
|
||||
"defines": [ "REALM_HAVE_CONFIG", "REALM_PLATFORM_NODE=1", "REALM_ENABLE_SYNC" ]
|
||||
},
|
||||
"variables": {
|
||||
"prefix": "<!(echo $REALM_CORE_PREFIX)"
|
||||
},
|
||||
"conditions": [
|
||||
["prefix!=''", {
|
||||
"all_dependent_settings": {
|
||||
"include_dirs": [ "<(prefix)/src" ],
|
||||
},
|
||||
"direct_dependent_settings": {
|
||||
"library_dirs": [ "<(prefix)/src/realm" ]
|
||||
}
|
||||
}, {
|
||||
"conditions": [
|
||||
["OS=='mac'", {
|
||||
"dependencies": [ "vendored-realm" ]
|
||||
}]
|
||||
]
|
||||
}]
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name": "realm-sync",
|
||||
"type": "none",
|
||||
"dependencies": [ "realm-core" ], # sync headers include core headers
|
||||
"direct_dependent_settings": {
|
||||
"conditions": [
|
||||
["use_realm_debug!=''", {
|
||||
"libraries": [ "-lrealm-sync-node-dbg" ]
|
||||
}, {
|
||||
"libraries": [ "-lrealm-sync-node" ]
|
||||
}]
|
||||
]
|
||||
},
|
||||
"export_dependent_settings": [ "realm-core" ], # depending on sync is tantamount to depending on core
|
||||
"variables": {
|
||||
"prefix": "<!(echo $REALM_SYNC_PREFIX)"
|
||||
},
|
||||
"conditions": [
|
||||
["prefix!=''", {
|
||||
"all_dependent_settings": {
|
||||
"include_dirs": [ "<(prefix)/src" ],
|
||||
},
|
||||
"direct_dependent_settings": {
|
||||
"library_dirs": [ "<(prefix)/src/realm" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"conditions": [
|
||||
["OS=='mac'", {
|
||||
"dependencies": [ "vendored-realm" ]
|
||||
}]
|
||||
]
|
||||
}]
|
||||
],
|
||||
},
|
||||
{
|
||||
"variables": {
|
||||
"realm_vendor_dir%": "<(module_root_dir)/vendor",
|
||||
},
|
||||
"target_name": "vendored-realm",
|
||||
"type": "none",
|
||||
"all_dependent_settings": {
|
||||
"include_dirs": [ "<(realm_vendor_dir)/realm-sync/include" ],
|
||||
"library_dirs": [ "<(realm_vendor_dir)/realm-sync/osx" ]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
42
src/node/gyp/target_defaults.gypi
Normal file
42
src/node/gyp/target_defaults.gypi
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"target_defaults": {
|
||||
"variables": {
|
||||
"warning-flags": [
|
||||
"-Wno-missing-field-initializers",
|
||||
"-Wno-return-type"
|
||||
]
|
||||
},
|
||||
"cflags_cc!": [ # turn off default flags on older nodes on linux
|
||||
"-fno-exceptions",
|
||||
"-fno-rtti",
|
||||
"-std=gnu++0x"
|
||||
],
|
||||
"cflags_cc": [
|
||||
"-fexceptions",
|
||||
"-frtti",
|
||||
"-std=c++14",
|
||||
"<@(warning-flags)"
|
||||
],
|
||||
"include_dirs": [
|
||||
"<!(node -e \"require('nan')\")"
|
||||
],
|
||||
"configurations": {
|
||||
"Debug": {
|
||||
"defines": ["DEBUG=1"]
|
||||
}
|
||||
},
|
||||
"conditions": [
|
||||
["OS=='mac'", {
|
||||
"xcode_settings": {
|
||||
"CLANG_CXX_LANGUAGE_STANDARD": "c++14",
|
||||
"CLANG_CXX_LIBRARY": "libc++",
|
||||
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
|
||||
"GCC_ENABLE_CPP_RTTI": "YES",
|
||||
"MACOSX_DEPLOYMENT_TARGET": "10.8",
|
||||
"OTHER_LDFLAGS": ["-framework Foundation"],
|
||||
"WARNING_CFLAGS": [ "<@(warning-flags)" ]
|
||||
}
|
||||
}]
|
||||
]
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "node_init.hpp"
|
||||
#include "js_realm.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace node {
|
||||
|
@ -27,5 +27,3 @@
|
||||
#include "node_exception.hpp"
|
||||
#include "node_return_value.hpp"
|
||||
#include "node_object_accessor.hpp"
|
||||
|
||||
#include "js_realm.hpp"
|
||||
|
@ -35,7 +35,7 @@ class Protected {
|
||||
operator v8::Local<MemberType>() const {
|
||||
return Nan::New(m_value);
|
||||
}
|
||||
operator bool() const {
|
||||
explicit operator bool() const {
|
||||
return m_value.isEmpty();
|
||||
}
|
||||
bool operator==(const v8::Local<MemberType> &other) const {
|
||||
@ -50,6 +50,12 @@ class Protected {
|
||||
bool operator!=(const Protected<MemberType> &other) const {
|
||||
return m_value != other.m_value;
|
||||
}
|
||||
|
||||
struct Comparator {
|
||||
bool operator()(const Protected<MemberType>& a, const Protected<MemberType>& b) const {
|
||||
return Nan::New(a.m_value)->StrictEquals(Nan::New(b.m_value));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // node
|
||||
|
94
src/node/node_sync_logger.cpp
Normal file
94
src/node/node_sync_logger.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <mutex>
|
||||
|
||||
#include "node_init.hpp"
|
||||
#include "node_class.hpp"
|
||||
#include "js_realm.hpp"
|
||||
#include "node_types.hpp"
|
||||
#include "node_sync_logger.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace node {
|
||||
|
||||
SyncLoggerQueue::~SyncLoggerQueue() noexcept
|
||||
{
|
||||
m_callback_this_object.Reset();
|
||||
m_callback.Reset();
|
||||
}
|
||||
|
||||
void SyncLoggerQueue::log_uv_callback()
|
||||
{
|
||||
// This function is always executed by the Node.js event loop
|
||||
// thread.
|
||||
Nan::HandleScope scope;
|
||||
v8::Local<v8::Object> this_object = Nan::New(m_callback_this_object);
|
||||
v8::Local<v8::Function> callback = Nan::New(m_callback);
|
||||
|
||||
std::queue<SyncLoggerMessage> popped;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex); // Throws
|
||||
popped.swap(m_log_queue);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (popped.empty())
|
||||
break;
|
||||
|
||||
Nan::TryCatch trycatch;
|
||||
v8::Local<v8::Value> argv[] = {
|
||||
Nan::New((int)(popped.front().m_level)),
|
||||
Nan::New(popped.front().m_message).ToLocalChecked()
|
||||
};
|
||||
Nan::MakeCallback(this_object, callback, 2, argv);
|
||||
if (trycatch.HasCaught()) {
|
||||
throw node::Exception(m_v8_isolate, trycatch.Exception());
|
||||
}
|
||||
popped.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void SyncLogger::do_log(realm::util::Logger::Level level, std::string message)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex); // Throws
|
||||
m_log_queue.emplace(level, message);
|
||||
m_log_uv_async.send();
|
||||
}
|
||||
|
||||
SyncLoggerFactory::~SyncLoggerFactory() noexcept
|
||||
{
|
||||
m_callback_this_object.Reset();
|
||||
m_callback.Reset();
|
||||
}
|
||||
|
||||
std::unique_ptr<util::Logger> SyncLoggerFactory::make_logger(util::Logger::Level level)
|
||||
{
|
||||
v8::Local<v8::Object> this_object = Nan::New(m_callback_this_object);
|
||||
v8::Local<v8::Function> callback = Nan::New(m_callback);
|
||||
|
||||
SyncLogger *logger = new SyncLogger(m_v8_isolate, this_object, callback); // Throws
|
||||
logger->set_level_threshold(level);
|
||||
|
||||
return std::unique_ptr<util::Logger>(logger);
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
} // namespace realm
|
89
src/node/node_sync_logger.hpp
Normal file
89
src/node/node_sync_logger.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef REALM_NODE_GLOBAL_LOGGER_HPP
|
||||
#define REALM_NODE_GLOBAL_LOGGER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
|
||||
#include <nan.h>
|
||||
|
||||
#include "node_uv_async.hpp"
|
||||
|
||||
namespace realm {
|
||||
namespace node {
|
||||
|
||||
struct SyncLoggerMessage {
|
||||
std::string m_message;
|
||||
realm::util::Logger::Level m_level;
|
||||
SyncLoggerMessage(realm::util::Logger::Level level, std::string message):
|
||||
m_message(message),
|
||||
m_level(level) { }
|
||||
};
|
||||
|
||||
class SyncLoggerQueue {
|
||||
public:
|
||||
SyncLoggerQueue(v8::Isolate* v8_isolate, v8::Local<v8::Object> callback_this_object, v8::Local<v8::Function> callback) :
|
||||
m_log_uv_async([this] { log_uv_callback(); }), // Throws
|
||||
m_v8_isolate(v8_isolate),
|
||||
m_callback_this_object(callback_this_object),
|
||||
m_callback(callback) { }
|
||||
~SyncLoggerQueue() noexcept;
|
||||
|
||||
protected:
|
||||
void log_uv_callback();
|
||||
std::queue<SyncLoggerMessage> m_log_queue;
|
||||
std::mutex m_mutex;
|
||||
UvAsync m_log_uv_async;
|
||||
|
||||
private:
|
||||
v8::Isolate* m_v8_isolate;
|
||||
Nan::Persistent<v8::Object> m_callback_this_object;
|
||||
Nan::Persistent<v8::Function> m_callback;
|
||||
};
|
||||
|
||||
class SyncLogger: public realm::util::RootLogger, public SyncLoggerQueue {
|
||||
public:
|
||||
SyncLogger(v8::Isolate* v8_isolate, v8::Local<v8::Object> callback_this_object, v8::Local<v8::Function> callback) :
|
||||
SyncLoggerQueue(v8_isolate, callback_this_object, callback) { }
|
||||
|
||||
protected:
|
||||
void do_log(realm::util::Logger::Level, std::string) override final;
|
||||
};
|
||||
|
||||
class SyncLoggerFactory : public realm::SyncLoggerFactory {
|
||||
public:
|
||||
SyncLoggerFactory(v8::Isolate* v8_isolate, v8::Local<v8::Object> callback_this_object, v8::Local<v8::Function> callback) :
|
||||
m_v8_isolate(v8_isolate),
|
||||
m_callback_this_object(callback_this_object),
|
||||
m_callback(callback) { }
|
||||
~SyncLoggerFactory() noexcept;
|
||||
|
||||
virtual std::unique_ptr<util::Logger> make_logger(util::Logger::Level level);
|
||||
private:
|
||||
v8::Isolate* m_v8_isolate;
|
||||
Nan::Persistent<v8::Object> m_callback_this_object;
|
||||
Nan::Persistent<v8::Function> m_callback;
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
} // namespace realm
|
||||
|
||||
#endif // REALM_NODE_GLOBAL_LOGGER_HPP
|
@ -41,6 +41,7 @@ struct Types {
|
||||
using Object = v8::Local<v8::Object>;
|
||||
using String = v8::Local<v8::String>;
|
||||
using Function = v8::Local<v8::Function>;
|
||||
using HandleScope = Nan::HandleScope;
|
||||
|
||||
using ConstructorCallback = Nan::FunctionCallback;
|
||||
using FunctionCallback = Nan::FunctionCallback;
|
||||
|
104
src/node/node_uv_async.hpp
Normal file
104
src/node/node_uv_async.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef REALM_NODE_UV_ASYNC_HPP
|
||||
#define REALM_NODE_UV_ASYNC_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
namespace realm {
|
||||
namespace node {
|
||||
|
||||
// Must be created and destroyed by the thread that is associated with the
|
||||
// specified libuv event loop.
|
||||
class UvAsync {
|
||||
public:
|
||||
UvAsync(std::function<void()> func, uv_loop_t* = uv_default_loop());
|
||||
~UvAsync() noexcept;
|
||||
|
||||
/// Schedule the associated callback function to be executed by the
|
||||
/// associated libuv event loop. May be called by any thread.
|
||||
void send();
|
||||
|
||||
private:
|
||||
struct Rep;
|
||||
|
||||
Rep* m_rep;
|
||||
|
||||
static void exec(uv_async_t* handle) noexcept;
|
||||
static void close(uv_handle_t* handle) noexcept;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// implementation
|
||||
|
||||
struct UvAsync::Rep {
|
||||
uv_async_t handle;
|
||||
std::function<void()> func;
|
||||
};
|
||||
|
||||
inline UvAsync::UvAsync(std::function<void()> func, uv_loop_t* loop)
|
||||
{
|
||||
std::unique_ptr<Rep> rep(new Rep);
|
||||
rep->handle.data = &*rep;
|
||||
rep->func = std::move(func);
|
||||
int ret = uv_async_init(loop, &rep->handle, &UvAsync::exec);
|
||||
if (ret < 0)
|
||||
throw std::runtime_error("uv_async_init() failed");
|
||||
m_rep = rep.release();
|
||||
}
|
||||
|
||||
inline UvAsync::~UvAsync() noexcept
|
||||
{
|
||||
m_rep->func = std::function<void()>();
|
||||
uv_close(reinterpret_cast<uv_handle_t*>(&m_rep->handle), &UvAsync::close);
|
||||
}
|
||||
|
||||
inline void UvAsync::send()
|
||||
{
|
||||
int ret = uv_async_send(&m_rep->handle);
|
||||
if (ret < 0)
|
||||
throw std::runtime_error("uv_async_send() failed");
|
||||
}
|
||||
|
||||
inline void UvAsync::exec(uv_async_t* handle) noexcept
|
||||
{
|
||||
Rep* rep = static_cast<Rep*>(handle->data);
|
||||
if (rep->func)
|
||||
rep->func(); // Throws
|
||||
|
||||
// FIXME: How to deal with C++ exceptions here?
|
||||
}
|
||||
|
||||
inline void UvAsync::close(uv_handle_t* handle) noexcept
|
||||
{
|
||||
Rep* rep = static_cast<Rep*>(handle->data);
|
||||
delete rep;
|
||||
}
|
||||
|
||||
} // namespace node
|
||||
} // namespace realm
|
||||
|
||||
#endif // REALM_NODE_UV_ASYNC_HPP
|
@ -129,7 +129,7 @@ inline bool node::Value::to_boolean(v8::Isolate* isolate, const v8::Local<v8::Va
|
||||
template<>
|
||||
inline double node::Value::to_number(v8::Isolate* isolate, const v8::Local<v8::Value> &value) {
|
||||
double number = Nan::To<double>(value).FromMaybe(NAN);
|
||||
if (isnan(number)) {
|
||||
if (std::isnan(number)) {
|
||||
throw std::invalid_argument("Value not convertible to a number.");
|
||||
}
|
||||
return number;
|
||||
|
85
test.js
Normal file
85
test.js
Normal file
@ -0,0 +1,85 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
'use strict';
|
||||
|
||||
const Realm = require('.');
|
||||
const prompt = require('prompt');
|
||||
const mkdirp = require('mkdirp');
|
||||
const wildcard = require('wildcard');
|
||||
|
||||
// User.loginWithProvider('http://127.0.0.1:8080/', 'debug', 'abcd', function(error, user) {
|
||||
// console.log(user);
|
||||
// });
|
||||
|
||||
Realm.Sync.User.create('http://127.0.0.1:9080/', 'ari', 'aaa', function(error, user) {
|
||||
|
||||
var notifier_dir = './notifier';
|
||||
mkdirp.sync(notifier_dir);
|
||||
|
||||
var access_token = 'ewoJImlkZW50aXR5IjogImFkbWluIiwKCSJhY2Nlc3MiOiBbInVwbG9hZCIsICJkb3dubG9hZCIsICJtYW5hZ2UiXQp9Cg==:DlFksxA+cJyEOc9bu6JwBUfDi4fJCagjAcIPPsoisjqfmOzSrk5Omuw0IkxCRU534p2+CAAj5IOH47DfObPtAA8q2DHguYDOKWYxyktS/6doPCqDHYN7k9EgUHdPTkESNkuPZbaVfXZTGzocB8m7+MaEXJde7FGPbh1sBz/+sPldnlAhnOqO5QbWzIEyoGHiOSg3V7UCh2H8kalr3tef7fkE2X65OBMgcarPvM5M6sPijOx2N5zrVrjL2wvguP9zS+g2ybFPUqV3DGv3S8cnGA+wVId/jCfGc2ujNhecunJdENH+/pL+0BTYHCFEWkY1WP1NUyti60FwRaXAtcYxeA==';
|
||||
var admin_user = new Realm.Sync.User.adminUser('http://127.0.0.1:9080/', access_token);
|
||||
|
||||
Realm.Sync.setLogLevel('error');
|
||||
Realm.Sync.setGlobalListener(notifier_dir, 'realm://127.0.0.1:9080', admin_user,
|
||||
(name) => {
|
||||
console.log('filter: ' + name);
|
||||
return true;
|
||||
},
|
||||
(name, realm, changes) => {
|
||||
console.log('change: ' + name);
|
||||
console.log(changes);
|
||||
}
|
||||
);
|
||||
console.log('global notifier listening...');
|
||||
|
||||
Realm.Sync.User.login('http://127.0.0.1:9080/', 'ari', 'aaa', function(error, user) {
|
||||
console.log(user);
|
||||
|
||||
var realm = new Realm({
|
||||
sync: {
|
||||
user: user,
|
||||
url: 'realm://127.0.0.1:9080/~/demo/realm1'
|
||||
},
|
||||
schema: [{
|
||||
name: 'IntObject',
|
||||
properties: {
|
||||
int: 'int'
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
function create(err, result) {
|
||||
if (err) {
|
||||
exit();
|
||||
}
|
||||
if (!err) {
|
||||
realm.write(() => {
|
||||
realm.create('IntObject', {int: parseInt(result.int)});
|
||||
});
|
||||
console.log(realm.objects('IntObject'));
|
||||
}
|
||||
|
||||
prompt.get(['int'], create);
|
||||
}
|
||||
prompt.start();
|
||||
prompt.get(['int'], create);
|
||||
});
|
||||
|
||||
});
|
||||
|
2
tests/.gitignore
vendored
Normal file
2
tests/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/sync-bundle/
|
||||
/junitresults-*.xml
|
@ -23,14 +23,13 @@
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const mockery = require('mockery');
|
||||
|
||||
function runTests() {
|
||||
const Realm = require('realm');
|
||||
const RealmTests = require('./js');
|
||||
|
||||
RealmTests.registerTests({
|
||||
WorkerTests: require('./js/worker-tests'),
|
||||
AsyncTests: require('./js/async-tests'),
|
||||
});
|
||||
|
||||
const testNames = RealmTests.getTestNames();
|
||||
@ -79,10 +78,6 @@ function runTests() {
|
||||
}
|
||||
|
||||
if (require.main == module) {
|
||||
mockery.enable();
|
||||
mockery.warnOnUnregistered(false);
|
||||
mockery.registerMock('realm', require('..'));
|
||||
|
||||
runTests().then(
|
||||
(passed) => {
|
||||
if (!passed) {
|
||||
|
@ -27,5 +27,6 @@
|
||||
|
||||
- (JSValue *)loadModuleFromURL:(NSURL *)url error:(NSError **)error;
|
||||
- (JSValue *)loadJSONFromURL:(NSURL *)url error:(NSError **)error;
|
||||
- (JSValue *)loadGlobalModule:(NSString *)name relativeToURL:(NSURL *)url error:(NSError **)error;
|
||||
|
||||
@end
|
||||
|
@ -195,6 +195,18 @@ static NSString * const RJSModuleLoaderErrorDomain = @"RJSModuleLoaderErrorDomai
|
||||
BOOL isDirectory;
|
||||
|
||||
if ([fileManager fileExistsAtPath:moduleURL.path isDirectory:&isDirectory] && isDirectory) {
|
||||
NSURL *packageURL = [moduleURL URLByAppendingPathComponent:@"package.json"];
|
||||
NSDictionary *package;
|
||||
|
||||
if ([fileManager fileExistsAtPath:packageURL.path]) {
|
||||
NSError *error;
|
||||
NSData *data = [NSData dataWithContentsOfURL:packageURL options:0 error:&error];
|
||||
|
||||
package = data ? [NSJSONSerialization JSONObjectWithData:data options:0 error:&error] : nil;
|
||||
NSAssert(package, @"%@", error);
|
||||
}
|
||||
|
||||
moduleURL = [moduleURL URLByAppendingPathComponent:package[@"main"] ?: @"index.js"];
|
||||
return [self loadModuleFromURL:moduleURL error:error];
|
||||
}
|
||||
|
||||
|
@ -31,12 +31,23 @@
|
||||
|
||||
+ (XCTestSuite *)defaultTestSuite {
|
||||
XCTestSuite *suite = [super defaultTestSuite];
|
||||
JSContext *context = [[JSContext alloc] init];
|
||||
|
||||
// We need a JS context from a UIWebView so it has setTimeout, Promise, etc.
|
||||
UIWebView *webView = [[UIWebView alloc] init];
|
||||
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
|
||||
RJSModuleLoader *moduleLoader = [[RJSModuleLoader alloc] initWithContext:context];
|
||||
NSURL *realmURL = [[NSBundle bundleForClass:self] URLForResource:@"index" withExtension:@"js" subdirectory:@"lib"];
|
||||
NSURL *scriptURL = [[NSBundle bundleForClass:self] URLForResource:@"index" withExtension:@"js" subdirectory:@"js"];
|
||||
NSError *error;
|
||||
|
||||
// The ES6 global Promise constructor was added in iOS 8.
|
||||
if (![context[@"Promise"] isObject]) {
|
||||
JSValue *promiseModule = [moduleLoader loadGlobalModule:@"es6-promise" relativeToURL:scriptURL error:&error];
|
||||
NSAssert(promiseModule, @"%@", error);
|
||||
|
||||
context[@"Promise"] = promiseModule[@"Promise"];
|
||||
}
|
||||
|
||||
// Create Realm constructor in the JS context.
|
||||
RJSInitializeInContext(context.JSGlobalContextRef);
|
||||
|
||||
@ -73,25 +84,46 @@
|
||||
JSContext *context = testObject.context;
|
||||
context.exception = nil;
|
||||
|
||||
[testObject invokeMethod:@"runTest" withArguments:@[NSStringFromClass(self.class), method]];
|
||||
JSValue *promise = [testObject invokeMethod:@"runTest" withArguments:@[NSStringFromClass(self.class), method]];
|
||||
|
||||
JSValue *exception = context.exception;
|
||||
if (exception) {
|
||||
JSValue *message = [exception hasProperty:@"message"] ? exception[@"message"] : exception;
|
||||
NSString *source = [exception hasProperty:@"sourceURL"] ? [exception[@"sourceURL"] toString] : nil;
|
||||
NSUInteger line = [exception hasProperty:@"line"] ? [exception[@"line"] toUInt32] - 1 : 0;
|
||||
NSURL *sourceURL = nil;
|
||||
if (context.exception) {
|
||||
[self recordException:context.exception];
|
||||
return;
|
||||
}
|
||||
|
||||
if (source) {
|
||||
NSString *path = [NSString pathWithComponents:@[[@(__FILE__) stringByDeletingLastPathComponent], @"..", @"js", source.lastPathComponent]];
|
||||
sourceURL = [NSURL URLWithString:path];
|
||||
}
|
||||
if ([promise isObject]) {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Promise resolved or rejected"];
|
||||
|
||||
[self recordFailureWithDescription:message.description
|
||||
inFile:sourceURL ? sourceURL.absoluteString : @(__FILE__)
|
||||
atLine:sourceURL ? line : __LINE__
|
||||
expected:YES];
|
||||
JSValue *onFulfilled = [JSValue valueWithObject:^() {
|
||||
[expectation fulfill];
|
||||
} inContext:context];
|
||||
|
||||
JSValue *onRejected = [JSValue valueWithObject:^(JSValue *error) {
|
||||
[self recordException:error];
|
||||
[expectation fulfill];
|
||||
} inContext:context];
|
||||
|
||||
[promise invokeMethod:@"then" withArguments:@[onFulfilled, onRejected]];
|
||||
|
||||
[self waitForExpectationsWithTimeout:5.0 handler:NULL];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)recordException:(JSValue *)exception {
|
||||
JSValue *message = [exception hasProperty:@"message"] ? exception[@"message"] : exception;
|
||||
NSString *source = [exception hasProperty:@"sourceURL"] ? [exception[@"sourceURL"] toString] : nil;
|
||||
NSUInteger line = [exception hasProperty:@"line"] ? [exception[@"line"] toUInt32] - 1 : 0;
|
||||
NSURL *sourceURL = nil;
|
||||
|
||||
if (source) {
|
||||
NSString *path = [NSString pathWithComponents:@[[@(__FILE__) stringByDeletingLastPathComponent], @"..", @"js", source.lastPathComponent]];
|
||||
sourceURL = [NSURL URLWithString:path];
|
||||
}
|
||||
|
||||
[self recordFailureWithDescription:message.description
|
||||
inFile:sourceURL ? sourceURL.absoluteString : @(__FILE__)
|
||||
atLine:sourceURL ? line : __LINE__
|
||||
expected:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
360
tests/js/async-tests.js
Normal file
360
tests/js/async-tests.js
Normal file
@ -0,0 +1,360 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* eslint-env es6, node */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Realm = require('realm');
|
||||
const TestCase = require('./asserts');
|
||||
const schemas = require('./schemas');
|
||||
const Worker = require('./worker');
|
||||
|
||||
function createNotificationTest(config, getObservable, addListener, removeListener, messages, expectedCount) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let realm = new Realm(config);
|
||||
let observable = getObservable(realm);
|
||||
let worker = new Worker(__dirname + '/worker-tests-script.js');
|
||||
|
||||
// Test will fail if it does not receive a change event within a second.
|
||||
let timer = setTimeout(() => {
|
||||
reject(new Error('Timed out waiting for change notification'));
|
||||
}, 5000);
|
||||
|
||||
let cleanup = () => {
|
||||
clearTimeout(timer);
|
||||
worker.terminate();
|
||||
};
|
||||
|
||||
var count = 0;
|
||||
var listener = addListener(observable, () => count++, resolve, reject, cleanup);
|
||||
|
||||
messages.push(['echo', 'resolve']);
|
||||
var messageIndex = 0;
|
||||
|
||||
worker.onmessage = (message) => {
|
||||
if (message.error) {
|
||||
reject(message.error);
|
||||
cleanup();
|
||||
}
|
||||
else if (message.result == 'resolve') {
|
||||
if (count != expectedCount) {
|
||||
reject('Notification count ' + count + ' not equal to expected count ' + expectedCount);
|
||||
}
|
||||
else {
|
||||
resolve();
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
else {
|
||||
if (message.result == 'removeListener') {
|
||||
removeListener(observable, listener);
|
||||
}
|
||||
worker.postMessage(messages[messageIndex++]);
|
||||
}
|
||||
};
|
||||
|
||||
worker.postMessage(messages[messageIndex++]);
|
||||
});
|
||||
};
|
||||
|
||||
function createCollectionChangeTest(config, createCollection, messages, expected, removeAll) {
|
||||
return createNotificationTest(
|
||||
config,
|
||||
createCollection,
|
||||
(collection, increment, resolve, reject, cleanup) => {
|
||||
var listener = (object, changes) => {
|
||||
try {
|
||||
var notificationCount = increment();
|
||||
TestCase.assertArraysEqual(changes.insertions, expected[notificationCount][0]);
|
||||
TestCase.assertArraysEqual(changes.deletions, expected[notificationCount][1]);
|
||||
TestCase.assertArraysEqual(changes.modifications, expected[notificationCount][2]);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
cleanup();
|
||||
}
|
||||
};
|
||||
collection.addListener(listener);
|
||||
return listener;
|
||||
},
|
||||
removeAll ? (observable) => observable.removeAllListeners() :
|
||||
(observable, listener) => observable.removeListener(listener),
|
||||
messages,
|
||||
expected.length
|
||||
);
|
||||
};
|
||||
|
||||
const ListObject = {
|
||||
name: 'ListObject',
|
||||
properties: {
|
||||
list: {type: 'list', objectType: 'TestObject'},
|
||||
}
|
||||
};
|
||||
|
||||
const PrimaryListObject = {
|
||||
name: 'PrimaryListObject',
|
||||
properties: {
|
||||
list: {type: 'list', objectType: 'IntPrimaryObject'},
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
testChangeNotifications() {
|
||||
var config = { schema: [schemas.TestObject] };
|
||||
return createNotificationTest(
|
||||
config,
|
||||
(realm) => realm,
|
||||
(realm, increment, resolve, reject, cleanup) => realm.addListener('change', () => {
|
||||
try {
|
||||
var objects = realm.objects('TestObject');
|
||||
TestCase.assertEqual(objects.length, 1);
|
||||
TestCase.assertEqual(objects[0].doubleCol, 42);
|
||||
increment();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
cleanup();
|
||||
}
|
||||
}),
|
||||
undefined,
|
||||
[[config, 'create', 'TestObject', [{doubleCol: 42}]]],
|
||||
1
|
||||
);
|
||||
},
|
||||
|
||||
testResultsAddNotifications() {
|
||||
var config = { schema: [schemas.TestObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
(realm) => realm.objects('TestObject'),
|
||||
[
|
||||
[config, 'create', 'TestObject', [{ doubleCol: 1 }]],
|
||||
[config, 'create', 'TestObject', [{ doubleCol: 2 }, { doubleCol: 3 }]]
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[0], [], []],
|
||||
[[1, 2], [], []],
|
||||
]
|
||||
);
|
||||
},
|
||||
|
||||
testResultsRemoveNotifications() {
|
||||
var config = { schema: [schemas.TestObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
(realm) => realm.objects('TestObject'),
|
||||
[
|
||||
[config, 'create', 'TestObject', [{ doubleCol: 1 }]],
|
||||
['echo', 'removeListener'],
|
||||
[config, 'create', 'TestObject', [{ doubleCol: 2 }, { doubleCol: 3 }]]
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[0], [], []],
|
||||
]
|
||||
);
|
||||
},
|
||||
|
||||
testResultsRemoveAllNotifications() {
|
||||
var config = { schema: [schemas.TestObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
(realm) => realm.objects('TestObject'),
|
||||
[
|
||||
[config, 'create', 'TestObject', [{ doubleCol: 1 }]],
|
||||
['echo', 'removeListener'],
|
||||
[config, 'create', 'TestObject', [{ doubleCol: 2 }, { doubleCol: 3 }]]
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[0], [], []],
|
||||
],
|
||||
true
|
||||
);
|
||||
},
|
||||
|
||||
testResultsDeleteNotifications() {
|
||||
var config = { schema: [schemas.TestObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
function(realm) {
|
||||
return realm.objects('TestObject');
|
||||
},
|
||||
[
|
||||
[config, 'create', 'TestObject', [[0], [1], [2], [3], [4]]],
|
||||
[config, 'delete', 'TestObject', [4]],
|
||||
[config, 'delete', 'TestObject', [0, 2]]
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[0, 1, 2, 3, 4], [], []],
|
||||
[[], [4], []],
|
||||
[[0], [0, 2, 3], []]
|
||||
]
|
||||
);
|
||||
},
|
||||
|
||||
testResultsUpdateNotifications() {
|
||||
var config = { schema: [schemas.IntPrimary] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
(realm) => realm.objects('IntPrimaryObject'),
|
||||
[
|
||||
[config, 'create', 'IntPrimaryObject', [[0, '0'], [1, '1'], [2, '2']]],
|
||||
[config, 'update', 'IntPrimaryObject', [[0, '00'], [2, '22']]]
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[0, 1, 2], [], []],
|
||||
[[], [], [0, 2]]
|
||||
]
|
||||
);
|
||||
},
|
||||
|
||||
testListAddNotifications() {
|
||||
var config = { schema: [schemas.TestObject, ListObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
function(realm) {
|
||||
let listObject;
|
||||
realm.write(() => {
|
||||
listObject = realm.create('ListObject', {list: []})
|
||||
});
|
||||
return listObject.list;
|
||||
},
|
||||
[
|
||||
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}]
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[0, 1], [], []]
|
||||
]
|
||||
);
|
||||
},
|
||||
|
||||
testListRemoveNotifications() {
|
||||
var config = { schema: [schemas.TestObject, ListObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
function(realm) {
|
||||
let listObject;
|
||||
realm.write(() => {
|
||||
listObject = realm.create('ListObject', {list: []})
|
||||
});
|
||||
return listObject.list;
|
||||
},
|
||||
[
|
||||
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}],
|
||||
['echo', 'removeListener'],
|
||||
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}],
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[0, 1], [], []]
|
||||
]
|
||||
);
|
||||
},
|
||||
|
||||
testListRemoveAllNotifications() {
|
||||
var config = { schema: [schemas.TestObject, ListObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
function(realm) {
|
||||
let listObject;
|
||||
realm.write(() => {
|
||||
listObject = realm.create('ListObject', {list: []})
|
||||
});
|
||||
return listObject.list;
|
||||
},
|
||||
[
|
||||
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}],
|
||||
['echo', 'removeListener'],
|
||||
[config, 'list_method', 'ListObject', 'list', 'push', {doubleCol: 0}, {doubleCol: 1}],
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[0, 1], [], []]
|
||||
],
|
||||
true
|
||||
);
|
||||
},
|
||||
|
||||
testListDeleteNotifications() {
|
||||
var config = { schema: [schemas.TestObject, ListObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
function(realm) {
|
||||
let listObject;
|
||||
realm.write(() => {
|
||||
listObject = realm.create('ListObject', {list: [[0], [1], [2]]})
|
||||
});
|
||||
return listObject.list;
|
||||
},
|
||||
[
|
||||
[config, 'list_method', 'ListObject', 'list', 'splice', 1, 2]
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[], [1, 2], []]
|
||||
]
|
||||
);
|
||||
},
|
||||
|
||||
testListSpliceNotifications() {
|
||||
var config = { schema: [schemas.TestObject, ListObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
function(realm) {
|
||||
let listObject;
|
||||
realm.write(() => {
|
||||
listObject = realm.create('ListObject', {list: [[0], [1], [2]]})
|
||||
});
|
||||
return listObject.list;
|
||||
},
|
||||
[
|
||||
[config, 'list_method', 'ListObject', 'list', 'splice', 1, 1, [2]]
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[1], [1], []]
|
||||
]
|
||||
);
|
||||
},
|
||||
|
||||
testListUpdateNotifications() {
|
||||
var config = { schema: [schemas.IntPrimary, PrimaryListObject] };
|
||||
return createCollectionChangeTest(
|
||||
config,
|
||||
function(realm) {
|
||||
let listObject;
|
||||
realm.write(() => {
|
||||
listObject = realm.create('PrimaryListObject', {list: [[0, '0'], [1, '1']]})
|
||||
});
|
||||
return listObject.list;
|
||||
},
|
||||
[
|
||||
[config, 'update', 'IntPrimaryObject', [[1, '11']]]
|
||||
],
|
||||
[
|
||||
[[], [], []],
|
||||
[[], [], [1]]
|
||||
]
|
||||
);
|
||||
},
|
||||
};
|
||||
|
@ -1,5 +1,8 @@
|
||||
{
|
||||
"name": "realm-tests",
|
||||
"version": "0.0.1",
|
||||
"private": true
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"es6-promise": "^3.2.1"
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ module.exports = {
|
||||
|
||||
testRealmConstructorPath: function() {
|
||||
TestCase.assertThrows(function() {
|
||||
new Realm('/invalidpath');
|
||||
new Realm('');
|
||||
}, 'Realm cannot be created with an invalid path');
|
||||
TestCase.assertThrows(function() {
|
||||
new Realm('test1.realm', 'invalidArgument');
|
||||
|
@ -23,30 +23,39 @@
|
||||
|
||||
const Realm = require('../..');
|
||||
|
||||
const handlers = {
|
||||
create(options) {
|
||||
let realm = new Realm(options.config);
|
||||
|
||||
realm.write(() => {
|
||||
realm.create(options.type, options.properties);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
process.on('message', (message) => {
|
||||
process.send(handleMessage(message));
|
||||
});
|
||||
|
||||
function handleMessage(message) {
|
||||
let error, result;
|
||||
if (message[0] == 'echo') {
|
||||
return {result: message[1]}
|
||||
}
|
||||
|
||||
try {
|
||||
let handler = handlers[message.action];
|
||||
if (handler) {
|
||||
result = handler(message);
|
||||
} else {
|
||||
throw new Error('Unknown worker action: ' + message.action);
|
||||
}
|
||||
let realm = new Realm(message[0]);
|
||||
realm.write(() => {
|
||||
if (message[1] == 'create') {
|
||||
result = message[3].map((value) => realm.create(message[2], value));
|
||||
}
|
||||
else if (message[1] == 'delete') {
|
||||
let objects = realm.objects(message[2]);
|
||||
objects = message[3].map((index) => objects[index]);
|
||||
realm.delete(objects);
|
||||
}
|
||||
else if (message[1] == 'update') {
|
||||
result = message[3].map((value) => realm.create(message[2], value, true));
|
||||
}
|
||||
else if (message[1] == 'list_method') {
|
||||
var listObject = realm.objects(message[2])[0];
|
||||
var list = listObject[message[3]];
|
||||
result = list[message[4]].apply(list, message.slice(5));
|
||||
}
|
||||
else {
|
||||
throw new Error('Unknown realm method: ' + message[1]);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
error = e.message;
|
||||
|
@ -1,76 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* eslint-env es6, node */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Realm = require('realm');
|
||||
const TestCase = require('./asserts');
|
||||
const schemas = require('./schemas');
|
||||
const Worker = require('./worker');
|
||||
|
||||
module.exports = {
|
||||
testChangeNotifications() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let config = {schema: [schemas.TestObject]};
|
||||
let realm = new Realm(config);
|
||||
let objects = realm.objects('TestObject');
|
||||
let worker = new Worker(__dirname + '/worker-tests-script.js');
|
||||
|
||||
// Test will fail if it does not receive a change event within a second.
|
||||
let timer = setTimeout(() => {
|
||||
reject(new Error('Timed out waiting for change notification'));
|
||||
}, 1000);
|
||||
|
||||
let cleanup = () => {
|
||||
clearTimeout(timer);
|
||||
worker.terminate();
|
||||
};
|
||||
|
||||
// Test will pass if it receives a change event and the Realm changed.
|
||||
realm.addListener('change', () => {
|
||||
try {
|
||||
TestCase.assertEqual(objects.length, 1);
|
||||
TestCase.assertEqual(objects[0].doubleCol, 42);
|
||||
resolve();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
worker.onmessage = (message) => {
|
||||
if (message.error) {
|
||||
cleanup();
|
||||
reject(message.error);
|
||||
}
|
||||
};
|
||||
|
||||
worker.postMessage({
|
||||
action: 'create',
|
||||
config: config,
|
||||
type: 'TestObject',
|
||||
properties: {
|
||||
doubleCol: 42,
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
@ -31,7 +31,9 @@ class Worker {
|
||||
});
|
||||
}
|
||||
postMessage(message) {
|
||||
this._process.send(message);
|
||||
if (this._process) {
|
||||
this._process.send(message);
|
||||
}
|
||||
}
|
||||
terminate() {
|
||||
if (this._process) {
|
||||
|
16
tests/package.json
Normal file
16
tests/package.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "realm-tests-jasmine",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"es6-promise": "^3.2.1",
|
||||
"jasmine": "^2.5.1",
|
||||
"jasmine-console-reporter": "^1.2.7",
|
||||
"jasmine-reporters": "^2.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jasmine spec/unit_tests.js",
|
||||
"test-sync": "jasmine spec/sync_integration_tests.js",
|
||||
"postinstall": "rm -f node_modules/realm && ln -s ../.. node_modules/realm"
|
||||
}
|
||||
}
|
@ -28,6 +28,36 @@ import {
|
||||
import React from 'react';
|
||||
import { runTests } from './tests';
|
||||
|
||||
const Realm = require('realm');
|
||||
|
||||
Realm.Sync.setLogLevel('error');
|
||||
var realm;
|
||||
Realm.Sync.User.login('http://127.0.0.1:9080/', 'ari', 'aaa', function(error, user) {
|
||||
console.log(user);
|
||||
|
||||
realm = new Realm({
|
||||
sync: {
|
||||
user: user,
|
||||
url: 'realm://127.0.0.1:9080/~/demo/realm1'
|
||||
},
|
||||
schema: [{
|
||||
name: 'IntObject',
|
||||
properties: {
|
||||
int: 'int'
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
realm.addListener('change', () => {
|
||||
console.log(realm.objects('IntObject'));
|
||||
});
|
||||
|
||||
realm.write(() => {
|
||||
realm.create('IntObject', {int: realm.objects('IntObject').length});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
class ReactTests extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
|
@ -91,6 +91,13 @@
|
||||
ReferencedContainer = "container:ReactTests.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "OS_ACTIVITY_MODE"
|
||||
value = "disable"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
|
@ -6,11 +6,11 @@
|
||||
"start": "react-native start"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "~15.2.0",
|
||||
"react-native": "^0.31.0",
|
||||
"react": "~15.3.1",
|
||||
"react-native": "^0.34.0",
|
||||
"react-native-fs": "^1.1.0",
|
||||
"xmlbuilder": "^4.2.1",
|
||||
"realm": "file:../..",
|
||||
"realm-tests": "file:../js"
|
||||
"realm-tests": "file:../js",
|
||||
"xmlbuilder": "^4.2.1"
|
||||
}
|
||||
}
|
||||
|
9
tests/spec/helpers/reporters.js
Normal file
9
tests/spec/helpers/reporters.js
Normal file
@ -0,0 +1,9 @@
|
||||
var jasmineReporters = require('jasmine-reporters');
|
||||
var junitReporter = new jasmineReporters.JUnitXmlReporter({
|
||||
savePath: '.',
|
||||
consolidateAll: false
|
||||
});
|
||||
jasmine.getEnv().addReporter(junitReporter);
|
||||
|
||||
var JasmineConsoleReporter = require('jasmine-console-reporter');
|
||||
jasmine.getEnv().addReporter(new JasmineConsoleReporter());
|
11
tests/spec/support/jasmine.json
Normal file
11
tests/spec/support/jasmine.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"spec_dir": "spec",
|
||||
"spec_files": [
|
||||
"**/*[sS]pec.js"
|
||||
],
|
||||
"helpers": [
|
||||
"helpers/**/*.js"
|
||||
],
|
||||
"stopSpecOnExpectationFailure": false,
|
||||
"random": false
|
||||
}
|
99
tests/spec/sync_integration_tests.js
Normal file
99
tests/spec/sync_integration_tests.js
Normal file
@ -0,0 +1,99 @@
|
||||
"use strict";
|
||||
|
||||
const spawn = require("child_process").spawn;
|
||||
const readline = require("readline");
|
||||
const fs = require("fs");
|
||||
const Realm = require("realm");
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
||||
|
||||
beforeEach(function(done) {
|
||||
this.objectServer = spawn("sync-bundle/start-object-server.command");
|
||||
this.objectServer.once("close", (code) => {
|
||||
if (typeof code === "number" && code != 0) {
|
||||
console.error(`Object Server exited with code ${code}`);
|
||||
process.exit(-1);
|
||||
}
|
||||
});
|
||||
|
||||
this.rl = readline.createInterface({ input: this.objectServer.stdout });
|
||||
this.rl.on("line", (line) => {
|
||||
var match;
|
||||
if ((match = line.match(/Connection\[1\]: Session\[1\]: Received: BIND\(server_path='\/(.+)',/))) {
|
||||
var adminUser = Realm.Sync.User.adminUser('http://127.0.0.1:9080/',
|
||||
fs.readFileSync("sync-bundle/admin_token.base64", "utf-8"));
|
||||
this.adminRealmPath = match[1];
|
||||
this.adminRealm = new Realm({
|
||||
path: "__admin.realm",
|
||||
sync: {
|
||||
user: adminUser,
|
||||
url: `realm://127.0.0.1:9080/${this.adminRealmPath}`
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
name: "RealmFile",
|
||||
properties: {
|
||||
id: 'string',
|
||||
path: 'string'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function(done) {
|
||||
this.rl.close();
|
||||
this.objectServer.kill('SIGKILL');
|
||||
this.adminRealm.close();
|
||||
|
||||
let reset = spawn("sync-bundle/reset-server-realms.command");
|
||||
reset.once("close", done);
|
||||
reset.stdin.write("yes\n");
|
||||
|
||||
Realm.clearTestState();
|
||||
});
|
||||
|
||||
describe("Sync", function() {
|
||||
it("should work", function(done) {
|
||||
Realm.Sync.User.create('http://127.0.0.1:9080/', 'foo', 'bar', function(error) {
|
||||
if (error) {
|
||||
fail(error);
|
||||
return;
|
||||
}
|
||||
|
||||
Realm.Sync.User.login('http://127.0.0.1:9080/', 'foo', 'bar', function(error, user) {
|
||||
if (error) {
|
||||
fail(error);
|
||||
return;
|
||||
}
|
||||
|
||||
var realm = new Realm({
|
||||
syncConfig: {
|
||||
identity: user.identity,
|
||||
url: 'realm://127.0.0.1:9080/~/demo/realm1'
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
name: 'IntObject',
|
||||
properties: {
|
||||
int: 'int'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var realms = this.adminRealm.objects("RealmFile");
|
||||
realms.addListener((sender, changeset) => {
|
||||
if (changeset.insertions.length === 1) {
|
||||
expect(realms[changeset.insertions[0]].path).toMatch(/demo\/realm1$/);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
79
tests/spec/unit_tests.js
Normal file
79
tests/spec/unit_tests.js
Normal file
@ -0,0 +1,79 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright 2016 Realm Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* eslint-env es6, node */
|
||||
/* eslint-disable no-console */
|
||||
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const Realm = require('../..');
|
||||
const RealmTests = require('../js');
|
||||
|
||||
// Create this method with appropriate implementation for Node testing.
|
||||
Realm.copyBundledRealmFiles = function() {
|
||||
let sourceDir = path.join(__dirname, '../data');
|
||||
let destinationDir = path.dirname(Realm.defaultPath);
|
||||
|
||||
for (let filename of fs.readdirSync(sourceDir)) {
|
||||
let src = path.join(sourceDir, filename);
|
||||
let dest = path.join(destinationDir, filename);
|
||||
|
||||
// If the destination file already exists, then don't overwrite it.
|
||||
try {
|
||||
fs.accessSync(dest);
|
||||
continue;
|
||||
} catch (e) {}
|
||||
|
||||
fs.writeFileSync(dest, fs.readFileSync(src));
|
||||
}
|
||||
};
|
||||
|
||||
const tests = RealmTests.getTestNames();
|
||||
for (const suiteName in tests) {
|
||||
describe(suiteName, () => {
|
||||
beforeEach(() => RealmTests.runTest(suiteName, 'beforeEach'));
|
||||
|
||||
for (const testName of tests[suiteName]) {
|
||||
it(testName, () => {
|
||||
try {
|
||||
RealmTests.runTest(suiteName, testName)
|
||||
}
|
||||
catch (e) {
|
||||
fail(e);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(() => RealmTests.runTest(suiteName, 'afterEach'));
|
||||
});
|
||||
}
|
||||
|
||||
const asyncTests = require('../js/async-tests');
|
||||
describe('AsyncTests', () => {
|
||||
beforeEach(() => Realm.clearTestState());
|
||||
|
||||
for (const testName in asyncTests) {
|
||||
it(testName, (done) => asyncTests[testName]().catch((e) => fail(e)).then(done));
|
||||
}
|
||||
|
||||
afterEach(() => Realm.clearTestState());
|
||||
});
|
1
vendor/.gitignore
vendored
Normal file
1
vendor/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/realm/
|
Loading…
x
Reference in New Issue
Block a user