BUG FIX: A schema is required when Realm is opened in a query-based sync situation (#1985)

This commit is contained in:
Kenneth Geisshirt 2018-08-24 08:50:27 +02:00 committed by GitHub
parent 05aef95bbd
commit 1b1b51ded2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 34 additions and 8 deletions

View File

@ -14,6 +14,7 @@ X.Y.Z Release notes (YYYY-MM-DD)
* [Sync] Added support for `Session.addConnectionNotification()` and `Session.removeConnectionNotification`. * [Sync] Added support for `Session.addConnectionNotification()` and `Session.removeConnectionNotification`.
* [Sync] Added `Session.connectionState`. * [Sync] Added `Session.connectionState`.
* [Sync] Added `Session.isConnected()`. * [Sync] Added `Session.isConnected()`.
* [Sync] Added a check to prevent the case where query-based sync is opened without a schema. It is not possible to deduce the schema, and subscribing to a query-based sync will lead to an error if no schema is defined (#1976).
### Bug fixes ### Bug fixes
* React Native for Android now supports the Android Gradle Plugin 3.0 (#1742). * React Native for Android now supports the Android Gradle Plugin 3.0 (#1742).

View File

@ -96,6 +96,8 @@ class Realm {
* In this case, `config.schema` is _optional_ or not have changed, unless * In this case, `config.schema` is _optional_ or not have changed, unless
* `config.schemaVersion` is incremented, in which case the Realm will be automatically * `config.schemaVersion` is incremented, in which case the Realm will be automatically
* migrated to use the new schema. * migrated to use the new schema.
* In the case of query-based sync, `config.schema` is required. An exception will be
* thrown if `config.schema` is not defined.
* @param {Realm~Configuration} [config] - **Required** when first creating the Realm. * @param {Realm~Configuration} [config] - **Required** when first creating the Realm.
* @throws {Error} If anything in the provided `config` is invalid. * @throws {Error} If anything in the provided `config` is invalid.
* @throws {IncompatibleSyncedRealmError} when an incompatible synced Realm is opened * @throws {IncompatibleSyncedRealmError} when an incompatible synced Realm is opened
@ -105,8 +107,11 @@ class Realm {
/** /**
* Open a Realm asynchronously with a promise. If the Realm is synced, it will be fully * Open a Realm asynchronously with a promise. If the Realm is synced, it will be fully
* synchronized before it is available. * synchronized before it is available.
* In the case of query-based sync, `config.schema` is required. An exception will be
* thrown if `config.schema` is not defined.
* @param {Realm~Configuration} config - if no config is defined, it will open the default realm * @param {Realm~Configuration} config - if no config is defined, it will open the default realm
* @returns {ProgressPromise} - a promise that will be resolved with the Realm instance when it's available. * @returns {ProgressPromise} - a promise that will be resolved with the Realm instance when it's available.
* @throws {Error} If anything in the provided `config` is invalid.
*/ */
static open(config) {} static open(config) {}

View File

@ -80,6 +80,9 @@ module.exports = function(realmConstructor) {
// result in sync rejecting the writes. `_waitForDownload` ensures that the session is kept // result in sync rejecting the writes. `_waitForDownload` ensures that the session is kept
// alive until our callback has returned, which prevents it from being torn down and recreated // alive until our callback has returned, which prevents it from being torn down and recreated
// when we close the schemaless Realm and open it with the correct schema. // when we close the schemaless Realm and open it with the correct schema.
if (!config.sync.fullSynchronization && config.schema === undefined) {
throw new Error('Query-based sync requires a schema.');
}
let syncSession; let syncSession;
let promise = new Promise((resolve, reject) => { let promise = new Promise((resolve, reject) => {
let realm = new realmConstructor(waitForDownloadConfig(config)); let realm = new realmConstructor(waitForDownloadConfig(config));

View File

@ -609,6 +609,13 @@ SharedRealm RealmClass<T>::create_shared_realm(ContextType ctx, realm::Realm::Co
handleRealmFileException(ctx, config, ex); handleRealmFileException(ctx, config, ex);
} }
#if REALM_ENABLE_SYNC
auto schema = realm->schema();
if (realm->is_partial() && schema.empty() && config.cache) {
throw std::invalid_argument("Query-based sync requires a schema.");
}
#endif
GlobalContextType global_context = Context<T>::get_global_context(ctx); GlobalContextType global_context = Context<T>::get_global_context(ctx);
if (!realm->m_binding_context) { if (!realm->m_binding_context) {
realm->m_binding_context.reset(new RealmDelegate<T>(realm, global_context)); realm->m_binding_context.reset(new RealmDelegate<T>(realm, global_context));

View File

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

View File

@ -174,16 +174,16 @@ module.exports = {
let config = (user, url) => { let config = (user, url) => {
return { return {
schema: [ schema: [
Realm.Permissions.Permission,
Realm.Permissions.User,
Realm.Permissions.Role,
{ {
name: 'Object', name: 'Object',
properties: { properties: {
value: 'int', value: 'int',
permissions: '__Permission[]' permissions: '__Permission[]'
} }
} },
Realm.Permissions.Permission,
Realm.Permissions.User,
Realm.Permissions.Role,
], ],
sync: {user: user, url: url, fullSynchronization: false } sync: {user: user, url: url, fullSynchronization: false }
}; };

View File

@ -478,7 +478,8 @@ module.exports = {
sync: { sync: {
user, user,
error : err => console.log(err), error : err => console.log(err),
url: 'realm://localhost:9080/~/sync-v1' url: 'realm://localhost:9080/~/sync-v1',
fullSynchronization: true,
} }
}; };
return Realm.open(config) return Realm.open(config)
@ -520,7 +521,8 @@ module.exports = {
sync: { sync: {
user, user,
error : err => console.log(err), error : err => console.log(err),
url: 'realm://localhost:9080/~/sync-v1' url: 'realm://localhost:9080/~/sync-v1',
fullSynchronization: true,
} }
}; };
@ -776,7 +778,8 @@ module.exports = {
url: `realm://localhost:9080/default/__partial/`, url: `realm://localhost:9080/default/__partial/`,
_disableQueryBasedSyncUrlChecks: true, _disableQueryBasedSyncUrlChecks: true,
fullSynchronization: false, fullSynchronization: false,
} },
schema: [ { name: 'Dog', properties: { name: 'string' } } ]
}; };
const realm = new Realm(config1); const realm = new Realm(config1);
TestCase.assertFalse(realm.isClosed); TestCase.assertFalse(realm.isClosed);
@ -816,6 +819,12 @@ module.exports = {
TestCase.assertThrows(() => Realm.automaticSyncConfiguration('foo', 'bar')); // too many arguments TestCase.assertThrows(() => Realm.automaticSyncConfiguration('foo', 'bar')); // too many arguments
} }
function schemalessNotAllowed() {
let config = Realm.Sync.User.current.createConfiguration();
config.schema = undefined; // no schema in the configuration
Realm.deleteFile(config);
TestCase.assertThrows(() => { let realm = new Realm(config); } );
}
return runOutOfProcess(__dirname + '/partial-sync-api-helper.js', username, REALM_MODULE_PATH) return runOutOfProcess(__dirname + '/partial-sync-api-helper.js', username, REALM_MODULE_PATH)
.then(() => { .then(() => {
@ -826,6 +835,7 @@ module.exports = {
__partialIsNotAllowed(); __partialIsNotAllowed();
shouldFail(); shouldFail();
defaultRealmInvalidArguments(); defaultRealmInvalidArguments();
schemalessNotAllowed();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let config = Realm.Sync.User.current.createConfiguration(); let config = Realm.Sync.User.current.createConfiguration();