diff --git a/CHANGELOG.md b/CHANGELOG.md index e8018f01..ad70d254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ -vnext +1.3.1 Release notes (2017-5-18) ============================================================= ### Breaking changes * None ### Enhancements -* None +* Add Realm open async API support. ### Bug fixes * None @@ -19,7 +19,7 @@ Old files can still be opened and files open in read-only mode will not be modif * The SyncConfig now gets two more optional parameters, `validate_ssl` and `ssl_trust_certificate_path`. ### Enhancements -* Add Realm open async API support. +* None ### Bug fixes * None diff --git a/dependencies.list b/dependencies.list index 2d6fdca0..51f17dbe 100644 --- a/dependencies.list +++ b/dependencies.list @@ -1,5 +1,5 @@ PACKAGE_NAME=realm-js -VERSION=1.3.0 +VERSION=1.3.1 REALM_CORE_VERSION=2.7.0 REALM_SYNC_VERSION=1.8.4 REALM_OBJECT_SERVER_VERSION=1.3.0 diff --git a/docs/realm.js b/docs/realm.js index 7964fcbe..7d7f97ba 100644 --- a/docs/realm.js +++ b/docs/realm.js @@ -75,6 +75,23 @@ class Realm { */ constructor(config) {} + /** + * Open a realm asynchronously with a promise. If the realm is synced, it will be fully + * synchronized before it is available. + * @param {Realm~Configuration} config + * @returns {Promise} - a promise that will be resolved with the realm instance when it's available. + */ + static open(config) {} + + /** + * Open a realm asynchronously with a callback. If the realm is synced, it will be fully + * synchronized before it is available. + * @param {Realm~Configuration} config + * @param {function(error, realm)} callback - will be called when the realm is ready. + * @throws {Error} If anything in the provided `config` is invalid. + */ + static openAsync(config, callback) {} + /** * Closes this Realm so it may be re-opened with a newer schema version. * All objects and collections from this Realm are no longer valid after calling this method. diff --git a/docs/sync.js b/docs/sync.js index 18559058..ed6006f1 100644 --- a/docs/sync.js +++ b/docs/sync.js @@ -264,3 +264,104 @@ class Session { */ get state() {} } + + +/** + * Class for creating custom Data Connectors. Only available in the Enterprise Edition. + * @memberof Realm.Sync + */ +class Adapter { + /** + * Create a new Adapter to moitor and process changes made across multiple Realms + * @param {string} local_path - the local path where realm files are stored + * @param {string} server_url - the sync server to listen to + * @param {SyncUser} admin_user - an admin user obtained by calling `new Realm.Sync.User.adminUser` + * @param {string} regex - a regular expression used to determine which cahnged Realms should be monitored - + * use `.*` to match all all Realms + * @param {function(realm_path)} change_callback - called when a new transaction is available + * to process for the given realm_path + */ + constructor(local_path, server_url, admin_user, regex, change_callback) {} + + /** + * Get the Array of current instructions for the given Realm. + * @param {string} path - the path for the Realm being monitored + * @returns {Array(instructions)} or {undefined} if all transactions have been processed + */ + current(path) {} + + /** + * Advance the to the next transaction indicating that you are done processing the current + * instructions for the given Realm. + * @param {string} path - the path for the Realm to advance + */ + advance(path) {} + + /** + * Open the Realm used by the Adapter for the given path. This is useful for writing two way + * adapters as transactions written to this realm will be ignored when calling `current` and `advance` + * @param {string} path - the path for the Realm to open + * @returns {Realm} + */ + realmAtPath(path) {} + + /** + * Close the adapter and all opened Realms. + */ + close() {} +} + +/** + * The following Instructions can be returned by `Adapter.current(path)`. Each instruction object has + * a `type` property which is one of the following types. For each type below we list the other properties + * that will exist in the instruction object. + * @typedef Realm.Sync.Adapter~Instruction + * @type {(INSERT|SET|DELETE|CLEAR|CHANGE_IDENTITY|LIST_SET|LIST_INSERT|LIST_ERASE|LIST_CLEAR|ADD_TYPE|ADD_PROPERTY)} + * @property INSERT - insert a new object + * - `object_type` - type of the object being inserted + * - `identity` - primary key value or row index for the object + * - `values` - map of property names and property values for the object to insert + * @property SET - set property values for an existing object + * - `object_type` - type of the object + * - `identity` - primary key value or row index for the object + * - `values` - map of property names and property values to update for the object + * @property DELETE - delete an exising object + * - `object_type` - type of the object + * - `identity` - primary key value or row index for the object + * @property CLEAR - delete all objects of a given type + * - `object_type` - type of the object + * @property LIST_SET - set the object at a given list index to an object + * - `object_type` - type of the object + * - `identity` - primary key for the object + * - `property` - property name for the list property to mutate + * - `list_index` - list index to set + * - `object_identity` - primary key or row number of the object being set + * @property LIST_INSERT - insert an object in the list at the given index + * - `object_type` - type of the object + * - `identity` - primary key for the object + * - `property` - property name for the list property to mutate + * - `list_index` - list index at which to insert + * - `object_identity` - primary key or row number of the object to insert + * @property LIST_ERASE - erase an object in the list at the given index - this removes the object + * from the list but the object will still exist in the Realm + * - `object_type` - type of the object + * - `identity` - primary key for the object + * - `property` - property name for the list property to mutate + * - `list_index` - list index which should be erased + * @property LIST_CLEAR - clear a list removing all objects - objects are not deleted from the Realm + * - `object_type` - type of the object + * - `identity` - primary key for the object + * - `property` - property name for the list property to clear + * @property ADD_TYPE - add a new type + * - `object_type` - name of the type + * - `primary_key` - name of primary key property for this type + * - `properties` - Property map as described in {@link Realm~ObjectSchema} + * @property ADD_PROPERTIES - add properties to an existing type + * - `object_type` - name of the type + * - `properties` - Property map as described in {@link Realm~ObjectSchema} + * @property CHANGE_IDENTITY - change the row index for an existing object - not called for objects + * with primary keys + * - `object_type` - type fo the object + * - `identity` - old row value for the object + * - `new_identity` - new row value for the object + */ diff --git a/lib/browser/index.js b/lib/browser/index.js index efe9957b..e66ab2b9 100644 --- a/lib/browser/index.js +++ b/lib/browser/index.js @@ -175,6 +175,11 @@ Object.defineProperties(Realm, { rpc.clearTestState(); }, }, + _waitForDownload: { + value: function(_config, callback) { + callback(); + } + }, }); for (let i = 0, len = debugHosts.length; i < len; i++) { diff --git a/package.json b/package.json index 90c1571a..266a29c6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "realm", "description": "Realm is a mobile database: an alternative to SQLite and key-value stores", - "version": "1.3.0", + "version": "1.3.1", "license": "Apache-2.0", "homepage": "https://realm.io", "keywords": [ @@ -46,9 +46,7 @@ "scripts": { "get-version": "node -p process.env.npm_package_version", "set-version": "scripts/set-version.sh", - "get-core-version": "env-cmd ./dependencies.list node -p process.env.REALM_CORE_VERSION", - "get-sync-version": "env-cmd ./dependencies.list node -p process.env.REALM_SYNC_VERSION", - "lint": "eslint", + "lint": "eslint", "test": "scripts/test.sh", "install": "node-pre-gyp install --fallback-to-build", "build-changes": "node-pre-gyp build --fallback-to-build --debug --build-from-source", @@ -89,7 +87,6 @@ }, "devDependencies": { "babel-eslint": "^6.0.4", - "env-cmd": "^5.0.0", "eslint": "^3.2.2", "eslint-plugin-jasmine": "^2.1.0", "eslint-plugin-react": "^6.7.0", diff --git a/src/RealmJS.xcodeproj/project.pbxproj b/src/RealmJS.xcodeproj/project.pbxproj index 4a4d3d2e..c658e426 100644 --- a/src/RealmJS.xcodeproj/project.pbxproj +++ b/src/RealmJS.xcodeproj/project.pbxproj @@ -956,7 +956,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1.3.0; + CURRENT_PROJECT_VERSION = 1.3.1; CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1020,7 +1020,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1.3.0; + CURRENT_PROJECT_VERSION = 1.3.1; CXX = "$(SRCROOT)/../scripts/ccache-clang++.sh"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; diff --git a/tests/js/session-tests.js b/tests/js/session-tests.js index 08a5fbd7..e36cfe3f 100644 --- a/tests/js/session-tests.js +++ b/tests/js/session-tests.js @@ -234,6 +234,134 @@ module.exports = { }); }, + testRealmOpenAsyncNoSchema() { + if (!isNodeProccess) { + return Promise.resolve(); + } + + const username = uuid(); + const realmName = uuid(); + const expectedObjectsCount = 3; + + let tmpDir = tmp.dirSync(); + let content = fs.readFileSync(__dirname + '/download-api-helper.js', 'utf8'); + let tmpFile = tmp.fileSync({ dir: tmpDir.name }); + fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' }); + + return new Promise((resolve, reject) => { + //execute download-api-helper which inserts predefined number of objects into the synced realm. + const child = execFile('node', [tmpFile.name, username, realmName, REALM_MODULE_PATH], { cwd: tmpDir.name }, (error, stdout, stderr) => { + if (error) { + reject(new Error('Error executing download api helper' + error)); + } + resolve(); + }); + }) + .then(() => { + return promisifiedLogin('http://localhost:9080', username, 'password').then(user => { + return new Promise((resolve, reject) => { + const accessTokenRefreshed = this; + let successCounter = 0; + + let config = { + sync: { user, url: `realm://localhost:9080/~/${realmName}` } + }; + + Realm.openAsync(config, (error, realm) => { + try { + if (error) { + reject(error); + } + + let actualObjectsCount = realm.objects('Dog').length; + TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count"); + + let firstDog = realm.objects('Dog')[0]; + TestCase.assertTrue(({}).hasOwnProperty.call(firstDog, 'name'), "Synced realm does not have an inffered schema"); + TestCase.assertTrue(firstDog.name, "Synced realm object's property should have a value"); + TestCase.assertTrue(firstDog.name.indexOf('Lassy') !== -1, "Synced realm object's property should contain the actual written value"); + + + const session = realm.syncSession; + TestCase.assertInstanceOf(session, Realm.Sync.Session); + TestCase.assertEqual(session.user.identity, user.identity); + TestCase.assertEqual(session.config.url, config.sync.url); + TestCase.assertEqual(session.config.user.identity, config.sync.user.identity); + TestCase.assertEqual(session.state, 'active'); + resolve(); + } + catch (e) { + reject(e); + } + }); + }); + }); + }); + }, + + testRealmOpenLocalRealm() { + const username = uuid(); + const expectedObjectsCount = 3; + + + return new Promise((resolve, reject) => { + const accessTokenRefreshed = this; + let successCounter = 0; + + let config = { + schema: [{ name: 'Dog', properties: { name: 'string' } }], + }; + + Realm.open(config).then(realm => { + realm.write(() => { + for (let i = 1; i <= 3; i++) { + realm.create('Dog', { name: `Lassy ${i}` }); + } + }); + + let actualObjectsCount = realm.objects('Dog').length; + TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Local realm does not contain the expected objects count"); + resolve(); + }).catch(error => reject(error)); + }); + }, + + testRealmOpenAsyncLocalRealm() { + const username = uuid(); + const expectedObjectsCount = 3; + + + return new Promise((resolve, reject) => { + const accessTokenRefreshed = this; + let successCounter = 0; + + let config = { + schema: [{ name: 'Dog', properties: { name: 'string' } }], + }; + + Realm.openAsync(config, (error, realm) => { + try { + if (error) { + reject(error); + } + + realm.write(() => { + for (let i = 1; i <= 3; i++) { + realm.create('Dog', { name: `Lassy ${i}` }); + } + }); + + let actualObjectsCount = realm.objects('Dog').length; + TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Local realm does not contain the expected objects count"); + resolve(); + } + catch (e) { + reject(e); + } + }); + }); + }, + testErrorHandling() { return promisifiedRegister('http://localhost:9080', uuid(), 'password').then(user => { return new Promise((resolve, _reject) => {