realm-js/lib/browser/rpc.js

301 lines
9.4 KiB
JavaScript
Raw Normal View History

2016-02-18 19:59:34 +00:00
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
2015-10-28 17:37:17 +00:00
'use strict';
import * as base64 from './base64';
import { keys, objectTypes } from './constants';
2017-11-14 09:08:40 +00:00
const { id: idKey, realm: _realmKey } = keys;
Add missing chrome debugging apis for Sync.User (#801) * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Replace needle with node-fetch * Make AuthError work in the browser * Allow for persistent callbacks * Expose _authenticateRealm to native * Use clear_test_state in roc * Streamline test inclusion * Fix login tests * Promisify testLogin() * Disable AsyncTests for now * Mode jasmine timeout to spec/unit_tests.js * Avoid use of global for fetch module * Only include user-tests is sync is enabled * Skip ROS for android tests * Add some comments about persistent callbacks * Fix results-test * Run async-tests in node * Add a comment about (user object) * Cache all users in User.current and return promises in fetch * node_require async-tests so RN doesn't try and package them * Add missing parenthesis
2017-01-31 13:07:29 +00:00
let registeredCallbacks = [];
const typeConverters = {};
// Callbacks that are registered initially (currently only refreshAccessToken) will
Add missing chrome debugging apis for Sync.User (#801) * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Replace needle with node-fetch * Make AuthError work in the browser * Allow for persistent callbacks * Expose _authenticateRealm to native * Use clear_test_state in roc * Streamline test inclusion * Fix login tests * Promisify testLogin() * Disable AsyncTests for now * Mode jasmine timeout to spec/unit_tests.js * Avoid use of global for fetch module * Only include user-tests is sync is enabled * Skip ROS for android tests * Add some comments about persistent callbacks * Fix results-test * Run async-tests in node * Add a comment about (user object) * Cache all users in User.current and return promises in fetch * node_require async-tests so RN doesn't try and package them * Add missing parenthesis
2017-01-31 13:07:29 +00:00
// carry this symbol so they are not wiped in clearTestState.
const persistentCallback = Symbol("persistentCallback");
let XMLHttpRequest = global.originalXMLHttpRequest || global.XMLHttpRequest;
let sessionHost;
let sessionId;
// Check if XMLHttpRequest has been overridden, and get the native one if that's the case.
if (XMLHttpRequest.__proto__ != global.XMLHttpRequestEventTarget) {
let fakeXMLHttpRequest = XMLHttpRequest;
delete global.XMLHttpRequest;
XMLHttpRequest = global.XMLHttpRequest;
global.XMLHttpRequest = fakeXMLHttpRequest;
}
2017-11-14 09:08:40 +00:00
registerTypeConverter(objectTypes.DATA, (_, { value }) => base64.decode(value));
registerTypeConverter(objectTypes.DATE, (_, { value }) => new Date(value));
registerTypeConverter(objectTypes.DICT, deserializeDict);
registerTypeConverter(objectTypes.FUNCTION, deserializeFunction);
export function registerTypeConverter(type, handler) {
typeConverters[type] = handler;
}
export function createSession(refreshAccessToken, host) {
refreshAccessToken[persistentCallback] = true;
sessionId = sendRequest('create_session', { refreshAccessToken: serialize(undefined, refreshAccessToken) }, host);
sessionHost = host;
return sessionId;
}
export function createRealm(args) {
if (args) {
args = args.map((arg) => serialize(null, arg));
}
2017-11-14 09:08:40 +00:00
return sendRequest('create_realm', { arguments: args });
}
Add missing chrome debugging apis for Sync.User (#801) * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Replace needle with node-fetch * Make AuthError work in the browser * Allow for persistent callbacks * Expose _authenticateRealm to native * Use clear_test_state in roc * Streamline test inclusion * Fix login tests * Promisify testLogin() * Disable AsyncTests for now * Mode jasmine timeout to spec/unit_tests.js * Avoid use of global for fetch module * Only include user-tests is sync is enabled * Skip ROS for android tests * Add some comments about persistent callbacks * Fix results-test * Run async-tests in node * Add a comment about (user object) * Cache all users in User.current and return promises in fetch * node_require async-tests so RN doesn't try and package them * Add missing parenthesis
2017-01-31 13:07:29 +00:00
export function createUser(args) {
args = args.map((arg) => serialize(null, arg));
2017-11-14 09:08:40 +00:00
const result = sendRequest('create_user', { arguments: args });
Add missing chrome debugging apis for Sync.User (#801) * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Replace needle with node-fetch * Make AuthError work in the browser * Allow for persistent callbacks * Expose _authenticateRealm to native * Use clear_test_state in roc * Streamline test inclusion * Fix login tests * Promisify testLogin() * Disable AsyncTests for now * Mode jasmine timeout to spec/unit_tests.js * Avoid use of global for fetch module * Only include user-tests is sync is enabled * Skip ROS for android tests * Add some comments about persistent callbacks * Fix results-test * Run async-tests in node * Add a comment about (user object) * Cache all users in User.current and return promises in fetch * node_require async-tests so RN doesn't try and package them * Add missing parenthesis
2017-01-31 13:07:29 +00:00
return deserialize(undefined, result);
}
2017-09-27 22:26:20 +00:00
export function _adminUser(args) {
args = args.map((arg) => serialize(null, arg));
2017-11-14 09:08:40 +00:00
const result = sendRequest('_adminUser', { arguments: args });
2017-09-27 22:26:20 +00:00
return deserialize(undefined, result);
}
export function _getExistingUser(args) {
args = args.map((arg) => serialize(null, arg));
const result = sendRequest('_getExistingUser', { arguments: args });
return deserialize(undefined, result);
}
export function callMethod(realmId, id, name, args) {
if (args) {
args = args.map((arg) => serialize(realmId, arg));
}
2015-10-08 08:53:22 +00:00
2017-11-14 09:08:40 +00:00
let result = sendRequest('call_method', { realmId, id, name, arguments: args });
return deserialize(realmId, result);
2015-10-08 08:53:22 +00:00
}
export function getProperty(realmId, id, name) {
2017-11-14 09:08:40 +00:00
let result = sendRequest('get_property', { realmId, id, name });
return deserialize(realmId, result);
}
export function setProperty(realmId, id, name, value) {
2015-10-08 09:00:10 +00:00
value = serialize(realmId, value);
2017-11-14 09:08:40 +00:00
sendRequest('set_property', { realmId, id, name, value });
}
Add missing chrome debugging apis for Sync.User (#801) * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Replace needle with node-fetch * Make AuthError work in the browser * Allow for persistent callbacks * Expose _authenticateRealm to native * Use clear_test_state in roc * Streamline test inclusion * Fix login tests * Promisify testLogin() * Disable AsyncTests for now * Mode jasmine timeout to spec/unit_tests.js * Avoid use of global for fetch module * Only include user-tests is sync is enabled * Skip ROS for android tests * Add some comments about persistent callbacks * Fix results-test * Run async-tests in node * Add a comment about (user object) * Cache all users in User.current and return promises in fetch * node_require async-tests so RN doesn't try and package them * Add missing parenthesis
2017-01-31 13:07:29 +00:00
export function getAllUsers() {
let result = sendRequest('get_all_users');
return deserialize(undefined, result);
}
export function clearTestState() {
sendRequest('clear_test_state');
Add missing chrome debugging apis for Sync.User (#801) * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Add user tests * Add Sync member to Realm * Add a simple test for addListener * Introduce browser User class * Introduce Sync.User proxy * Fix User.all * Add User.logout * Remove some console.log statements * Update rpc.cpp from tests-folder * Use user.current from user-methods, not cpp * remove user.current from two missing places * Start server in realmjs and react tests * Replace needle with node-fetch * Make AuthError work in the browser * Allow for persistent callbacks * Expose _authenticateRealm to native * Use clear_test_state in roc * Streamline test inclusion * Fix login tests * Promisify testLogin() * Disable AsyncTests for now * Mode jasmine timeout to spec/unit_tests.js * Avoid use of global for fetch module * Only include user-tests is sync is enabled * Skip ROS for android tests * Add some comments about persistent callbacks * Fix results-test * Run async-tests in node * Add a comment about (user object) * Cache all users in User.current and return promises in fetch * node_require async-tests so RN doesn't try and package them * Add missing parenthesis
2017-01-31 13:07:29 +00:00
// Clear all registered callbacks that are specific to this session.
registeredCallbacks = registeredCallbacks.filter(cb => Reflect.has(cb, persistentCallback));
}
function registerCallback(callback) {
let key = registeredCallbacks.indexOf(callback);
return key >= 0 ? key : (registeredCallbacks.push(callback) - 1);
}
function serialize(realmId, value) {
if (typeof value == 'undefined') {
2017-11-14 09:08:40 +00:00
return { type: objectTypes.UNDEFINED };
}
if (typeof value == 'function') {
2017-11-14 09:08:40 +00:00
return { type: objectTypes.FUNCTION, value: registerCallback(value) };
}
if (!value || typeof value != 'object') {
2017-11-14 09:08:40 +00:00
return { value: value };
}
let id = value[idKey];
if (id) {
2017-11-14 09:08:40 +00:00
return { id };
}
2015-10-19 23:46:56 +00:00
if (value instanceof Date) {
2017-11-14 09:08:40 +00:00
return { type: objectTypes.DATE, value: value.getTime() };
2015-10-19 23:46:56 +00:00
}
if (Array.isArray(value)) {
let array = value.map((item) => serialize(realmId, item));
2017-11-14 09:08:40 +00:00
return { value: array };
}
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
2017-11-14 09:08:40 +00:00
return { type: objectTypes.DATA, value: base64.encode(value) };
}
let keys = Object.keys(value);
let values = keys.map((key) => serialize(realmId, value[key]));
2017-11-14 09:08:40 +00:00
return { type: objectTypes.DICT, keys, values };
}
export function deserialize(realmId, info) {
let type = info.type;
let handler = type && typeConverters[type];
if (handler) {
return handler(realmId, info);
}
let value = info.value;
if (value && Array.isArray(value)) {
return value.map((item) => deserialize(realmId, item));
}
return value;
}
function deserializeDict(realmId, info) {
2017-11-14 09:08:40 +00:00
let { keys, values } = info;
let object = {};
for (let i = 0, len = keys.length; i < len; i++) {
object[keys[i]] = deserialize(realmId, values[i]);
}
return object;
}
function deserializeFunction(realmId, info) {
return registeredCallbacks[info.value];
}
function makeRequest(url, data) {
let statusCode;
let responseText;
// The global __debug__ object is provided by Visual Studio Code.
if (global.__debug__) {
let request = global.__debug__.require('sync-request');
2017-02-09 22:26:51 +00:00
let response = request('POST', url, {
2017-11-14 09:08:40 +00:00
body: JSON.stringify(data),
headers: {
"Content-Type": "text/plain;charset=UTF-8"
}
2017-02-09 22:26:51 +00:00
});
statusCode = response.statusCode;
responseText = response.body.toString('utf-8');
} else {
let body = JSON.stringify(data);
let request = new XMLHttpRequest();
request.open('POST', url, false);
request.send(body);
statusCode = request.status;
responseText = request.responseText;
}
if (statusCode != 200) {
throw new Error(responseText);
}
return JSON.parse(responseText);
}
2017-10-31 14:54:13 +00:00
let pollTimeoutId;
//returns an object from rpc serialized json value
function deserialize_json_value(value) {
let result = {};
for (let index = 0; index < value.keys.length; index++) {
var propName = value.keys[index];
var propValue = value.values[index];
if (propValue.type && propValue.type == 'dict') {
result[propName] = deserialize_json_value(propValue);
}
else {
result[propName] = propValue.value;
}
}
2017-11-14 09:08:40 +00:00
return result;
}
2017-11-13 20:53:27 +00:00
function sendRequest(command, data, host = sessionHost) {
2017-10-31 14:54:13 +00:00
clearTimeout(pollTimeoutId);
try {
if (!host) {
throw new Error('Must first create RPC session with a valid host');
}
2017-11-14 09:08:40 +00:00
data = Object.assign({}, data, sessionId ? { sessionId } : null);
2017-10-31 14:54:13 +00:00
let url = 'http://' + host + '/' + command;
let response = makeRequest(url, data);
2017-10-31 14:54:13 +00:00
if (!response || response.error) {
let error = response && response.error;
2017-11-14 09:08:40 +00:00
// Remove the type prefix from the error message (e.g. "Error: ").
if (error && error.replace) {
error = error.replace(/^[a-z]+: /i, '');
}
else if (error.type && error.type === 'dict') {
const responseError = deserialize_json_value(error);
let responeMessage;
if (response.message && response.message !== '') {
// Remove the type prefix from the error message (e.g. "Error: ").
responeMessage = response.message.replace(/^[a-z]+: /i, '');
}
const exceptionToReport = new Error(responeMessage);
Object.assign(exceptionToReport, responseError);
throw exceptionToReport;
}
2017-10-02 14:10:58 +00:00
2017-11-14 09:08:40 +00:00
throw new Error(error || `Invalid response for "${command}"`);
}
2017-10-31 14:54:13 +00:00
let callback = response.callback;
if (callback != null) {
let result;
let error;
try {
let realmId = data.realmId;
let thisObject = deserialize(realmId, response.this);
let args = deserialize(realmId, response.arguments);
result = registeredCallbacks[callback].apply(thisObject, args);
result = serialize(realmId, result);
} catch (e) {
error = e.message || ('' + e);
}
2017-11-14 09:08:40 +00:00
2017-11-05 13:57:44 +00:00
let callbackCommand = "callback_result";
if (command == 'callbacks_poll') {
2017-11-14 09:08:40 +00:00
callbackCommand = "callback_poll_result";
2017-11-05 13:57:44 +00:00
}
2017-11-14 09:08:40 +00:00
return sendRequest(callbackCommand, { callback, result, error, "callback_call_counter": response.callback_call_counter });
}
2017-10-31 14:54:13 +00:00
return response.result;
}
finally {
2017-11-05 13:57:44 +00:00
pollTimeoutId = setTimeout(() => sendRequest('callbacks_poll'), 100);
2017-10-31 14:54:13 +00:00
}
}