diff --git a/docs/realm.js b/docs/realm.js index c953a337..e0a0ddde 100644 --- a/docs/realm.js +++ b/docs/realm.js @@ -272,6 +272,18 @@ class Realm { * @returns {true} if compaction succeeds. */ compact() {} + + /** + * Writes a compacted copy of the Realm to the given path. + * + * The destination file cannot already exist. + * + * Note that if this method is called from within a write transaction, the current data is written, + * not the data from the point when the previous write transaction was committed. + * @param {string} path path to save the Realm to + * @param {ArrayBuffer|ArrayBufferView} [encryptionKey] - Optional 64-byte encryption key to encrypt the new file with. + */ + writeCopyTo(path, encryptionKey) {} } /** diff --git a/lib/index.d.ts b/lib/index.d.ts index 669ed99c..664168e4 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -677,6 +677,14 @@ declare class Realm { */ compact(): boolean; + /** + * Write a copy to destination path + * @param path destination path + * @param encryptionKey encryption key to use + * @returns void + */ + writeCopyTo(path: string, encryptionKey?: ArrayBuffer | ArrayBufferView): void; + privileges() : Realm.Permissions.Realm; privileges(objectType: string | Realm.ObjectSchema | Function) : Realm.Permissions.Class; privileges(obj: Realm.Object) : Realm.Permissions.Class; diff --git a/src/js_realm.hpp b/src/js_realm.hpp index 977a5e12..d38f783c 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -183,6 +183,7 @@ public: static void remove_all_listeners(ContextType, ObjectType, Arguments, ReturnValue &); static void close(ContextType, ObjectType, Arguments, ReturnValue &); static void compact(ContextType, ObjectType, Arguments, ReturnValue &); + static void writeCopyTo(ContextType, ObjectType, Arguments, ReturnValue &); static void delete_model(ContextType, ObjectType, Arguments, ReturnValue &); static void object_for_object_id(ContextType, ObjectType, Arguments, ReturnValue&); static void privileges(ContextType, ObjectType, Arguments, ReturnValue&); @@ -241,6 +242,7 @@ public: {"removeAllListeners", wrap}, {"close", wrap}, {"compact", wrap}, + {"writeCopyTo", wrap}, {"deleteModel", wrap}, {"privileges", wrap}, {"_objectForObjectId", wrap}, @@ -1004,6 +1006,36 @@ void RealmClass::compact(ContextType ctx, ObjectType this_object, Arguments a return_value.set(realm->compact()); } +template +void RealmClass::writeCopyTo(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue &return_value) { + args.validate_maximum(2); + + if (args.count == 0) { + throw std::runtime_error("At least path has to be provided for 'writeCopyTo'"); + } + + SharedRealm realm = *get_internal>(this_object); + + ValueType pathValue = args[0]; + if (!Value::is_string(ctx, pathValue)) { + throw std::runtime_error("Argument to 'writeCopyTo' must be a String."); + } + + std::string path = Value::validated_to_string(ctx, pathValue); + BinaryData key; + if (args.count == 2) { + ValueType key_value = args[1]; + if (!Value::is_binary(ctx, key_value)) { + throw std::runtime_error("Encryption key for 'writeCopyTo' must be a Binary."); + } + + auto key_data = Value::validated_to_binary(ctx, key_value); + key = { static_cast(key_data.data()), key_data.size() }; + } + + realm->write_copy(path, key); +} + template void RealmClass::object_for_object_id(ContextType ctx, ObjectType this_object, Arguments args, ReturnValue& return_value) { args.validate_count(2); diff --git a/tests/js/realm-tests.js b/tests/js/realm-tests.js index 9fe9ab76..a2e4d493 100644 --- a/tests/js/realm-tests.js +++ b/tests/js/realm-tests.js @@ -56,7 +56,7 @@ module.exports = { const realm2 = new Realm({schema: [], path: testPath2}); TestCase.assertEqual(realm2.path, defaultDir + testPath2); }, - + testRealmIsClosed: function() { const realm = new Realm({schema: []}); TestCase.assertFalse(realm.isClosed); @@ -1200,8 +1200,49 @@ module.exports = { testDisableFileFormatUpgrade: function() { Realm.copyBundledRealmFiles(); - TestCase.assertThrowsContaining(() => { + TestCase.assertThrowsContaining(() => { new Realm({ path: 'dates-v3.realm', disableFormatUpgrade: true } ); }, 'The Realm file format must be allowed to be upgraded in order to proceed.'); + }, + + testWriteCopyTo: function() { + const realm = new Realm({schema: [schemas.IntPrimary, schemas.AllTypes, schemas.TestObject, schemas.LinkToAllTypes]}); + + realm.write(() => { + realm.create('TestObject', {doubleCol: 1}); + }); + TestCase.assertEqual(1, realm.objects('TestObject').length); + + TestCase.assertThrowsContaining(() => { + realm.writeCopyTo(); + }, "At least path has to be provided for 'writeCopyTo'"); + + TestCase.assertThrowsContaining(() => { + realm.writeCopyTo(34); + }, "Argument to 'writeCopyTo' must be a String."); + + const copyName = "testWriteCopy.realm"; + realm.writeCopyTo(copyName); + + const copyConfig = { path: copyName }; + const realmCopy = new Realm(copyConfig); + TestCase.assertEqual(1, realmCopy.objects('TestObject').length); + realmCopy.close(); + + TestCase.assertThrowsContaining(() => { + realm.writeCopyTo("testWriteCopyWithInvalidKey.realm", "hello"); + }, "Encryption key for 'writeCopyTo' must be a Binary."); + + const encryptedCopyName = "testWriteEncryptedCopy.realm"; + var encryptionKey = new Int8Array(64); + encryptionKey[0] = 1; + realm.writeCopyTo(encryptedCopyName, encryptionKey); + + const encryptedCopyConfig = { path: encryptedCopyName, encryptionKey: encryptionKey }; + const encryptedRealmCopy = new Realm(encryptedCopyConfig); + TestCase.assertEqual(1, encryptedRealmCopy.objects('TestObject').length); + encryptedRealmCopy.close(); + + realm.close(); } };