Query-based sync as the default sync mode (#1830)

This commit is contained in:
Christian Melchior 2018-05-30 12:54:51 +02:00 committed by GitHub
parent 5c035fbdce
commit 4cb9c77f46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 193 additions and 37 deletions

View File

@ -1,3 +1,29 @@
2.8.0 Release notes (YYYY-MM-DD)
=============================================================
### Compatibility
* Sync protocol: 24
* Server-side history format: 4
* File format: 7
* Realm Object Server: 3.0.0 or later
The feature known as Partial synchronization has been renamed to Query-based synchronization and is now the default mode
for synchronized Realms. This has impacted a number of APIs. See below for the details.
### Deprecated
* [Sync] `Realm.Configuration.SyncConfiguration.partial` has been deprecated in favor of `Realm.Configuration.SyncConfiguration.fullSynchronization`.
* [Sync] `Realm.automaticSyncConfiguration()` has been deprecated in favor of `Realm.Sync.User.createConfiguration()`
### Enhancements
* [Sync] `Realm.Configuration.SyncConfiguration.fullSynchronization` has been added.
* [Sync] `Realm.Sync.User.createConfiguration(config)` has been added for creating default and user defined sync configurations.
### Internal
* [Sync] `Realm.Configuration.SyncConfig._disablePartialSyncUrlChecks` has been renamed to `Realm.Configuration.sync._disableQueryBasedSyncUrlChecks`.
2.7.0 Release notes (2018-5-29)
=============================================================
### Compatibility

View File

@ -83,6 +83,13 @@ npm install --build-from-source=realm
- Check [node-gyp](https://github.com/nodejs/node-gyp) manual for custom installation procedure for Windows
### Building docs:
API documentation is written using [JSDoc](http://usejsdoc.org/).
- `npm run jsdoc`
The generated docs can be found by opening `docs/output/realm/<version>/index.html`.
## Debugging the node addon
You can use (Visual Studio Code)[https://code.visualstudio.com/] to develop and debug. In the `.vscode` folder, configuration for building and debugging has been added for your convience.

@ -1 +1 @@
Subproject commit 682e21fe456b39169ff2d4f3f7ffc24d04cb84fd
Subproject commit 441d8a45eb39d454ea612251bf2adb60473ede70

View File

@ -128,6 +128,7 @@ class Realm {
* @throws {Error} if zero or multiple users are logged in
* @returns {Realm~Configuration} - a configuration matching a default synced Realm.
* @since 2.3.0
* @deprecated use {@link Sync.User.createConfiguration()} instead.
*/
static automaticSyncConfiguration(user) {}

View File

@ -35,10 +35,14 @@
* accept or reject the server's SSL certificate.
*
* @property {Realm.Sync~SSLConfiguration} [ssl] - SSL configuration.
* @property {boolean} [partial] - Whether this Realm should be opened in 'partial synchronization' mode.
* Partial synchronisation only synchronizes those objects that match the query specified in contrast
* @deprecated
* @property {boolean} [partial] - Whether this Realm should be opened in 'query-based synchronization' mode.
* Query-based synchronisation only synchronizes those objects that match the query specified in contrast
* to the normal mode of operation that synchronises all objects in a remote Realm.
* **Partial synchronization is a tech preview. Its APIs are subject to change.**
* @property {boolean} [fullSynchronization] - Whether this Realm should be opened in query-based or full
* synchronization mode. The default is query-based mode which only synchronizes objects that have been subscribed to.
* A fully synchronized Realm will synchronize the entire Realm in the background, irrespectively of the data being
* used or not.
*/
/**
@ -438,6 +442,15 @@ class User {
*/
get isAdminToken() {}
/**
* Creates the configuration object required to open a synchronized Realm.
*
* @param {Realm.PartialConfiguration} config - optional parameters that should override any default settings.
* @returns {Realm.Configuration} the full Realm configuration
* @since 3.0.0
*/
createConfiguration(config) {}
/**
* Logs out the user from the Realm Object Server.
*/

View File

@ -181,7 +181,6 @@ module.exports = function(realmConstructor) {
sync: {
user: user,
url: realmUrl,
partial: true
}
};
return config;

16
lib/index.d.ts vendored
View File

@ -83,11 +83,18 @@ declare namespace Realm {
inMemory?: boolean;
schema?: (ObjectClass | ObjectSchema)[];
schemaVersion?: number;
sync?: Realm.Sync.SyncConfiguration;
sync?: Partial<Realm.Sync.SyncConfiguration>;
deleteRealmIfMigrationNeeded?: boolean;
disableFormatUpgrade?: boolean;
}
/**
* realm configuration used for overriding default configuration values.
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.html#~Configuration }
*/
interface PartialConfiguration extends Partial<Realm.Configuration> {
}
// object props type
interface ObjectPropsType {
[keys: string]: any;
@ -314,6 +321,7 @@ declare namespace Realm.Sync {
static confirmEmail(server:string, confirmation_token:string): Promise<void>;
createConfiguration(config?: Realm.PartialConfiguration): Realm.Configuration
authenticate(server: string, provider: string, options: any): Promise<Realm.Sync.User>;
logout(): void;
openManagementRealm(): Realm;
@ -415,7 +423,8 @@ declare namespace Realm.Sync {
ssl?: SSLConfiguration;
error?: ErrorCallback;
partial?: boolean;
_disablePartialSyncUrlChecks?:boolean;
fullSynchronization?: boolean;
_disableQueryBasedSyncUrlChecks?:boolean;
}
type ProgressNotificationCallback = (transferred: number, transferable: number) => void;
@ -588,8 +597,6 @@ declare class Realm {
*/
static schemaVersion(path: string, encryptionKey?: ArrayBuffer | ArrayBufferView): number;
/**
* Open a realm asynchronously with a promise. If the realm is synced, it will be fully synchronized before it is available.
* @param {Configuration} config
@ -605,6 +612,7 @@ declare class Realm {
static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void, progressCallback?: Realm.Sync.ProgressNotificationCallback): void
/**
* @deprecated in favor of `Realm.Sync.User.createConfiguration()`.
* Return a configuration for a default Realm.
* @param {Realm.Sync.User} optional user.
*/

View File

@ -68,7 +68,8 @@ function getSpecialPurposeRealm(user, realmName, schema) {
schema: schema,
sync: {
user,
url: url.href
url: url.href,
fullSynchronization: true
}
};

View File

@ -21,7 +21,9 @@
const AuthError = require('./errors').AuthError;
const permissionApis = require('./permission-api');
const merge = require('deepmerge');
const require_method = require;
const URL = require('url-parse');
function node_require(module) {
return require_method(module);
@ -520,6 +522,42 @@ const instanceMethods = {
}
});
},
createConfiguration(config) {
if (config && config.sync) {
if (console.warn !== undefined) {
console.warn(`'user' property will be overridden by ${this.identity}`);
}
if (config.sync.partial !== undefined && config.sync.fullSynchronization !== undefined) {
throw new Error("'partial' and 'fullSynchronization' were both set. 'partial' has been deprecated, use only 'fullSynchronization'");
}
}
// Create default config
let url = new URL(this.server);
let secure = (url.protocol === 'https:')?'s':'';
let port = (url.port === undefined)?'9080':url.port;
let realmUrl = `realm${secure}://${url.hostname}:${port}/default`;
let defaultConfig = {
sync: {
user: this,
url: realmUrl,
}
};
// Set query-based as the default setting if the user doesn't specified any other behaviour.
if (!(config && config.sync && config.sync.partial)) {
defaultConfig.sync.fullSynchronization = false;
}
// Merge default configuration with user provided config. User defined properties should aways win.
// Doing the naive merge in JS break objects that are backed by native objects, so these needs to
// be merged manually. This is currently only `sync.user`.
let mergedConfig = (config === undefined) ? defaultConfig : merge(defaultConfig, config);
mergedConfig.sync.user = this;
return mergedConfig;
},
};
// Append the permission apis

View File

@ -80,6 +80,7 @@
"dependencies": {
"command-line-args": "^4.0.6",
"decompress": "^4.2.0",
"deepmerge": "2.1.0",
"fs-extra": "^4.0.2",
"ini": "^1.3.4",
"nan": "2.8.0",

View File

@ -833,19 +833,28 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
ssl_verify_callback = std::move(ssl_verify_functor);
}
bool is_partial = false;
bool is_partial = false; // Change to `true` when `partial` is removed
ValueType full_synchronization_value = Object::get_property(ctx, sync_config_object, "fullSynchronization");
ValueType partial_value = Object::get_property(ctx, sync_config_object, "partial");
// Disallow setting `partial` and `fullSynchronization` at the same time
if (!Value::is_undefined(ctx, full_synchronization_value) && !Value::is_undefined(ctx, partial_value)) {
throw std::invalid_argument("'partial' and 'fullSynchronization' were both set. 'partial' has been deprecated, use only 'fullSynchronization'");
}
if (!Value::is_undefined(ctx, partial_value)) {
is_partial = Value::validated_to_boolean(ctx, partial_value);
} else if (!Value::is_undefined(ctx, full_synchronization_value)) {
is_partial = !Value::validated_to_boolean(ctx, full_synchronization_value);
}
bool disable_partial_sync_url_checks = false;
ValueType disable_partial_sync_url_checks_value = Object::get_property(ctx, sync_config_object, "_disablePartialSyncUrlChecks");
if (!Value::is_undefined(ctx, disable_partial_sync_url_checks_value)) {
disable_partial_sync_url_checks = Value::validated_to_boolean(ctx, disable_partial_sync_url_checks_value);
bool disable_query_based_sync_url_checks = false;
ValueType disable_query_based_sync_url_checks_value = Object::get_property(ctx, sync_config_object, "_disableQueryBasedSyncUrlChecks");
if (!Value::is_undefined(ctx, disable_query_based_sync_url_checks_value)) {
disable_query_based_sync_url_checks = Value::validated_to_boolean(ctx, disable_query_based_sync_url_checks_value);
}
if (disable_partial_sync_url_checks) {
if (disable_query_based_sync_url_checks) {
config.sync_config = std::make_shared<SyncConfig>(shared_user, std::move(""));
config.sync_config->reference_realm_url = std::move(raw_realm_url);
}

View File

@ -49,12 +49,12 @@ module.exports = {
}
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => {
const config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' },
const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/myrealm' },
schema: [{ name: 'IntegerPrimaryKey', properties: { int: 'int?' }, primaryKey: 'int' },
{ name: 'StringPrimaryKey', properties: { string: 'string?' }, primaryKey: 'string' },
{ name: 'NoPrimaryKey', properties: { string: 'string' }},
],
};
});
return Realm.open(config).then(realm => {
var integer, nullInteger;
var string, nullString;

View File

@ -32,7 +32,7 @@ function createObjects(user) {
sync: {
user,
url: `realm://localhost:9080/default`,
partial: true,
fullSynchronization: false,
error: err => console.log('partial-sync-api-helper', err)
},
schema: [{ name: 'Dog', properties: { name: 'string' } }]

View File

@ -33,7 +33,7 @@ function createUsersWithTestRealms(count) {
return Realm.Sync.User
.register('http://localhost:9080', uuid(), 'password')
.then(user => {
new Realm({sync: {user, url: 'realm://localhost:9080/~/test'}}).close();
new Realm({sync: {user, url: 'realm://localhost:9080/~/test', fullSynchronization: true }}).close();
return user;
});
};
@ -185,7 +185,7 @@ module.exports = {
}
}
],
sync: {user: user, url: url, partial: true}
sync: {user: user, url: url, fullSynchronization: false }
};
};
let owner, otherUser
@ -193,7 +193,7 @@ module.exports = {
.register('http://localhost:9080', uuid(), 'password')
.then(user => {
owner = user;
new Realm({sync: {user, url: 'realm://localhost:9080/default', partial: true}}).close();
new Realm({sync: {user, url: 'realm://localhost:9080/default'}}).close();
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password')
})
.then((user) => {

View File

@ -116,7 +116,7 @@ module.exports = {
// Let the error handler trigger our checks when the access token was refreshed.
postTokenRefreshChecks._notifyOnAccessTokenRefreshed = accessTokenRefreshed;
const config = { sync: { user, url: 'realm://localhost:9080/~/myrealm', error: postTokenRefreshChecks } };
const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/myrealm', error: postTokenRefreshChecks, fullSynchronization: true } });
const realm = new Realm(config);
const session = realm.syncSession;
TestCase.assertInstanceOf(session, Realm.Sync.Session);
@ -148,7 +148,7 @@ module.exports = {
let successCounter = 0;
config = {
sync: { user, url: `realm://localhost:9080/~/${realmName}` },
sync: { user, url: `realm://localhost:9080/~/${realmName}`, fullSynchronization: true },
schema: [{ name: 'Dog', properties: { name: 'string' } }],
};
@ -184,7 +184,7 @@ module.exports = {
let successCounter = 0;
config = {
sync: { user, url: `realm://localhost:9080/~/${realmName}` },
sync: { user, url: `realm://localhost:9080/~/${realmName}`, fullSynchronization: true },
schema: [{ name: 'Dog', properties: { name: 'string' } }],
schemaVersion: 1,
};
@ -226,7 +226,7 @@ module.exports = {
let successCounter = 0;
let config = {
sync: { user, url: `realm://localhost:9080/~/${realmName}` },
sync: { user, url: `realm://localhost:9080/~/${realmName}`, fullSynchronization: true },
schema: [{ name: 'Dog', properties: { name: 'string' } }],
};
return new Promise((resolve, reject) => {
@ -277,7 +277,7 @@ module.exports = {
let successCounter = 0;
let config = {
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
sync: { user, url: `realm://localhost:9080/~/${realmName}`, fullSynchronization: true }
};
return new Promise((resolve, reject) => {
Realm.openAsync(config, (error, realm) => {
@ -372,7 +372,7 @@ module.exports = {
testErrorHandling() {
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => {
return new Promise((resolve, _reject) => {
const config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/myrealm' } });
config.sync.error = (sender, error) => {
try {
TestCase.assertEqual(error.message, 'simulated error');
@ -405,7 +405,7 @@ module.exports = {
.then(user => {
let config = {
schema: [schemas.ParentObject, schemas.NameObject],
sync: { user, url: `realm://localhost:9080/~/${realmName}` }
sync: { user, url: `realm://localhost:9080/~/${realmName}`, fullSynchronization: true }
};
return Realm.open(config)
}).then(realm => {
@ -744,8 +744,8 @@ module.exports = {
sync: {
user: user,
url: `realm://localhost:9080/default/__partial/`,
partial: true,
_disablePartialSyncUrlChecks: true
_disableQueryBasedSyncUrlChecks: true,
fullSynchronization: false,
}
};
const realm = new Realm(config1);
@ -758,7 +758,7 @@ module.exports = {
sync: {
user: user,
url: `realm://localhost:9080/default/__partial/`, // <--- not allowed URL
partial: true,
fullSynchronization: false,
}
};
TestCase.assertThrows(() => new Realm(config2));
@ -769,7 +769,7 @@ module.exports = {
sync: {
user: user,
url: 'realm://localhost:9080/~/default',
partial: false, // <---- calling subscribe should fail
fullSynchronization: true, // <---- calling subscribe should fail
error: (session, error) => console.log(error)
},
schema: [{ name: 'Dog', properties: { name: 'string' } }]
@ -798,7 +798,7 @@ module.exports = {
defaultRealmInvalidArguments();
return new Promise((resolve, reject) => {
let config = Realm.automaticSyncConfiguration();
let config = Realm.Sync.User.current.createConfiguration();
config.schema = [{ name: 'Dog', properties: { name: 'string' } }];
Realm.deleteFile(config);
@ -874,7 +874,7 @@ module.exports = {
return Realm.Sync.User.register('http://localhost:9080', uuid(), 'password').then(user => {
return new Promise((resolve, _reject) => {
var realm;
const config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/myrealm' } });
config.sync.error = (sender, error) => {
try {
TestCase.assertEqual(error.name, 'ClientReset');

View File

@ -130,7 +130,8 @@ module.exports = {
}).then((user => {
assertIsUser(user);
// Can we open a realm with the logged-in user?
const realm = new Realm({ sync: { user: user, url: 'realm://localhost:9080/~/test' } });
const config = user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/test' }});
const realm = new Realm(config);
TestCase.assertInstanceOf(realm, Realm);
realm.close();
}))
@ -143,7 +144,7 @@ module.exports = {
return Realm.Sync.User.authenticate('http://localhost:9080', 'password', { username: username, password: 'password' });
}).then((user => {
assertIsUser(user);
const realm = new Realm({ sync: { user: user, url: 'realm://localhost:9080/~/test' } });
const realm = new Realm(user.createConfiguration({ sync: { url: 'realm://localhost:9080/~/test' } }));
TestCase.assertInstanceOf(realm, Realm);
realm.close();
}))
@ -318,6 +319,58 @@ module.exports = {
}).then(account => { if (account) { throw new Error("Retrieving nonexistent account should fail"); }});
},
testCreateConfiguration_defaultConfig() {
const username = uuid();
return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => {
let config = user.createConfiguration();
TestCase.assertEqual(config.sync.url, "realm://localhost:9080/default");
TestCase.assertUndefined(config.sync.partial);
TestCase.assertFalse(config.sync.fullSynchronization);
});
},
testCreateConfiguration_useOldConfiguration() {
const username = uuid();
return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => {
let config = user.createConfiguration({ sync: { url: 'http://localhost:9080/other_realm', partial: true }});
TestCase.assertEqual(config.sync.url, 'http://localhost:9080/other_realm');
TestCase.assertUndefined(config.sync.fullSynchronization);
TestCase.assertTrue(config.sync.partial);
});
},
testCreateConfiguration_settingPartialAndFullSynchronizationThrows() {
const username = uuid();
return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => {
TestCase.assertThrowsContaining(() => {
let config = {
sync: {
url: 'http://localhost:9080/~/default',
partial: true,
fullSynchronization: false
}
};
user.createConfiguration(config);
}, "'partial' and 'fullSynchronization' were both set. 'partial' has been deprecated, use only 'fullSynchronization'");
});
},
testOpen_partialAndFullSynchronizationSetThrows() {
const username = uuid();
return Realm.Sync.User.register('http://localhost:9080', username, 'password').then((user) => {
TestCase.assertThrowsContaining(() => {
new Realm({
sync: {
user: user,
url: 'http://localhost:9080/~/default',
partial: false,
fullSynchronization: true
}
})
}, "'partial' and 'fullSynchronization' were both set. 'partial' has been deprecated, use only 'fullSynchronization'");
});
}
/* This test fails because of realm-object-store #243 . We should use 2 users.
testSynchronizeChangesWithTwoClientsAndOneUser() {
// Test Schema