diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b31ca49..787f1375 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ X.Y.Z Release notes ### Enhancements * Improve performance of the RPC worker for chrome debugging. +* Added `Realm.deleteFile` for deleting a Realm (#363). ### Bug fixes * None @@ -35,6 +36,7 @@ X.Y.Z Release notes ### Bug fixes * None + 1.10.3 Release notes (2017-8-16) ============================================================= ### Breaking changes diff --git a/docs/realm.js b/docs/realm.js index a4d667aa..05d5ead5 100644 --- a/docs/realm.js +++ b/docs/realm.js @@ -237,6 +237,13 @@ class Realm { */ Realm.schemaVersion = function(path, encryptionKey) {}; +/** + * Delete the Realm file for the given configuration. + * @param {Realm~Configuration} config + * @throws {Error} If anything in the provided `config` is invalid. + */ +Realm.deleteFile = function(config) {}; + /** * The default path where to create and access the Realm file. * @type {string} diff --git a/lib/browser/index.js b/lib/browser/index.js index ccecf45e..8caff788 100644 --- a/lib/browser/index.js +++ b/lib/browser/index.js @@ -169,6 +169,11 @@ Object.defineProperties(Realm, { return rpc.callMethod(undefined, Realm[keys.id], 'schemaVersion', Array.from(arguments)); } }, + deleteFile: { + value: function(config) { + return rpc.callMethod(undefined, Realm[keys.id], 'deleteFile', Array.from(arguments)); + } + }, copyBundledRealmFiles: { value: function() { return rpc.callMethod(undefined, Realm[keys.id], 'copyBundledRealmFiles', []); diff --git a/lib/index.d.ts b/lib/index.d.ts index 4ecfd612..614dd52d 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -423,6 +423,12 @@ declare class Realm { */ static openAsync(config: Realm.Configuration, callback: (error: any, realm: Realm) => void): void + /** + * Delete the Realm file for the given configuration. + * @param {Configuration} config + */ + static deleteFile(config: Realm.Configuration): void + /** * @param {Realm.Configuration} config? */ diff --git a/src/android/platform.cpp b/src/android/platform.cpp index e008169f..e26ba655 100644 --- a/src/android/platform.cpp +++ b/src/android/platform.cpp @@ -93,4 +93,19 @@ namespace realm { s_default_realm_directory + "/*.realm.lock"; system(cmd.c_str()); } + + void remove_directory(const std::string &path) + { + std::string cmd_clear_dir = "rm " + path + "/*"; + system(cmd_clear_dir.c_str()); + + std::string cmd_rmdir = "rmdir " + path; + system(cmd_rmdir.c_str()); + } + + void remove_file(const std::string &path) + { + std::string cmd = "rm " + path; + system(cmd.c_str()); + } } diff --git a/src/ios/platform.mm b/src/ios/platform.mm index 26d93075..c412f8d6 100644 --- a/src/ios/platform.mm +++ b/src/ios/platform.mm @@ -101,5 +101,20 @@ void remove_realm_files_from_directory(const std::string &directory) } } } - + +void remove_file(const std::string &path) +{ + NSFileManager *manager = [NSFileManager defaultManager]; + NSString *filePath = @(path.c_str()); + + if (![manager removeItemAtPath:filePath error:nil]) { + throw std::runtime_error((std::string)"Failed to delete file at path " + filePath.UTF8String); + } +} + +void remove_directory(const std::string &path) +{ + remove_file(path); // works for directories too +} + } diff --git a/src/js_realm.hpp b/src/js_realm.hpp index c9702575..848a2ccc 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -200,6 +200,7 @@ public: static void schema_version(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void clear_test_state(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void copy_bundled_realm_files(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void delete_file(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); // static properties static void get_default_path(ContextType, ObjectType, ReturnValue &); @@ -211,6 +212,7 @@ public: {"schemaVersion", wrap}, {"clearTestState", wrap}, {"copyBundledRealmFiles", wrap}, + {"deleteFile", wrap}, {"_waitForDownload", wrap}, }; @@ -526,6 +528,37 @@ void RealmClass::copy_bundled_realm_files(ContextType ctx, FunctionType, Obje realm::copy_bundled_realm_files(); } +template +void RealmClass::delete_file(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 1); + + ValueType value = arguments[0]; + if (!Value::is_object(ctx, value)) { + throw std::runtime_error("Invalid argument, expected a Realm configuration object"); + } + + ObjectType object = Value::validated_to_object(ctx, value); + realm::Realm::Config config; + + static const String path_string = "path"; + ValueType path_value = Object::get_property(ctx, object, path_string); + if (!Value::is_undefined(ctx, path_value)) { + config.path = Value::validated_to_string(ctx, path_value, "path"); + } + else if (config.path.empty()) { + config.path = js::default_path(); + } + + config.path = normalize_realm_path(config.path); + + std::string realm_file_path = config.path; + realm::remove_file(realm_file_path); + realm::remove_file(realm_file_path + ".lock"); + realm::remove_file(realm_file_path + ".note"); + realm::remove_directory(realm_file_path + ".management"); + +} + template void RealmClass::get_default_path(ContextType ctx, ObjectType object, ReturnValue &return_value) { return_value.set(realm::js::default_path()); diff --git a/src/node/platform.cpp b/src/node/platform.cpp index 4d23e587..9a16fe85 100644 --- a/src/node/platform.cpp +++ b/src/node/platform.cpp @@ -134,4 +134,55 @@ void remove_realm_files_from_directory(const std::string &dir_path) } } +void remove_directory(const std::string &path) +{ + FileSystemRequest exists_req; + if (uv_fs_stat(uv_default_loop(), &exists_req, path.c_str(), nullptr) != 0) { + if (exists_req.result == UV_ENOENT) { + // path doesn't exist, ignore + return; + } else { + throw UVException(static_cast(exists_req.result)); + } + } + + uv_dirent_t dir_entry; + FileSystemRequest dir_scan_req; + if (uv_fs_scandir(uv_default_loop(), &dir_scan_req, path.c_str(), 0, nullptr) < 0) { + throw UVException(static_cast(dir_scan_req.result)); + } + + while (uv_fs_scandir_next(&dir_scan_req, &dir_entry) != UV_EOF) { + std::string dir_entry_path = path + '/' + dir_entry.name; + FileSystemRequest delete_req; + if (uv_fs_unlink(uv_default_loop(), &delete_req, dir_entry_path.c_str(), nullptr) != 0) { + throw UVException(static_cast(delete_req.result)); + } + } + + FileSystemRequest rmdir_req; + if (uv_fs_rmdir(uv_default_loop(), &rmdir_req, path.c_str(), nullptr)) { + throw UVException(static_cast(rmdir_req.result)); + } +} + + +void remove_file(const std::string &path) +{ + FileSystemRequest exists_req; + if (uv_fs_stat(uv_default_loop(), &exists_req, path.c_str(), nullptr) != 0) { + if (exists_req.result == UV_ENOENT) { + // path doesn't exist, ignore + return; + } else { + throw UVException(static_cast(exists_req.result)); + } + } + + FileSystemRequest delete_req; + if (uv_fs_unlink(uv_default_loop(), &delete_req, path.c_str(), nullptr) != 0) { + throw UVException(static_cast(delete_req.result)); + } +} + } // realm diff --git a/src/platform.hpp b/src/platform.hpp index 56afc299..b56037bd 100644 --- a/src/platform.hpp +++ b/src/platform.hpp @@ -41,4 +41,10 @@ void copy_bundled_realm_files(); // remove all realm files in the given directory void remove_realm_files_from_directory(const std::string &directory); +// remove file at the given path +void remove_file(const std::string &path); + +// remove directory at the given path +void remove_directory(const std::string &path); + } diff --git a/tests/js/realm-tests.js b/tests/js/realm-tests.js index 4a66bc5c..e98db623 100644 --- a/tests/js/realm-tests.js +++ b/tests/js/realm-tests.js @@ -1030,5 +1030,41 @@ module.exports = { const realm1 = new Realm({schema: [schemas.StringOnly]}); const realm2 = new Realm({schema: [schemas.StringOnly]}); TestCase.assertThrows(realm1.compact()); + }, + + testRealmDeleteFileDefaultConfigPath: function() { + const config = {schema: [schemas.TestObject]}; + const realm = new Realm(config); + + realm.write(function() { + realm.create('TestObject', {doubleCol: 1}); + }); + + TestCase.assertEqual(realm.objects('TestObject').length, 1); + realm.close(); + + Realm.deleteFile(config); + + const realm2 = new Realm(config); + TestCase.assertEqual(realm2.objects('TestObject').length, 0); + realm.close(); + }, + + testRealmDeleteFileCustomConfigPath: function() { + const config = {schema: [schemas.TestObject], path: 'test-realm-delete-file.realm'}; + const realm = new Realm(config); + + realm.write(function() { + realm.create('TestObject', {doubleCol: 1}); + }); + + TestCase.assertEqual(realm.objects('TestObject').length, 1); + realm.close(); + + Realm.deleteFile(config); + + const realm2 = new Realm(config); + TestCase.assertEqual(realm2.objects('TestObject').length, 0); + realm.close(); } };