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
This commit is contained in:
parent
8bc4e4f428
commit
0c9c5ac407
|
@ -39,6 +39,7 @@ export const propTypes = {};
|
|||
'OBJECT',
|
||||
'REALM',
|
||||
'RESULTS',
|
||||
'USER',
|
||||
'UNDEFINED',
|
||||
].forEach(function(type) {
|
||||
Object.defineProperty(objectTypes, type, {
|
||||
|
|
|
@ -24,8 +24,10 @@ import Collection, * as collections from './collections';
|
|||
import List, { createList } from './lists';
|
||||
import Results, { createResults } from './results';
|
||||
import RealmObject, * as objects from './objects';
|
||||
import User, { createUser } from './user';
|
||||
import * as rpc from './rpc';
|
||||
import * as util from './util';
|
||||
import { _authenticateRealm } from '../user-methods';
|
||||
|
||||
const {debugHosts, debugPort} = NativeModules.Realm;
|
||||
|
||||
|
@ -33,6 +35,7 @@ rpc.registerTypeConverter(objectTypes.LIST, createList);
|
|||
rpc.registerTypeConverter(objectTypes.RESULTS, createResults);
|
||||
rpc.registerTypeConverter(objectTypes.OBJECT, objects.createObject);
|
||||
rpc.registerTypeConverter(objectTypes.REALM, createRealm);
|
||||
rpc.registerTypeConverter(objectTypes.USER, createUser);
|
||||
|
||||
function createRealm(_, info) {
|
||||
let realm = Object.create(Realm.prototype);
|
||||
|
@ -127,6 +130,10 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
|||
'write',
|
||||
], true);
|
||||
|
||||
const Sync = {
|
||||
User
|
||||
};
|
||||
|
||||
Object.defineProperties(Realm, {
|
||||
Collection: {
|
||||
value: Collection,
|
||||
|
@ -140,6 +147,9 @@ Object.defineProperties(Realm, {
|
|||
Object: {
|
||||
value: RealmObject,
|
||||
},
|
||||
Sync: {
|
||||
value: Sync,
|
||||
},
|
||||
defaultPath: {
|
||||
get: util.getterForProperty('defaultPath'),
|
||||
set: util.setterForProperty('defaultPath'),
|
||||
|
@ -166,7 +176,7 @@ Object.defineProperties(Realm, {
|
|||
for (let i = 0, len = debugHosts.length; i < len; i++) {
|
||||
try {
|
||||
// The session ID refers to the Realm constructor object in the RPC server.
|
||||
Realm[keys.id] = rpc.createSession(debugHosts[i] + ':' + debugPort);
|
||||
Realm[keys.id] = rpc.createSession(_authenticateRealm, debugHosts[i] + ':' + debugPort);
|
||||
break;
|
||||
} catch (e) {
|
||||
// Only throw exception after all hosts have been tried.
|
||||
|
|
|
@ -22,9 +22,13 @@ import * as base64 from './base64';
|
|||
import { keys, objectTypes } from './constants';
|
||||
|
||||
const {id: idKey, realm: _realmKey} = keys;
|
||||
const registeredCallbacks = [];
|
||||
let registeredCallbacks = [];
|
||||
const typeConverters = {};
|
||||
|
||||
// Callbacks that are registered initially (currently only authenticateRealm) will
|
||||
// carry this symbol so they are not wiped in clearTestState.
|
||||
const persistentCallback = Symbol("persistentCallback");
|
||||
|
||||
let XMLHttpRequest = global.originalXMLHttpRequest || global.XMLHttpRequest;
|
||||
let sessionHost;
|
||||
let sessionId;
|
||||
|
@ -45,8 +49,9 @@ export function registerTypeConverter(type, handler) {
|
|||
typeConverters[type] = handler;
|
||||
}
|
||||
|
||||
export function createSession(host) {
|
||||
sessionId = sendRequest('create_session', null, host);
|
||||
export function createSession(authenticateRealm, host) {
|
||||
authenticateRealm[persistentCallback] = true;
|
||||
sessionId = sendRequest('create_session', { authenticateRealm: serialize(undefined, authenticateRealm) }, host);
|
||||
sessionHost = host;
|
||||
|
||||
return sessionId;
|
||||
|
@ -60,6 +65,12 @@ export function createRealm(args) {
|
|||
return sendRequest('create_realm', {arguments: args});
|
||||
}
|
||||
|
||||
export function createUser(args) {
|
||||
args = args.map((arg) => serialize(null, arg));
|
||||
const result = sendRequest('create_user', {arguments: args});
|
||||
return deserialize(undefined, result);
|
||||
}
|
||||
|
||||
export function callMethod(realmId, id, name, args) {
|
||||
if (args) {
|
||||
args = args.map((arg) => serialize(realmId, arg));
|
||||
|
@ -79,11 +90,16 @@ export function setProperty(realmId, id, name, value) {
|
|||
sendRequest('set_property', {realmId, id, name, value});
|
||||
}
|
||||
|
||||
export function getAllUsers() {
|
||||
let result = sendRequest('get_all_users');
|
||||
return deserialize(undefined, result);
|
||||
}
|
||||
|
||||
export function clearTestState() {
|
||||
sendRequest('clear_test_state');
|
||||
|
||||
// Clear all registered callbacks.
|
||||
registeredCallbacks.length = 0;
|
||||
// Clear all registered callbacks that are specific to this session.
|
||||
registeredCallbacks = registeredCallbacks.filter(cb => Reflect.has(cb, persistentCallback));
|
||||
}
|
||||
|
||||
function registerCallback(callback) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
'use strict';
|
||||
|
||||
import { createUser as createUserRPC, getAllUsers as getAllUsersRPC } from './rpc';
|
||||
import { keys, objectTypes } from './constants';
|
||||
import { createMethods } from './util';
|
||||
|
||||
export default class User {
|
||||
static createUser(server, identity, token, isAdmin) {
|
||||
return createUserRPC(Array.from(arguments));
|
||||
}
|
||||
|
||||
static get all() {
|
||||
return getAllUsersRPC();
|
||||
}
|
||||
}
|
||||
|
||||
createMethods(User.prototype, objectTypes.USER, [
|
||||
'logout'
|
||||
]);
|
||||
|
||||
export function createUser(realmId, info) {
|
||||
const userProxy = Object.create(User.prototype);
|
||||
|
||||
// FIXME: This is currently necessary because util/createMethod expects
|
||||
// the realm id to be present on any object that is used over rpc
|
||||
userProxy[keys.realm] = "(User object)";
|
||||
|
||||
userProxy[keys.id] = info.id;
|
||||
userProxy[keys.type] = objectTypes.USER;
|
||||
Object.assign(userProxy, info.data);
|
||||
|
||||
return userProxy;
|
||||
}
|
|
@ -18,11 +18,16 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
class AuthError extends Error {
|
||||
constructor(problem) {
|
||||
super(problem.title);
|
||||
Object.assign(this, problem);
|
||||
}
|
||||
function AuthError(problem) {
|
||||
var err = Error.apply(undefined, [problem.title]);
|
||||
Object.assign(err, problem);
|
||||
Object.setPrototypeOf(err, AuthError.prototype);
|
||||
Object.setPrototypeOf(this, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
AuthError.prototype = Object.create(Error.prototype, {
|
||||
constructor: { value: AuthError }
|
||||
});
|
||||
|
||||
exports['AuthError'] = AuthError;
|
||||
|
|
|
@ -6,33 +6,7 @@ function node_require(module) {
|
|||
return require(module);
|
||||
}
|
||||
|
||||
var post;
|
||||
if (typeof fetch !== 'undefined') {
|
||||
post = function(options, callback) {
|
||||
options.method = 'POST';
|
||||
// eslint-disable-next-line no-undef
|
||||
fetch(options.url, options)
|
||||
.then((response) => {
|
||||
if (response.status != 200) {
|
||||
callback(undefined, {statusCode: response.status});
|
||||
}
|
||||
else {
|
||||
return response.text();
|
||||
}
|
||||
})
|
||||
.then((body) => {
|
||||
callback(undefined, {statusCode: 200}, JSON.parse(body));
|
||||
})
|
||||
.catch((error) => {
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
post = function(options, callback) {
|
||||
node_require('needle').post(options.url, options.body, options, callback);
|
||||
}
|
||||
}
|
||||
const performFetch = typeof fetch === 'undefined' ? node_require('node-fetch') : fetch;
|
||||
|
||||
const url_parse = require("url-parse");
|
||||
|
||||
|
@ -48,29 +22,65 @@ function auth_url(server) {
|
|||
return server + 'auth';
|
||||
}
|
||||
|
||||
function authenticateRealm(user, fileUrl, realmUrl, callback) {
|
||||
var url = auth_url(user.server);
|
||||
var options = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
data: user.token,
|
||||
path: url_parse(realmUrl).pathname,
|
||||
provider: 'realm',
|
||||
app_id: ''
|
||||
}),
|
||||
headers: postHeaders
|
||||
};
|
||||
performFetch(url, options, function(error, response, body) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
}
|
||||
else if (response.statusCode != 200) {
|
||||
callback(new AuthError('Bad response: ' + response.statusCode));
|
||||
}
|
||||
else {
|
||||
// TODO: validate JSON
|
||||
|
||||
callback(undefined, {
|
||||
token: body.access_token.token,
|
||||
file_url: url_parse(fileUrl).pathname,
|
||||
resolved_realm_url: 'realm://' + url_parse(realmUrl).host + body.access_token.token_data.path
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function(realmConstructor) {
|
||||
function _authenticate(server, json, callback) {
|
||||
json.app_id = '';
|
||||
var url = auth_url(server);
|
||||
var options = {
|
||||
url: auth_url(server),
|
||||
method: 'POST',
|
||||
body: JSON.stringify(json),
|
||||
headers: postHeaders,
|
||||
open_timeout: 5000
|
||||
};
|
||||
post(options, function(error, response, body) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
}
|
||||
else if (response.statusCode != 200) {
|
||||
callback(new AuthError(JSON.parse(body)));
|
||||
}
|
||||
else {
|
||||
// TODO: validate JSON
|
||||
const token = body.refresh_token.token;
|
||||
const identity = body.refresh_token.token_data.identity;
|
||||
callback(undefined, realmConstructor.Sync.User.createUser(server, identity, token, false));
|
||||
}
|
||||
});
|
||||
performFetch(url, options)
|
||||
.then(function(response) {
|
||||
if (response.status !== 200) {
|
||||
return response.json().then(function (body) {
|
||||
callback(new AuthError(body));
|
||||
});
|
||||
} else {
|
||||
return response.json().then(function (body) {
|
||||
// TODO: validate JSON
|
||||
const token = body.refresh_token.token;
|
||||
const identity = body.refresh_token.token_data.identity;
|
||||
callback(undefined, realmConstructor.Sync.User.createUser(server, identity, token, false));
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
var methods = {};
|
||||
|
@ -106,39 +116,27 @@ module.exports = function(realmConstructor) {
|
|||
}, callback);
|
||||
}
|
||||
|
||||
methods['_authenticateRealm'] = function(fileUrl, realmUrl, callback) {
|
||||
var options = {
|
||||
url: auth_url(this.server),
|
||||
body: JSON.stringify({
|
||||
data: this.token,
|
||||
path: url_parse(realmUrl).pathname,
|
||||
provider: 'realm',
|
||||
app_id: ''
|
||||
}),
|
||||
headers: postHeaders
|
||||
};
|
||||
post(options, function(error, response, body) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
}
|
||||
else if (response.statusCode != 200) {
|
||||
callback(new AuthError('Bad response: ' + response.statusCode));
|
||||
}
|
||||
else {
|
||||
// TODO: validate JSON
|
||||
|
||||
callback(undefined, {
|
||||
token: body.access_token.token,
|
||||
file_url: url_parse(fileUrl).pathname,
|
||||
resolved_realm_url: 'realm://' + url_parse(realmUrl).host + body.access_token.token_data.path
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
methods['_authenticateRealm'] = authenticateRealm;
|
||||
|
||||
for (var name in methods) {
|
||||
methods[name] = {value: methods[name], configurable: true, writable: true}
|
||||
}
|
||||
|
||||
methods['current'] = {
|
||||
get: function () {
|
||||
const allUsers = realmConstructor.Sync.User.all;
|
||||
const keys = Object.keys(allUsers);
|
||||
if (keys.length === 0) {
|
||||
return undefined;
|
||||
} else if (keys.length > 1) {
|
||||
throw new Error("Multiple users are logged in");
|
||||
}
|
||||
|
||||
return allUsers[keys[0]];
|
||||
}
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports._authenticateRealm = authenticateRealm;
|
|
@ -54,7 +54,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"nan": "^2.3.3",
|
||||
"needle": "^1.3.0",
|
||||
"node-fetch": "^1.6.3",
|
||||
"node-pre-gyp": "^0.6.30",
|
||||
"request": "^2.78.0",
|
||||
"sync-request": "^3.0.1",
|
||||
|
|
|
@ -253,10 +253,15 @@ case "$TARGET" in
|
|||
npm run jsdoc
|
||||
;;
|
||||
"realmjs")
|
||||
download_server
|
||||
start_server
|
||||
pushd src
|
||||
xctest RealmJS
|
||||
stop_server
|
||||
;;
|
||||
"react-tests")
|
||||
download_server
|
||||
start_server
|
||||
pushd tests/react-test-app
|
||||
npm install
|
||||
open_chrome
|
||||
|
@ -264,6 +269,7 @@ case "$TARGET" in
|
|||
|
||||
pushd ios
|
||||
xctest ReactTestApp
|
||||
stop_server
|
||||
;;
|
||||
"react-example")
|
||||
pushd examples/ReactExample
|
||||
|
@ -276,6 +282,8 @@ case "$TARGET" in
|
|||
xctest ReactExample
|
||||
;;
|
||||
"react-tests-android")
|
||||
# download_server
|
||||
# start_server
|
||||
[[ $CONFIGURATION == 'Debug' ]] && exit 0
|
||||
XCPRETTY=''
|
||||
|
||||
|
@ -309,6 +317,7 @@ case "$TARGET" in
|
|||
echo "********* TESTS COMPLETED *********";
|
||||
echo "********* File location: $(pwd)/tests.xml *********";
|
||||
cat tests.xml
|
||||
# stop_server
|
||||
;;
|
||||
"node")
|
||||
if [ "$(uname)" = 'Darwin' ]; then
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
#include "platform.hpp"
|
||||
#include "realm_coordinator.hpp"
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
#include "sync/sync_manager.hpp"
|
||||
#include "sync/sync_user.hpp"
|
||||
#endif
|
||||
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
|
@ -40,5 +45,16 @@ void delete_all_realms() {
|
|||
realm::remove_realm_files_from_directory(realm::default_realm_file_directory());
|
||||
}
|
||||
|
||||
void clear_test_state() {
|
||||
delete_all_realms();
|
||||
#if REALM_ENABLE_SYNC
|
||||
for(auto &user : SyncManager::shared().all_logged_in_users()) {
|
||||
user->log_out();
|
||||
}
|
||||
SyncManager::shared().reset_for_testing();
|
||||
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // js
|
||||
} // realm
|
||||
|
|
|
@ -132,6 +132,7 @@ class RealmDelegate : public BindingContext {
|
|||
std::string default_path();
|
||||
void set_default_path(std::string path);
|
||||
void delete_all_realms();
|
||||
void clear_test_state();
|
||||
|
||||
template<typename T>
|
||||
class RealmClass : public ClassDefinition<T, SharedRealm, ObservableClass<T>> {
|
||||
|
@ -455,14 +456,7 @@ template<typename T>
|
|||
void RealmClass<T>::clear_test_state(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 0);
|
||||
|
||||
delete_all_realms();
|
||||
#if REALM_ENABLE_SYNC
|
||||
for(auto &user : SyncManager::shared().all_logged_in_users()) {
|
||||
user->log_out();
|
||||
}
|
||||
SyncManager::shared().reset_for_testing();
|
||||
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
|
||||
#endif
|
||||
js::clear_test_state();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
|
@ -75,11 +75,11 @@ public:
|
|||
{"createUser", wrap<create_user>}
|
||||
};
|
||||
|
||||
static void current_user(ContextType ctx, ObjectType object, ReturnValue &return_value);
|
||||
/*static void current_user(ContextType ctx, ObjectType object, ReturnValue &return_value);*/
|
||||
static void all_users(ContextType ctx, ObjectType object, ReturnValue &return_value);
|
||||
|
||||
PropertyMap<T> const static_properties = {
|
||||
{"current", {wrap<current_user>, nullptr}},
|
||||
/*{"current", {wrap<current_user>, nullptr}},*/
|
||||
{"all", {wrap<all_users>, nullptr}},
|
||||
};
|
||||
|
||||
|
@ -135,6 +135,7 @@ void UserClass<T>::all_users(ContextType ctx, ObjectType object, ReturnValue &re
|
|||
return_value.set(users);
|
||||
}
|
||||
|
||||
/*
|
||||
template<typename T>
|
||||
void UserClass<T>::current_user(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
SharedUser *current = nullptr;
|
||||
|
@ -154,6 +155,7 @@ void UserClass<T>::current_user(ContextType ctx, ObjectType object, ReturnValue
|
|||
return_value.set_undefined();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
void UserClass<T>::logout(ContextType ctx, ObjectType object, size_t, const ValueType[], ReturnValue &) {
|
||||
|
@ -267,15 +269,27 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
|||
|
||||
EventLoopDispatcher<SyncBindSessionHandler> bind([=](const std::string& path, const realm::SyncConfig& config, std::shared_ptr<SyncSession>) {
|
||||
HANDLESCOPE
|
||||
ObjectType user_constructor = Object::validated_get_object(protected_ctx, protected_sync, std::string("User"));
|
||||
FunctionType authenticate = Object::validated_get_function(protected_ctx, user_constructor, std::string("_authenticateRealm"));
|
||||
if (config.user->is_admin()) {
|
||||
// FIXME: This log-in callback is called while the object store still holds some sync-related locks.
|
||||
// Notify the object store of the access token asynchronously to avoid the deadlock that would result
|
||||
// from reentering the object store here.
|
||||
auto thread = std::thread([path, config]{
|
||||
auto session = SyncManager::shared().get_existing_active_session(path);
|
||||
session->refresh_access_token(config.user->refresh_token(), config.realm_url);
|
||||
});
|
||||
thread.detach();
|
||||
}
|
||||
else {
|
||||
ObjectType user_constructor = Object::validated_get_object(ctx, protected_sync, std::string("User"));
|
||||
FunctionType authenticate = Object::validated_get_function(ctx, user_constructor, std::string("_authenticateRealm"));
|
||||
|
||||
ValueType arguments[3];
|
||||
arguments[0] = Value::from_string(protected_ctx, path.c_str());
|
||||
arguments[1] = Value::from_string(protected_ctx, config.realm_url.c_str());
|
||||
arguments[2] = refresh;
|
||||
ObjectType user = create_object<T, UserClass<T>>(protected_ctx, new SharedUser(config.user));
|
||||
Function::call(protected_ctx, authenticate, user, 3, arguments);
|
||||
ValueType arguments[4];
|
||||
arguments[0] = create_object<T, UserClass<T>>(ctx, new SharedUser(config.user));
|
||||
arguments[1] = Value::from_string(protected_ctx, path.c_str());
|
||||
arguments[2] = Value::from_string(protected_ctx, config.realm_url.c_str());
|
||||
arguments[3] = refresh;
|
||||
Function::call(protected_ctx, authenticate, 4, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
ObjectType user = Object::validated_get_object(ctx, sync_config_object, "user");
|
||||
|
|
55
src/rpc.cpp
55
src/rpc.cpp
|
@ -42,6 +42,7 @@ static const char * const RealmObjectTypesList = "list";
|
|||
static const char * const RealmObjectTypesObject = "object";
|
||||
static const char * const RealmObjectTypesResults = "results";
|
||||
static const char * const RealmObjectTypesRealm = "realm";
|
||||
static const char * const RealmObjectTypesUser = "user";
|
||||
static const char * const RealmObjectTypesUndefined = "undefined";
|
||||
|
||||
static RPCServer*& get_rpc_server(JSGlobalContextRef ctx) {
|
||||
|
@ -111,6 +112,11 @@ RPCServer::RPCServer() {
|
|||
|
||||
jsc::String realm_string = "Realm";
|
||||
JSObjectRef realm_constructor = jsc::Object::validated_get_constructor(m_context, JSContextGetGlobalObject(m_context), realm_string);
|
||||
JSValueRef authenticateRealm = deserialize_json_value(dict["authenticateRealm"]);
|
||||
|
||||
JSObjectRef sync_constructor = (JSObjectRef)jsc::Object::get_property(m_context, realm_constructor, "Sync");
|
||||
JSObjectRef user_constructor = (JSObjectRef)jsc::Object::get_property(m_context, sync_constructor, "User");
|
||||
jsc::Object::set_property(m_context, user_constructor, "_authenticateRealm", authenticateRealm);
|
||||
|
||||
m_session_id = store_object(realm_constructor);
|
||||
return (json){{"result", m_session_id}};
|
||||
|
@ -133,6 +139,27 @@ RPCServer::RPCServer() {
|
|||
RPCObjectID realm_id = store_object(realm_object);
|
||||
return (json){{"result", realm_id}};
|
||||
};
|
||||
m_requests["/create_user"] = [this](const json dict) {
|
||||
JSObjectRef realm_constructor = m_session_id ? JSObjectRef(m_objects[m_session_id]) : NULL;
|
||||
if (!realm_constructor) {
|
||||
throw std::runtime_error("Realm constructor not found!");
|
||||
}
|
||||
|
||||
JSObjectRef sync_constructor = (JSObjectRef)jsc::Object::get_property(m_context, realm_constructor, "Sync");
|
||||
JSObjectRef user_constructor = (JSObjectRef)jsc::Object::get_property(m_context, sync_constructor, "User");
|
||||
JSObjectRef create_user_method = (JSObjectRef)jsc::Object::get_property(m_context, user_constructor, "createUser");
|
||||
|
||||
json::array_t args = dict["arguments"];
|
||||
size_t arg_count = args.size();
|
||||
JSValueRef arg_values[arg_count];
|
||||
|
||||
for (size_t i = 0; i < arg_count; i++) {
|
||||
arg_values[i] = deserialize_json_value(args[i]);
|
||||
}
|
||||
|
||||
JSObjectRef user_object = (JSObjectRef)jsc::Function::call(m_context, create_user_method, arg_count, arg_values);
|
||||
return (json){{"result", serialize_json_value(user_object)}};
|
||||
};
|
||||
m_requests["/call_method"] = [this](const json dict) {
|
||||
JSObjectRef object = m_objects[dict["id"].get<RPCObjectID>()];
|
||||
std::string method_string = dict["name"].get<std::string>();
|
||||
|
@ -181,6 +208,18 @@ RPCServer::RPCServer() {
|
|||
m_objects.erase(oid);
|
||||
return json::object();
|
||||
};
|
||||
m_requests["/get_all_users"] = [this](const json dict) {
|
||||
JSObjectRef realm_constructor = m_session_id ? JSObjectRef(m_objects[m_session_id]) : NULL;
|
||||
if (!realm_constructor) {
|
||||
throw std::runtime_error("Realm constructor not found!");
|
||||
}
|
||||
|
||||
JSObjectRef sync_constructor = (JSObjectRef)jsc::Object::get_property(m_context, realm_constructor, "Sync");
|
||||
JSObjectRef user_constructor = (JSObjectRef)jsc::Object::get_property(m_context, sync_constructor, "User");
|
||||
JSValueRef value = jsc::Object::get_property(m_context, user_constructor, "all");
|
||||
|
||||
return (json){{"result", serialize_json_value(value)}};
|
||||
};
|
||||
m_requests["/clear_test_state"] = [this](const json dict) {
|
||||
// The session ID points to the Realm constructor object, which should remain.
|
||||
auto realm_constructor = m_objects[m_session_id];
|
||||
|
@ -192,7 +231,7 @@ RPCServer::RPCServer() {
|
|||
|
||||
m_callbacks.clear();
|
||||
JSGarbageCollect(m_context);
|
||||
js::delete_all_realms();
|
||||
js::clear_test_state();
|
||||
|
||||
return json::object();
|
||||
};
|
||||
|
@ -336,6 +375,20 @@ json RPCServer::serialize_json_value(JSValueRef js_value) {
|
|||
{"id", store_object(js_object)},
|
||||
};
|
||||
}
|
||||
else if (jsc::Object::is_instance<js::UserClass<jsc::Types>>(m_context, js_object)) {
|
||||
auto user = *jsc::Object::get_internal<js::UserClass<jsc::Types>>(js_object);
|
||||
json user_dict {
|
||||
{"identity", user->identity()},
|
||||
{"token", user->refresh_token()},
|
||||
{"server", user->server_url()},
|
||||
{"isAdmin", user->is_admin()}
|
||||
};
|
||||
return {
|
||||
{"type", RealmObjectTypesUser},
|
||||
{"id", store_object(js_object)},
|
||||
{"data", user_dict}
|
||||
};
|
||||
}
|
||||
else if (jsc::Value::is_array(m_context, js_object)) {
|
||||
uint32_t length = jsc::Object::validated_get_length(m_context, js_object);
|
||||
std::vector<json> array;
|
||||
|
|
|
@ -27,9 +27,21 @@ var TESTS = {
|
|||
ResultsTests: require('./results-tests'),
|
||||
QueryTests: require('./query-tests'),
|
||||
EncryptionTests: require('./encryption-tests'),
|
||||
MigrationTests: require('./migration-tests'),
|
||||
MigrationTests: require('./migration-tests')
|
||||
};
|
||||
|
||||
// If sync is enabled, run the user tests
|
||||
if (Realm.Sync) {
|
||||
TESTS.UserTests = require('./user-tests');
|
||||
}
|
||||
|
||||
function node_require(module) { return require(module); }
|
||||
|
||||
// If on node, run the async tests
|
||||
if (typeof process === 'object' && process + '' === '[object process]') {
|
||||
TESTS.AsyncTests = node_require('./async-tests');
|
||||
}
|
||||
|
||||
var SPECIAL_METHODS = {
|
||||
beforeEach: true,
|
||||
afterEach: true,
|
||||
|
|
|
@ -382,5 +382,30 @@ module.exports = {
|
|||
TestCase.assertEqual(objects.length, 0);
|
||||
TestCase.assertEqual(snapshot.length, 0);
|
||||
});
|
||||
},
|
||||
|
||||
testAddListener: function() {
|
||||
return new Promise((resolve, _reject) => {
|
||||
var realm = new Realm({ schema: [schemas.TestObject] });
|
||||
|
||||
realm.write(() => {
|
||||
realm.create('TestObject', { doubleCol: 1 });
|
||||
realm.create('TestObject', { doubleCol: 2 });
|
||||
realm.create('TestObject', { doubleCol: 3 });
|
||||
});
|
||||
|
||||
realm.objects('TestObject').addListener((testObjects, changes) => {
|
||||
// TODO: First notification is empty, so perform these
|
||||
// assertions on the second call. However, there is a race condition
|
||||
// in React Native, so find a way to do this in a robust way.
|
||||
//TestCase.assertEqual(testObjects.length, 4);
|
||||
//TestCase.assertEqual(changes.insertions.length, 1);
|
||||
resolve();
|
||||
});
|
||||
|
||||
realm.write(() => {
|
||||
realm.create('TestObject', { doubleCol: 1 });
|
||||
});
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
|
@ -47,10 +47,10 @@ function assertIsSameUser(value, user) {
|
|||
TestCase.assertEqual(value.isAdmin, user.isAdmin);
|
||||
}
|
||||
|
||||
function assertIsError(error, code) {
|
||||
function assertIsError(error, message) {
|
||||
TestCase.assertInstanceOf(error, Error, 'The API should return an Error');
|
||||
if (code) {
|
||||
TestCase.assertEqual(error.code, code);
|
||||
if (message) {
|
||||
TestCase.assertEqual(error.message, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,8 +87,8 @@ function callbackTest(requestFunc, callback) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
testLogout() {
|
||||
var username = uuid();
|
||||
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
|
||||
|
@ -131,7 +131,7 @@ module.exports = {
|
|||
TestCase.assertUndefined(user);
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
testRegisterMissingUsername() {
|
||||
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', undefined, 'password', callback), (error, user) => {
|
||||
|
@ -152,26 +152,29 @@ module.exports = {
|
|||
var username = uuid();
|
||||
// Because it waits for answer this takes some time..
|
||||
return callbackTest((callback) => Realm.Sync.User.register('http://fake_host.local', username, 'password', callback), (error, user) => {
|
||||
assertIsError(error, 'ECONNRESET');
|
||||
assertIsError(error);
|
||||
TestCase.assertUndefined(user);
|
||||
});
|
||||
},
|
||||
|
||||
testLogin() {
|
||||
var username = uuid();
|
||||
// Create user, logout the new user, then login
|
||||
return callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
|
||||
failOnError(error);
|
||||
user.logout();
|
||||
|
||||
Realm.Sync.User.login('http://localhost:9080', username, 'password', (error, user) => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
var username = uuid();
|
||||
// Create user, logout the new user, then login
|
||||
callbackTest((callback) => Realm.Sync.User.register('http://localhost:9080', username, 'password', callback), (error, user) => {
|
||||
failOnError(error);
|
||||
assertIsUser(user);
|
||||
user.logout();
|
||||
|
||||
// Can we open a realm with the logged-in user?
|
||||
var realm = new Realm({sync: {user: user, url: 'realm://localhost:9080/~/test'}});
|
||||
TestCase.assertInstanceOf(realm, Realm);
|
||||
realm.close();
|
||||
Realm.Sync.User.login('http://localhost:9080', username, 'password', (error, user) => {
|
||||
failOnError(error);
|
||||
assertIsUser(user);
|
||||
|
||||
// Can we open a realm with the logged-in user?
|
||||
var realm = new Realm({ sync: { user: user, url: 'realm://localhost:9080/~/test' } });
|
||||
TestCase.assertInstanceOf(realm, Realm);
|
||||
realm.close();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -200,10 +203,17 @@ module.exports = {
|
|||
|
||||
testLoginServerOffline() {
|
||||
var username = uuid();
|
||||
// Because it waits for answer this takes some time..
|
||||
return callbackTest((callback) => Realm.Sync.User.register('http://fake_host.local', username, 'password', callback), (error, user) => {
|
||||
assertIsError(error);
|
||||
TestCase.assertUndefined(user);
|
||||
|
||||
// Because it waits for answer this takes some time..
|
||||
return new Promise((resolve, reject) => {
|
||||
Realm.Sync.User.register('http://fake_host.local', username, 'password', (error, user) => {
|
||||
try {
|
||||
assertIsError(error);
|
||||
TestCase.assertUndefined(user);
|
||||
resolve();
|
||||
}
|
||||
catch (e) { reject(e) }
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -266,7 +276,7 @@ module.exports = {
|
|||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
/* This test fails because of realm-object-store #243 . We should use 2 users.
|
||||
|
||||
testSynchronizeChangesWithTwoClientsAndOneUser() {
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* eslint-env es6, node */
|
||||
/* eslint-disable no-console */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Realm = require('realm');
|
||||
|
||||
const userTests = require('../js/user-tests');
|
||||
describe('SyncTests', () => {
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000;
|
||||
beforeEach(() => Realm.clearTestState());
|
||||
afterEach(() => Realm.clearTestState());
|
||||
|
||||
for (const testName in userTests) {
|
||||
it(testName, (done) => {
|
||||
userTests[testName]()
|
||||
.catch((e) => fail(e))
|
||||
.then(done);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -27,6 +27,8 @@ const path = require('path');
|
|||
const Realm = require('realm');
|
||||
const RealmTests = require('../js');
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
|
||||
|
||||
// Create this method with appropriate implementation for Node testing.
|
||||
Realm.copyBundledRealmFiles = function() {
|
||||
let sourceDir = path.join(__dirname, '../data');
|
||||
|
@ -52,9 +54,14 @@ for (const suiteName in tests) {
|
|||
beforeEach(() => RealmTests.runTest(suiteName, 'beforeEach'));
|
||||
|
||||
for (const testName of tests[suiteName]) {
|
||||
it(testName, () => {
|
||||
it(testName, (done) => {
|
||||
try {
|
||||
RealmTests.runTest(suiteName, testName)
|
||||
let result = RealmTests.runTest(suiteName, testName);
|
||||
if (result instanceof Promise) {
|
||||
result.then(done, fail);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
fail(e);
|
||||
|
@ -65,14 +72,3 @@ for (const suiteName in tests) {
|
|||
afterEach(() => RealmTests.runTest(suiteName, 'afterEach'));
|
||||
});
|
||||
}
|
||||
|
||||
const asyncTests = require('../js/async-tests');
|
||||
describe('AsyncTests', () => {
|
||||
beforeEach(() => Realm.clearTestState());
|
||||
|
||||
for (const testName in asyncTests) {
|
||||
it(testName, (done) => asyncTests[testName]().catch((e) => fail(e)).then(done));
|
||||
}
|
||||
|
||||
afterEach(() => Realm.clearTestState());
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue