From 532f517ded0673c142d1b61ffe1b5de0822543fd Mon Sep 17 00:00:00 2001 From: Ashwin Phatak Date: Mon, 11 Sep 2017 11:15:08 +0530 Subject: [PATCH] Add Realm.deleteModel API (#573) --- CHANGELOG.md | 1 + docs/realm.js | 6 ++ lib/browser/index.js | 1 + lib/index.d.ts | 5 ++ src/js_realm.hpp | 14 ++- tests/js/migration-tests.js | 166 ++++++++++++++++++++++++++++++++++++ 6 files changed, 192 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a56cce72..f80cba59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ X.Y.Z Release notes * Added Progress API `realm.syncSession.addProgressNotification` and `realm.syncSession.removeProgressNotification` * Added additional parameter for `Realm.open` and `Realm.openAsync` for download progress notifications * Added `Realm.deleteFile` for deleting a Realm (#363). +* Added `Realm.deleteModel` for deleting a Realm model in a migration (#573). ### Bug fixes * Adding missing TypeScript declation (#1283). diff --git a/docs/realm.js b/docs/realm.js index 05d5ead5..2bd4c314 100644 --- a/docs/realm.js +++ b/docs/realm.js @@ -132,6 +132,12 @@ class Realm { */ delete(object) {} + /** + * Deletes a Realm model, including all of its objects. + * @param {string} name - the model name + */ + deleteModel(name) {} + /** * **WARNING:** This will delete **all** objects in the Realm! */ diff --git a/lib/browser/index.js b/lib/browser/index.js index 693f108b..cb285f8e 100644 --- a/lib/browser/index.js +++ b/lib/browser/index.js @@ -131,6 +131,7 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [ // Mutating methods: util.createMethods(Realm.prototype, objectTypes.REALM, [ 'delete', + 'deleteModel', 'deleteAll', 'write', 'compact', diff --git a/lib/index.d.ts b/lib/index.d.ts index 91320b2c..dd4c75a1 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -484,6 +484,11 @@ declare class Realm { */ delete(object: Realm.Object | Realm.Object[] | Realm.List | Realm.Results | any): void; + /** + * @returns void + */ + deleteModel(name: string): void; + /** * @returns void */ diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 3526960b..6bfdb496 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -180,7 +180,7 @@ public: static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void close(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void compact(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); - + static void delete_model(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); // properties static void get_empty(ContextType, ObjectType, ReturnValue &); @@ -235,6 +235,7 @@ public: {"removeAllListeners", wrap}, {"close", wrap}, {"compact", wrap}, + {"deleteModel", wrap}, }; PropertyMap const properties = { @@ -559,6 +560,17 @@ void RealmClass::delete_file(ContextType ctx, FunctionType, ObjectType this_o } +template +void RealmClass::delete_model(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 1); + ValueType value = arguments[0]; + + SharedRealm& realm = *get_internal>(this_object); + + std::string model_name = Value::validated_to_string(ctx, value, "deleteModel"); + ObjectStore::delete_data_for_object(realm->read_group(), model_name); +} + template void RealmClass::get_default_path(ContextType ctx, ObjectType object, ReturnValue &return_value) { return_value.set(realm::js::default_path()); diff --git a/tests/js/migration-tests.js b/tests/js/migration-tests.js index 017ec45e..de5cd271 100644 --- a/tests/js/migration-tests.js +++ b/tests/js/migration-tests.js @@ -158,4 +158,170 @@ module.exports = { } }); }, + + testDeleteModelMigration: function() { + const schema = [{ + name: 'TestObject', + properties: { + prop0: 'string', + prop1: 'int', + } + }]; + + var realm = new Realm({schema: schema}); + + realm.write(function() { + realm.create('TestObject', ['stringValue', 1]); + }); + + realm.close(); + + realm = new Realm({schema: [], schemaVersion: 1}); + TestCase.assertEqual(realm.schema.length, 0); // no models + realm.close(); // this won't delete the model + + realm = new Realm({schema: schema, schemaVersion: 2}); + TestCase.assertEqual(realm.objects('TestObject').length, 1); // the model objects are still there + realm.close(); + + // now delete the model explicitly, which should delete the objects too + realm = new Realm({schema: [], schemaVersion: 3, migration: function(oldRealm, newRealm) { + newRealm.deleteModel('TestObject'); + }}); + + TestCase.assertEqual(realm.schema.length, 0); // no models + + realm.close(); + + realm = new Realm({schema: schema, schemaVersion: 4}); + + TestCase.assertEqual(realm.objects('TestObject').length, 0); + + realm.close(); + }, + + testDeleteModelInSchema: function() { + const schema = [{ + name: 'TestObject', + properties: { + prop0: 'string', + prop1: 'int', + } + }]; + + var realm = new Realm({schema: schema}); + + realm.write(function() { + realm.create('TestObject', ['stringValue', 1]); + }); + + realm.close(); + + + // now delete the model explicitly, but it should remain as it's still in the schema + // only the rows should get deleted + realm = new Realm({schema: schema, schemaVersion: 1, migration: function(oldRealm, newRealm) { + newRealm.deleteModel('TestObject'); + }}); + + TestCase.assertEqual(realm.schema.length, 1); // model should remain + TestCase.assertEqual(realm.objects('TestObject').length, 0); // objects should be gone + + realm.close(); + + realm = new Realm({schema: schema, schemaVersion: 2}); + TestCase.assertEqual(realm.objects('TestObject').length, 0); + + realm.close(); + }, + + testDeleteModelIgnoreNotExisting: function() { + const schema = [{ + name: 'TestObject', + properties: { + prop0: 'string', + prop1: 'int', + } + }]; + + var realm = new Realm({schema: schema}); + + realm.write(function() { + realm.create('TestObject', ['stringValue', 1]); + }); + + realm.close(); + + // non-existing models should be ignore on delete + realm = new Realm({schema: schema, schemaVersion: 1, migration: function(oldRealm, newRealm) { + newRealm.deleteModel('NonExistingModel'); + }}); + + realm.close(); + + realm = new Realm({schema: schema, schemaVersion: 2}); + TestCase.assertEqual(realm.objects('TestObject').length, 1); + + realm.close(); + }, + + testDeleteModelWithRelationship: function() { + const ShipSchema = { + name: 'Ship', + properties: { + ship_name: 'string', + captain: 'Captain' + } + }; + + const CaptainSchema = { + name: 'Captain', + properties: { + captain_name: 'string', + ships: { type: 'linkingObjects', objectType: 'Ship', property: 'captain' } + } + }; + + var realm = new Realm({schema: [ShipSchema, CaptainSchema]}); + + realm.write(function() { + realm.create('Ship', { + ship_name: 'My Ship', + captain: { + captain_name: 'John Doe' + } + }); + }); + + TestCase.assertEqual(realm.objects('Captain').length, 1); + TestCase.assertEqual(realm.objects('Ship').length, 1); + TestCase.assertEqual(realm.objects('Ship')[0].captain.captain_name, "John Doe"); + TestCase.assertEqual(realm.objects('Captain')[0].ships[0].ship_name, "My Ship"); + + realm.close(); + + realm = new Realm({schema: [ShipSchema, CaptainSchema], schemaVersion: 1, migration: function(oldRealm, newRealm) { + TestCase.assertThrows(function(e) { + // deleting a model which is target of linkingObjects results in an exception + newRealm.deleteModel('Captain'); + console.log(e); + }, "Table is target of cross-table link columns"); + }}); + + TestCase.assertEqual(realm.objects('Captain').length, 1); + TestCase.assertEqual(realm.objects('Ship').length, 1); + + realm.close(); + + realm = new Realm({schema: [ShipSchema, CaptainSchema], schemaVersion: 2, migration: function(oldRealm, newRealm) { + // deleting a model which isn't target of linkingObjects works fine + newRealm.deleteModel('Ship'); + }}); + + TestCase.assertEqual(realm.objects('Captain').length, 1); + TestCase.assertEqual(realm.objects('Ship').length, 0); + TestCase.assertEqual(realm.objects('Captain')[0].ships.length, 0); + + realm.close(); + }, };