From 97f25cc1ab42e418a0746522319ed6bb8bcf7263 Mon Sep 17 00:00:00 2001 From: blagoev Date: Tue, 27 Jun 2017 23:45:46 +0300 Subject: [PATCH] Add progress notifications Support download progress notifications for Realm open and openAsync methods --- src/js_realm.hpp | 43 +++++++++++++-- tests/js/session-tests.js | 108 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/src/js_realm.hpp b/src/js_realm.hpp index a06dad00..9bcf6448 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -159,6 +159,8 @@ public: using ConstructorMap = typename Schema::ConstructorMap; using WaitHandler = void(std::error_code); + using ProgressHandler = void(uint64_t transferred_bytes, uint64_t transferrable_bytes); + static FunctionType create_constructor(ContextType); @@ -559,7 +561,7 @@ void RealmClass::wait_for_download_completion(ContextType ctx, FunctionType, Protected protected_callback(ctx, callback_function); Protected protected_this(ctx, this_object); Protected protected_ctx(Context::get_global_context(ctx)); - + EventLoopDispatcher wait_handler([=](std::error_code error_code) { HANDLESCOPE if (!error_code) { @@ -579,13 +581,48 @@ void RealmClass::wait_for_download_completion(ContextType ctx, FunctionType, }); std::function waitFunc = std::move(wait_handler); + std::function progressFunc; + auto realm = realm::Realm::get_shared_realm(config); - if (auto sync_config = config.sync_config) { + if (auto sync_config = config.sync_config) + { + static const String progressFuncName = "_onProgress"; + bool progressFuncDefined = false; + if (!Value::is_boolean(ctx, sync_config_value) && !Value::is_undefined(ctx, sync_config_value)) + { + auto sync_config_object = Value::validated_to_object(ctx, sync_config_value); + + ValueType progressFuncValue = Object::get_property(ctx, sync_config_object, progressFuncName); + progressFuncDefined = !Value::is_undefined(ctx, progressFuncValue); + + if (progressFuncDefined) + { + Protected protected_progressCallback(protected_ctx, Value::validated_to_function(protected_ctx, progressFuncValue)); + EventLoopDispatcher progress_handler([=](uint64_t transferred_bytes, uint64_t transferrable_bytes) { + HANDLESCOPE + ValueType callback_arguments[2]; + callback_arguments[0] = Value::from_number(protected_ctx, transferred_bytes); + callback_arguments[1] = Value::from_number(protected_ctx, transferrable_bytes); + + Function::callback(protected_ctx, protected_progressCallback, protected_this, 2, callback_arguments); + }); + + progressFunc = std::move(progress_handler); + } + } + std::shared_ptr user = sync_config->user; if (user && user->state() != SyncUser::State::Error) { if (auto session = user->session_for_on_disk_path(config.path)) { + if (progressFuncDefined) { + session->register_progress_notifier([=](uint64_t transferred_bytes, uint64_t transferrable_bytes) { + progressFunc(transferred_bytes, transferrable_bytes); + }, SyncSession::NotifierType::download, false); + } + + session->wait_for_download_completion([=](std::error_code error_code) { - realm->close(); //capture and keep realm instance for till here + realm->close(); //capture and keep realm instance for until here waitFunc(error_code); }); return; diff --git a/tests/js/session-tests.js b/tests/js/session-tests.js index cccbf48f..6d9a7a6e 100644 --- a/tests/js/session-tests.js +++ b/tests/js/session-tests.js @@ -237,6 +237,114 @@ module.exports = { }); }, + testProgressNotificationsForRealmOpen() { + if (!isNodeProccess) { + return Promise.resolve(); + } + + const username = uuid(); + const realmName = uuid(); + const expectedObjectsCount = 3; + + let tmpDir = tmp.dirSync(); + let content = fs.readFileSync(__dirname + '/download-api-helper.js', 'utf8'); + let tmpFile = tmp.fileSync({ dir: tmpDir.name }); + fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' }); + + return new Promise((resolve, reject) => { + //execute download-api-helper which inserts predefined number of objects into the synced realm. + const child = execFile('node', [tmpFile.name, username, realmName, REALM_MODULE_PATH], { cwd: tmpDir.name }, (error, stdout, stderr) => { + if (error) { + reject(new Error('testProgressNotifications: Error executing download api helper for Realm.open ' + error)); + } + resolve(); + }); + }) + .then(() => { + return promisifiedLogin('http://localhost:9080', username, 'password').then(user => { + const accessTokenRefreshed = this; + let successCounter = 0; + let progressNotificationCalled = false; + let config = { + sync: { + user, + url: `realm://localhost:9080/~/${realmName}`, + _onProgress: (transferred, remaining) => { + progressNotificationCalled = true + }, + }, + schema: [{ name: 'Dog', properties: { name: 'string' } }], + }; + + return Realm.open(config) + .then(realm => { + }).then(session => { + TestCase.assertTrue(progressNotificationCalled, "Progress notification not called for Realm.open"); + }); + }); + }); + }, + + testProgressNotificationsForRealmOpenAsync() { + if (!isNodeProccess) { + return Promise.resolve(); + } + + const username = uuid(); + const realmName = uuid(); + const expectedObjectsCount = 3; + + let tmpDir = tmp.dirSync(); + let content = fs.readFileSync(__dirname + '/download-api-helper.js', 'utf8'); + let tmpFile = tmp.fileSync({ dir: tmpDir.name }); + fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' }); + + return new Promise((resolve, reject) => { + //execute download-api-helper which inserts predefined number of objects into the synced realm. + const child = execFile('node', [tmpFile.name, username, realmName, REALM_MODULE_PATH], { cwd: tmpDir.name }, (error, stdout, stderr) => { + if (error) { + reject(new Error('testProgressNotifications: Error executing download api helper for Realm.openAsync ' + error)); + } + resolve(); + }); + }) + .then(() => { + return promisifiedLogin('http://localhost:9080', username, 'password').then(user => { + return new Promise((resolve, reject) => { + let progressNotificationCalled = false; + let config = { + sync: { user, url: `realm://localhost:9080/~/${realmName}`, + _onProgress: (transferred, remaining) => { + progressNotificationCalled = true + }, + }, + schema: [{ name: 'Dog', properties: { name: 'string' } }], + }; + + Realm.openAsync(config, (error, realm) => { + try { + if (error) { + reject(error); + } + + setTimeout(() => { + try { + TestCase.assertTrue(progressNotificationCalled, "Progress notification not called for Realm.openAsync"); + resolve(); + } catch (e) { + reject(e); + } + }, 50); + } + catch (e) { + reject(e); + } + }); + }); + }); + }); + }, + testRealmOpenAsyncNoSchema() { if (!isNodeProccess) { return Promise.resolve();