realm-js/lib/rpc.js

178 lines
4.4 KiB
JavaScript

/* Copyright 2015 Realm Inc - All Rights Reserved
* Proprietary and Confidential
*/
'use strict';
const base64 = require('./base64');
const constants = require('./constants');
const DEVICE_HOST = 'localhost:8082';
const {keys, objectTypes, propTypes} = constants;
const typeConverters = {};
let XMLHttpRequest = global.originalXMLHttpRequest || global.XMLHttpRequest;
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;
}
module.exports = {
registerTypeConverter,
createSession,
createRealm,
callMethod,
getProperty,
setProperty,
beginTransaction,
cancelTransaction,
commitTransaction,
clearTestState,
};
registerTypeConverter(propTypes.DATA, (_, {value}) => base64.decode(value));
registerTypeConverter(propTypes.DATE, (_, {value}) => new Date(value));
function registerTypeConverter(type, handler) {
typeConverters[type] = handler;
}
function createSession() {
sessionId = sendRequest('create_session');
return sessionId;
}
function createRealm(args) {
if (args) {
args = args.map((arg) => serialize(null, arg));
}
return sendRequest('create_realm', {arguments: args});
}
function callMethod(realmId, id, name, args) {
if (args) {
args = args.map((arg) => serialize(realmId, arg));
}
let result = sendRequest('call_method', {realmId, id, name, arguments: args});
return deserialize(realmId, result);
}
function getProperty(realmId, id, name) {
let result = sendRequest('get_property', {realmId, id, name});
return deserialize(realmId, result);
}
function setProperty(realmId, id, name, value) {
value = serialize(realmId, value);
sendRequest('set_property', {realmId, id, name, value});
}
function beginTransaction(realmId) {
sendRequest('begin_transaction', {realmId});
}
function cancelTransaction(realmId) {
sendRequest('cancel_transaction', {realmId});
}
function commitTransaction(realmId) {
sendRequest('commit_transaction', {realmId});
}
function clearTestState() {
sendRequest('clear_test_state');
}
function serialize(realmId, value) {
if (typeof value == 'function') {
return {type: objectTypes.FUNCTION};
}
if (typeof value === 'undefined') {
return {type: 'undefined'};
}
if (!value || typeof value != 'object') {
return {value: value};
}
let id = value[keys.id];
if (id) {
if (value[keys.realm] != realmId) {
throw new Error('Unable to serialize value from another Realm');
}
return {id};
}
if (value instanceof Date) {
return {type: propTypes.DATE, value: value.getTime()};
}
if (Array.isArray(value)) {
let array = value.map((item) => serialize(realmId, item));
return {value: array};
}
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
if (value instanceof DataView) {
throw new Error('Unable to serialize a DataView object');
}
return {type: propTypes.DATA, value: base64.encode(value)};
}
let object = {};
for (let key in value) {
object[key] = serialize(realmId, value[key]);
}
return {value: object};
}
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 sendRequest(command, data) {
data = Object.assign({}, data, sessionId ? {sessionId} : null);
let body = JSON.stringify(data);
let request = new XMLHttpRequest();
let url = 'http://' + DEVICE_HOST + '/' + command;
request.open('POST', url, false);
request.send(body);
if (request.status != 200) {
throw new Error(request.responseText);
}
let response = JSON.parse(request.responseText);
if (!response || response.error) {
throw new Error((response && response.error) || 'Invalid response for "' + command + '"');
}
return response.result;
}