mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-11 06:46:03 +00:00
Sync Session API (#825)
* Add Session class and expose it to JS * Add error property on the sync config object for the event handler * tests * refactor access token refresh * chrome debugging
This commit is contained in:
parent
408f5588f8
commit
9d1d970b1f
@ -40,6 +40,7 @@ export const propTypes = {};
|
||||
'REALM',
|
||||
'RESULTS',
|
||||
'USER',
|
||||
'SESSION',
|
||||
'UNDEFINED',
|
||||
].forEach(function(type) {
|
||||
Object.defineProperty(objectTypes, type, {
|
||||
|
@ -25,6 +25,7 @@ import List, { createList } from './lists';
|
||||
import Results, { createResults } from './results';
|
||||
import RealmObject, * as objects from './objects';
|
||||
import User, { createUser } from './user';
|
||||
import Session, { createSession } from './session';
|
||||
import * as rpc from './rpc';
|
||||
import * as util from './util';
|
||||
import { static as staticUserMethods } from '../user-methods';
|
||||
@ -36,6 +37,7 @@ rpc.registerTypeConverter(objectTypes.RESULTS, createResults);
|
||||
rpc.registerTypeConverter(objectTypes.OBJECT, objects.createObject);
|
||||
rpc.registerTypeConverter(objectTypes.REALM, createRealm);
|
||||
rpc.registerTypeConverter(objectTypes.USER, createUser);
|
||||
rpc.registerTypeConverter(objectTypes.SESSION, createSession);
|
||||
|
||||
function createRealm(_, info) {
|
||||
let realm = Object.create(Realm.prototype);
|
||||
@ -54,6 +56,7 @@ function setupRealm(realm, realmId) {
|
||||
'readOnly',
|
||||
'schema',
|
||||
'schemaVersion',
|
||||
'syncSession',
|
||||
].forEach((name) => {
|
||||
Object.defineProperty(realm, name, {get: util.getterForProperty(name)});
|
||||
});
|
||||
@ -131,7 +134,8 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [
|
||||
], true);
|
||||
|
||||
const Sync = {
|
||||
User
|
||||
User,
|
||||
Session
|
||||
};
|
||||
|
||||
Object.defineProperties(Realm, {
|
||||
|
48
lib/browser/session.js
Normal file
48
lib/browser/session.js
Normal file
@ -0,0 +1,48 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 { keys, objectTypes } from './constants';
|
||||
import { getterForProperty, createMethods } from './util';
|
||||
|
||||
export default class Session { }
|
||||
|
||||
Object.defineProperties(Session.prototype, {
|
||||
url: getterForProperty('url'),
|
||||
state: getterForProperty('state')
|
||||
});
|
||||
|
||||
createMethods(Session.prototype, objectTypes.SESSION, [
|
||||
'_refreshAccessToken',
|
||||
'_simulateError'
|
||||
]);
|
||||
|
||||
export function createSession(realmId, info) {
|
||||
let sessionProxy = Object.create(Session.prototype);
|
||||
|
||||
// FIXME: This is currently necessary because util/createMethod expects
|
||||
// the realm id to be present on any object that is used over rpc
|
||||
sessionProxy[keys.realm] = "(Session object)";
|
||||
|
||||
sessionProxy[keys.id] = info.id;
|
||||
sessionProxy[keys.type] = objectTypes.SESSION;
|
||||
Object.assign(sessionProxy, info.data);
|
||||
|
||||
return sessionProxy;
|
||||
}
|
@ -34,7 +34,8 @@ export default class User {
|
||||
}
|
||||
|
||||
createMethods(User.prototype, objectTypes.USER, [
|
||||
'logout'
|
||||
'logout',
|
||||
'_sessionForOnDiskPath'
|
||||
]);
|
||||
|
||||
export function createUser(realmId, info) {
|
||||
|
@ -40,35 +40,31 @@ function auth_url(server) {
|
||||
return server + 'auth';
|
||||
}
|
||||
|
||||
function authenticateRealm(user, fileUrl, realmUrl, callback) {
|
||||
var url = auth_url(user.server);
|
||||
var options = {
|
||||
function authenticateRealm(user, fileUrl, realmUrl) {
|
||||
let parsedRealmUrl = url_parse(realmUrl);
|
||||
const url = auth_url(user.server);
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
data: user.token,
|
||||
path: url_parse(realmUrl).pathname,
|
||||
path: parsedRealmUrl.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
|
||||
});
|
||||
}
|
||||
});
|
||||
performFetch(url, options)
|
||||
.then((response) => {
|
||||
if (response.status != 200) {
|
||||
//FIXME: propagate error to session error handler
|
||||
} else {
|
||||
return response.json().then((body) => {
|
||||
parsedRealmUrl.set('pathname', body.access_token.token_data.path);
|
||||
let session = user._sessionForOnDiskPath(fileUrl);
|
||||
session._refreshAccessToken(body.access_token.token, parsedRealmUrl.href);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _authenticate(userConstructor, server, json, callback) {
|
||||
|
@ -6,9 +6,8 @@ set -eo pipefail
|
||||
|
||||
. dependencies.list
|
||||
|
||||
if [ -f object-server-for-testing/node_modules/realm-object-server/CHANGELOG.md ]; then
|
||||
current_version=$(head -n1 object-server-for-testing/node_modules/realm-object-server/CHANGELOG.md | cut -d" " -f2)
|
||||
if [ "$REALM_OBJECT_SERVER_VERSION" = "$current_version" ]; then
|
||||
if [ -f object-server-for-testing/node_modules/realm-object-server/package.json ]; then
|
||||
if grep -q "\"version\": \"$REALM_OBJECT_SERVER_VERSION\"" object-server-for-testing/node_modules/realm-object-server/package.json; then
|
||||
echo -e "yes\n" | object-server-for-testing/reset-server-realms.command
|
||||
exit
|
||||
fi
|
||||
|
@ -167,6 +167,9 @@ public:
|
||||
static void get_schema_version(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_schema(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_read_only(ContextType, ObjectType, ReturnValue &);
|
||||
#if REALM_ENABLE_SYNC
|
||||
static void get_sync_session(ContextType, ObjectType, ReturnValue &);
|
||||
#endif
|
||||
|
||||
// static methods
|
||||
static void constructor(ContextType, ObjectType, size_t, const ValueType[]);
|
||||
@ -208,6 +211,9 @@ public:
|
||||
{"schemaVersion", {wrap<get_schema_version>, nullptr}},
|
||||
{"schema", {wrap<get_schema>, nullptr}},
|
||||
{"readOnly", {wrap<get_read_only>, nullptr}},
|
||||
#if REALM_ENABLE_SYNC
|
||||
{"syncSession", {wrap<get_sync_session>, nullptr}},
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
@ -498,6 +504,19 @@ void RealmClass<T>::get_read_only(ContextType ctx, ObjectType object, ReturnValu
|
||||
return_value.set(get_internal<T, RealmClass<T>>(object)->get()->config().read_only());
|
||||
}
|
||||
|
||||
#if REALM_ENABLE_SYNC
|
||||
template<typename T>
|
||||
void RealmClass<T>::get_sync_session(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
auto realm = *get_internal<T, RealmClass<T>>(object);
|
||||
if (std::shared_ptr<SyncSession> session = SyncManager::shared().get_existing_active_session(realm->config().path)) {
|
||||
return_value.set(create_object<T, SessionClass<T>>(ctx, new WeakSession(session)));
|
||||
} else {
|
||||
return_value.set_null();
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void RealmClass<T>::objects(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
|
189
src/js_sync.hpp
189
src/js_sync.hpp
@ -36,7 +36,8 @@
|
||||
namespace realm {
|
||||
namespace js {
|
||||
|
||||
using SharedUser = std::shared_ptr<realm::SyncUser>;
|
||||
using SharedUser = std::shared_ptr<realm::SyncUser>;
|
||||
using WeakSession = std::weak_ptr<realm::SyncSession>;
|
||||
|
||||
template<typename T>
|
||||
class UserClass : public ClassDefinition<T, SharedUser> {
|
||||
@ -84,9 +85,11 @@ public:
|
||||
};
|
||||
|
||||
static void logout(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void session_for_on_disk_path(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
{"logout", wrap<logout>}
|
||||
{"logout", wrap<logout>},
|
||||
{"_sessionForOnDiskPath", wrap<session_for_on_disk_path>}
|
||||
};
|
||||
};
|
||||
|
||||
@ -162,6 +165,126 @@ void UserClass<T>::logout(ContextType ctx, ObjectType object, size_t, const Valu
|
||||
get_internal<T, UserClass<T>>(object)->get()->log_out();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class SessionClass : public ClassDefinition<T, WeakSession> {
|
||||
using ContextType = typename T::Context;
|
||||
using FunctionType = typename T::Function;
|
||||
using ObjectType = typename T::Object;
|
||||
using ValueType = typename T::Value;
|
||||
using String = js::String<T>;
|
||||
using Object = js::Object<T>;
|
||||
using Value = js::Value<T>;
|
||||
using ReturnValue = js::ReturnValue<T>;
|
||||
|
||||
public:
|
||||
std::string const name = "Session";
|
||||
|
||||
static FunctionType create_constructor(ContextType);
|
||||
|
||||
static void get_config(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_user(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_url(ContextType, ObjectType, ReturnValue &);
|
||||
static void get_state(ContextType, ObjectType, ReturnValue &);
|
||||
|
||||
static void simulate_error(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void refresh_access_token(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
{"config", {wrap<get_config>, nullptr}},
|
||||
{"user", {wrap<get_user>, nullptr}},
|
||||
{"url", {wrap<get_url>, nullptr}},
|
||||
{"state", {wrap<get_state>, nullptr}}
|
||||
};
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
{"_simulateError", wrap<simulate_error>},
|
||||
{"_refreshAccessToken", wrap<refresh_access_token>}
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void UserClass<T>::session_for_on_disk_path(ContextType ctx, ObjectType object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
auto user = *get_internal<T, UserClass<T>>(object);
|
||||
if (auto session = user->session_for_on_disk_path(Value::validated_to_string(ctx, arguments[0]))) {
|
||||
return_value.set(create_object<T, SessionClass<T>>(ctx, new WeakSession(session)));
|
||||
} else {
|
||||
return_value.set_undefined();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SessionClass<T>::get_config(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
if (auto session = get_internal<T, SessionClass<T>>(object)->lock()) {
|
||||
ObjectType config = Object::create_empty(ctx);
|
||||
Object::set_property(ctx, config, "user", create_object<T, UserClass<T>>(ctx, new SharedUser(session->config().user)));
|
||||
Object::set_property(ctx, config, "url", Value::from_string(ctx, session->config().realm_url));
|
||||
return_value.set(config);
|
||||
} else {
|
||||
return_value.set_undefined();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SessionClass<T>::get_user(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
if (auto session = get_internal<T, SessionClass<T>>(object)->lock()) {
|
||||
return_value.set(create_object<T, UserClass<T>>(ctx, new SharedUser(session->config().user)));
|
||||
} else {
|
||||
return_value.set_undefined();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SessionClass<T>::get_url(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
if (auto session = get_internal<T, SessionClass<T>>(object)->lock()) {
|
||||
if (util::Optional<std::string> url = session->full_realm_url()) {
|
||||
return_value.set(*url);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return_value.set_undefined();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SessionClass<T>::get_state(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
static const std::string invalid("invalid");
|
||||
static const std::string inactive("inactive");
|
||||
static const std::string active("active");
|
||||
|
||||
return_value.set(invalid);
|
||||
|
||||
if (auto session = get_internal<T, SessionClass<T>>(object)->lock()) {
|
||||
if (session->state() == SyncSession::PublicState::Inactive) {
|
||||
return_value.set(inactive);
|
||||
} else if (session->state() != SyncSession::PublicState::Error) {
|
||||
return_value.set(active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SessionClass<T>::simulate_error(ContextType ctx, ObjectType object, size_t argc, const ValueType arguments[], ReturnValue &) {
|
||||
validate_argument_count(argc, 2);
|
||||
|
||||
if (auto session = get_internal<T, SessionClass<T>>(object)->lock()) {
|
||||
SyncError error;
|
||||
error.error_code = std::error_code(Value::validated_to_number(ctx, arguments[0]), realm::sync::protocol_error_category());
|
||||
error.message = Value::validated_to_string(ctx, arguments[1]);
|
||||
SyncSession::OnlyForTesting::handle_error(*session, std::move(error));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SessionClass<T>::refresh_access_token(ContextType ctx, ObjectType object, size_t argc, const ValueType arguments[], ReturnValue &) {
|
||||
validate_argument_count(argc, 2);
|
||||
|
||||
if (auto session = get_internal<T, SessionClass<T>>(object)->lock()) {
|
||||
std::string access_token = Value::validated_to_string(ctx, arguments[0], "accessToken");
|
||||
std::string realm_url = Value::validated_to_string(ctx, arguments[1], "realmUrl");
|
||||
session->refresh_access_token(std::move(access_token), std::move(realm_url));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class SyncClass : public ClassDefinition<T, void *> {
|
||||
using GlobalContextType = typename T::GlobalContext;
|
||||
@ -185,14 +308,12 @@ public:
|
||||
static void set_verify_servers_ssl_certificate(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
// private
|
||||
static void refresh_access_token(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void populate_sync_config(ContextType, ObjectType realm_constructor, ObjectType config_object, Realm::Config&);
|
||||
|
||||
// static properties
|
||||
static void get_is_developer_edition(ContextType, ObjectType, ReturnValue &);
|
||||
|
||||
MethodMap<T> const static_methods = {
|
||||
{"refreshAccessToken", wrap<refresh_access_token>},
|
||||
{"setLogLevel", wrap<set_sync_log_level>},
|
||||
{"setVerifyServersSslCertificate", wrap<set_verify_servers_ssl_certificate>}
|
||||
};
|
||||
@ -204,7 +325,8 @@ inline typename T::Function SyncClass<T>::create_constructor(ContextType ctx) {
|
||||
|
||||
PropertyAttributes attributes = ReadOnly | DontEnum | DontDelete;
|
||||
Object::set_property(ctx, sync_constructor, "User", ObjectWrap<T, UserClass<T>>::create_constructor(ctx), attributes);
|
||||
|
||||
Object::set_property(ctx, sync_constructor, "Session", ObjectWrap<T, SessionClass<T>>::create_constructor(ctx), attributes);
|
||||
|
||||
// setup synced realmFile paths
|
||||
ensure_directory_exists_for_file(default_realm_file_directory());
|
||||
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
|
||||
@ -233,28 +355,6 @@ void SyncClass<T>::set_verify_servers_ssl_certificate(ContextType ctx, ObjectTyp
|
||||
realm::SyncManager::shared().set_client_should_validate_ssl(verify_servers_ssl_certificate);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::refresh_access_token(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 2);
|
||||
|
||||
static const String token_string = "token";
|
||||
static const String file_url_string = "file_url";
|
||||
static const String realm_url_string = "resolved_realm_url";
|
||||
|
||||
ObjectType json_arguments = Value::validated_to_object(ctx, arguments[1]);
|
||||
std::string token = Object::validated_get_string(ctx, json_arguments, token_string);
|
||||
std::string file_url = Object::validated_get_string(ctx, json_arguments, file_url_string);
|
||||
std::string realm_url = Object::validated_get_string(ctx, json_arguments, realm_url_string);
|
||||
|
||||
if (auto session = SyncManager::shared().get_existing_active_session(file_url)) {
|
||||
session->refresh_access_token(token, realm_url);
|
||||
return_value.set(true);
|
||||
}
|
||||
else {
|
||||
return_value.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constructor, ObjectType config_object, Realm::Config& config)
|
||||
{
|
||||
@ -263,7 +363,6 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
||||
auto sync_config_object = Value::validated_to_object(ctx, sync_config_value);
|
||||
|
||||
ObjectType sync_constructor = Object::validated_get_object(ctx, realm_constructor, std::string("Sync"));
|
||||
Protected<ValueType> refresh(ctx, Object::validated_get_function(ctx, sync_constructor, std::string("refreshAccessToken")));
|
||||
Protected<ObjectType> protected_sync(ctx, sync_constructor);
|
||||
Protected<typename T::GlobalContext> protected_ctx(Context<T>::get_global_context(ctx));
|
||||
|
||||
@ -283,15 +382,41 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
||||
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[4];
|
||||
ValueType arguments[3];
|
||||
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);
|
||||
Function::call(protected_ctx, authenticate, 3, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
std::function<SyncSessionErrorHandler> error_handler;
|
||||
ValueType error_func = Object::get_property(ctx, sync_config_object, "error");
|
||||
if (!Value::is_undefined(ctx, error_func)) {
|
||||
Protected<FunctionType> protected_error_func(ctx, Value::validated_to_function(ctx, error_func));
|
||||
error_handler = EventLoopDispatcher<SyncSessionErrorHandler>([=](auto session, auto error) {
|
||||
HANDLESCOPE
|
||||
|
||||
ObjectType error_object = Object::create_empty(protected_ctx);
|
||||
Object::set_property(protected_ctx, error_object, "message", Value::from_string(protected_ctx, error.message));
|
||||
Object::set_property(protected_ctx, error_object, "isFatal", Value::from_boolean(protected_ctx, error.is_fatal));
|
||||
Object::set_property(protected_ctx, error_object, "category", Value::from_string(protected_ctx, error.error_code.category().name()));
|
||||
Object::set_property(protected_ctx, error_object, "code", Value::from_number(protected_ctx, error.error_code.value()));
|
||||
|
||||
ObjectType user_info = Object::create_empty(protected_ctx);
|
||||
for (auto& kvp : error.user_info) {
|
||||
Object::set_property(protected_ctx, user_info, kvp.first, Value::from_string(protected_ctx, kvp.second));
|
||||
}
|
||||
Object::set_property(protected_ctx, error_object, "userInfo", user_info);
|
||||
|
||||
ValueType arguments[2];
|
||||
arguments[0] = create_object<T, SessionClass<T>>(protected_ctx, new WeakSession(session));
|
||||
arguments[1] = error_object;
|
||||
|
||||
Function::call(protected_ctx, protected_error_func, 2, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
ObjectType user = Object::validated_get_object(ctx, sync_config_object, "user");
|
||||
SharedUser shared_user = *get_internal<T, UserClass<T>>(user);
|
||||
if (shared_user->state() != SyncUser::State::Active) {
|
||||
@ -303,7 +428,7 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
||||
// FIXME - use make_shared
|
||||
config.sync_config = std::shared_ptr<SyncConfig>(new SyncConfig{shared_user, raw_realm_url,
|
||||
SyncSessionStopPolicy::AfterChangesUploaded,
|
||||
std::move(bind), [=](auto, SyncError) {}});
|
||||
std::move(bind), std::move(error_handler)});
|
||||
config.schema_mode = SchemaMode::Additive;
|
||||
config.path = realm::SyncManager::shared().path_for_realm(shared_user->identity(), raw_realm_url);
|
||||
}
|
||||
|
12
src/rpc.cpp
12
src/rpc.cpp
@ -43,6 +43,7 @@ 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 RealmObjectTypesSession = "session";
|
||||
static const char * const RealmObjectTypesUndefined = "undefined";
|
||||
|
||||
static RPCServer*& get_rpc_server(JSGlobalContextRef ctx) {
|
||||
@ -389,6 +390,17 @@ json RPCServer::serialize_json_value(JSValueRef js_value) {
|
||||
{"data", user_dict}
|
||||
};
|
||||
}
|
||||
else if (jsc::Object::is_instance<js::SessionClass<jsc::Types>>(m_context, js_object)) {
|
||||
json session_dict {
|
||||
{"user", serialize_json_value(jsc::Object::get_property(m_context, js_object, "user"))},
|
||||
{"config", serialize_json_value(jsc::Object::get_property(m_context, js_object, "config"))}
|
||||
};
|
||||
return {
|
||||
{"type", RealmObjectTypesSession},
|
||||
{"id", store_object(js_object)},
|
||||
{"data", session_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;
|
||||
|
@ -142,6 +142,12 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
|
||||
assertNull: function(value, errorMessage) {
|
||||
if (value !== null) {
|
||||
throw new TestFailureError(errorMessage || `Value ${value} expected to be null`);
|
||||
}
|
||||
},
|
||||
|
||||
isNode: function() {
|
||||
// eslint-disable-next-line no-undef
|
||||
return typeof process == 'object' && Object.prototype.toString.call(process) == '[object process]';
|
||||
|
@ -33,6 +33,7 @@ var TESTS = {
|
||||
// If sync is enabled, run the user tests
|
||||
if (Realm.Sync) {
|
||||
TESTS.UserTests = require('./user-tests');
|
||||
TESTS.SessionTests = require('./session-tests');
|
||||
}
|
||||
|
||||
function node_require(module) { return require(module); }
|
||||
|
95
tests/js/session-tests.js
Normal file
95
tests/js/session-tests.js
Normal file
@ -0,0 +1,95 @@
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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 */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Realm = require('realm');
|
||||
const TestCase = require('./asserts');
|
||||
|
||||
function uuid() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
function promisifiedRegister(server, username, password) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Realm.Sync.User.register(server, username, password, (error, user) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(user);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function wait(delay) {
|
||||
return new Promise((resolve, reject) => setTimeout(resolve, delay));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
testLocalRealmHasNoSession() {
|
||||
let realm = new Realm();
|
||||
TestCase.assertNull(realm.syncSession);
|
||||
},
|
||||
|
||||
testProperties() {
|
||||
return promisifiedRegister('http://localhost:9080', uuid(), 'password').then(user => {
|
||||
let config = { sync: { user, url: 'realm://localhost:9080/~/myrealm' } };
|
||||
let realm = new Realm(config);
|
||||
let session = realm.syncSession;
|
||||
|
||||
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.assertUndefined(session.url);
|
||||
TestCase.assertEqual(session.state, 'active');
|
||||
|
||||
// give the session enough time to refresh its access token and bind itself
|
||||
return wait(500).then(() => {
|
||||
TestCase.assertEqual(session.url, `realm://localhost:9080/${user.identity}/myrealm`);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testErrorHandling() {
|
||||
return promisifiedRegister('http://localhost:9080', uuid(), 'password').then(user => {
|
||||
let errors = [];
|
||||
let config = { sync: { user,
|
||||
url: 'realm://localhost:9080/~/myrealm',
|
||||
error: (sender, error) => errors.push([sender, error])
|
||||
} };
|
||||
let realm = new Realm(config);
|
||||
let session = realm.syncSession;
|
||||
|
||||
session._simulateError(123, 'simulated error');
|
||||
|
||||
return wait(100).then(() => {
|
||||
TestCase.assertArrayLength(errors, 1);
|
||||
TestCase.assertEqual(errors[0][0].config.url, session.config.url);
|
||||
TestCase.assertEqual(errors[0][1].message, 'simulated error');
|
||||
TestCase.assertEqual(errors[0][1].code, 123);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user