Merge pull request #1105 from realm/progress-notifications

Add progress notifications
This commit is contained in:
blagoev 2017-06-29 00:02:37 +03:00 committed by GitHub
commit f1695f33db
2 changed files with 161 additions and 67 deletions

View File

@ -159,6 +159,8 @@ public:
using ConstructorMap = typename Schema<T>::ConstructorMap; using ConstructorMap = typename Schema<T>::ConstructorMap;
using WaitHandler = void(std::error_code); using WaitHandler = void(std::error_code);
using ProgressHandler = void(uint64_t transferred_bytes, uint64_t transferrable_bytes);
static FunctionType create_constructor(ContextType); static FunctionType create_constructor(ContextType);
@ -559,7 +561,7 @@ void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType,
Protected<FunctionType> protected_callback(ctx, callback_function); Protected<FunctionType> protected_callback(ctx, callback_function);
Protected<ObjectType> protected_this(ctx, this_object); Protected<ObjectType> protected_this(ctx, this_object);
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx)); Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
EventLoopDispatcher<WaitHandler> wait_handler([=](std::error_code error_code) { EventLoopDispatcher<WaitHandler> wait_handler([=](std::error_code error_code) {
HANDLESCOPE HANDLESCOPE
if (!error_code) { if (!error_code) {
@ -579,13 +581,45 @@ void RealmClass<T>::wait_for_download_completion(ContextType ctx, FunctionType,
}); });
std::function<WaitHandler> waitFunc = std::move(wait_handler); std::function<WaitHandler> waitFunc = std::move(wait_handler);
std::function<ProgressHandler> progressFunc;
auto realm = realm::Realm::get_shared_realm(config); 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 = "_onDownloadProgress";
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<FunctionType> protected_progressCallback(protected_ctx, Value::validated_to_function(protected_ctx, progressFuncValue));
EventLoopDispatcher<ProgressHandler> 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<T>::callback(protected_ctx, protected_progressCallback, protected_this, 2, callback_arguments);
});
progressFunc = std::move(progress_handler);
}
}
std::shared_ptr<SyncUser> user = sync_config->user; std::shared_ptr<SyncUser> user = sync_config->user;
if (user && user->state() != SyncUser::State::Error) { if (user && user->state() != SyncUser::State::Error) {
if (auto session = user->session_for_on_disk_path(config.path)) { if (auto session = user->session_for_on_disk_path(config.path)) {
if (progressFuncDefined) {
session->register_progress_notifier(std::move(progressFunc), SyncSession::NotifierType::download, false);
}
session->wait_for_download_completion([=](std::error_code error_code) { 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); waitFunc(error_code);
}); });
return; return;

View File

@ -74,6 +74,23 @@ function promisifiedLogin(server, username, password) {
}); });
} }
function runOutOfProcess(nodeJsFilePath) {
var nodeArgs = Array.prototype.slice.call(arguments);
let tmpDir = tmp.dirSync();
let content = fs.readFileSync(nodeJsFilePath, 'utf8');
let tmpFile = tmp.fileSync({ dir: tmpDir.name });
fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' });
nodeArgs[0] = tmpFile.name;
return new Promise((resolve, reject) => {
const child = execFile('node', nodeArgs, { cwd: tmpDir.name }, (error, stdout, stderr) => {
if (error) {
reject(new Error(`Error executing ${nodeJsFilePath} Error: ${error}`));
}
resolve();
});
})
}
module.exports = { module.exports = {
testLocalRealmHasNoSession() { testLocalRealmHasNoSession() {
@ -131,44 +148,31 @@ module.exports = {
const realmName = uuid(); const realmName = uuid();
const expectedObjectsCount = 3; const expectedObjectsCount = 3;
let tmpDir = tmp.dirSync(); runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
let content = fs.readFileSync(__dirname + '/download-api-helper.js', 'utf8'); .then(() => {
let tmpFile = tmp.fileSync({ dir: tmpDir.name }); return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
fs.appendFileSync(tmpFile.fd, content, { encoding: 'utf8' }); const accessTokenRefreshed = this;
let successCounter = 0;
return new Promise((resolve, reject) => { let config = {
//execute download-api-helper which inserts predefined number of objects into the synced realm. sync: { user, url: `realm://localhost:9080/~/${realmName}` },
const child = execFile('node', [tmpFile.name, username, realmName, REALM_MODULE_PATH], { cwd: tmpDir.name }, (error, stdout, stderr) => { schema: [{ name: 'Dog', properties: { name: 'string' } }],
if (error) { };
reject(new Error('Error executing download api helper' + error));
} return Realm.open(config)
resolve(); .then(realm => {
let actualObjectsCount = realm.objects('Dog').length;
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count");
return realm.syncSession;
}).then(session => {
TestCase.assertInstanceOf(session, Realm.Sync.Session);
TestCase.assertEqual(session.user.identity, user.identity);
TestCase.assertEqual(session.config.url, config.sync.url);
TestCase.assertEqual(session.config.user.identity, config.sync.user.identity);
TestCase.assertEqual(session.state, 'active');
});
});
}); });
})
.then(() => {
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
const accessTokenRefreshed = this;
let successCounter = 0;
let config = {
sync: { user, url: `realm://localhost:9080/~/${realmName}` },
schema: [{ name: 'Dog', properties: { name: 'string' } }],
};
return Realm.open(config)
.then(realm => {
let actualObjectsCount = realm.objects('Dog').length;
TestCase.assertEqual(actualObjectsCount, expectedObjectsCount, "Synced realm does not contain the expected objects count");
return realm.syncSession;
}).then(session => {
TestCase.assertInstanceOf(session, Realm.Sync.Session);
TestCase.assertEqual(session.user.identity, user.identity);
TestCase.assertEqual(session.config.url, config.sync.url);
TestCase.assertEqual(session.config.user.identity, config.sync.user.identity);
TestCase.assertEqual(session.state, 'active');
});
});
});
}, },
testRealmOpenAsync() { testRealmOpenAsync() {
@ -180,20 +184,7 @@ module.exports = {
const realmName = uuid(); const realmName = uuid();
const expectedObjectsCount = 3; const expectedObjectsCount = 3;
let tmpDir = tmp.dirSync(); runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
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('Error executing download api helper' + error));
}
resolve();
});
})
.then(() => { .then(() => {
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => { return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -237,6 +228,88 @@ module.exports = {
}); });
}, },
testProgressNotificationsForRealmOpen() {
if (!isNodeProccess) {
return Promise.resolve();
}
const username = uuid();
const realmName = uuid();
const expectedObjectsCount = 3;
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
.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}`,
_onDownloadProgress: (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;
runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
.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}`,
_onDownloadProgress: (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() { testRealmOpenAsyncNoSchema() {
if (!isNodeProccess) { if (!isNodeProccess) {
return Promise.resolve(); return Promise.resolve();
@ -246,20 +319,7 @@ module.exports = {
const realmName = uuid(); const realmName = uuid();
const expectedObjectsCount = 3; const expectedObjectsCount = 3;
let tmpDir = tmp.dirSync(); runOutOfProcess(__dirname + '/download-api-helper.js', username, realmName, REALM_MODULE_PATH)
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('Error executing download api helper' + error));
}
resolve();
});
})
.then(() => { .then(() => {
return promisifiedLogin('http://localhost:9080', username, 'password').then(user => { return promisifiedLogin('http://localhost:9080', username, 'password').then(user => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {