Propagate token refresh errors to the session error handler (#843)

* Get a session’s error handler

* Change the signature of all methods to add the callee

* Deserialize rpc callbacks

* Expose the session error handle on its config property

* When a token refresh error occurs and there is a session error handler

propagate the error to the handler
This commit is contained in:
Yavor Georgiev 2017-02-07 11:01:26 +01:00 committed by GitHub
parent 59d3a89da0
commit 7c1dab7079
14 changed files with 176 additions and 138 deletions

View File

@ -44,6 +44,7 @@ if (XMLHttpRequest.__proto__ != global.XMLHttpRequestEventTarget) {
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;
@ -167,6 +168,10 @@ function deserializeDict(realmId, info) {
return object;
}
function deserializeFunction(realmId, info) {
return registeredCallbacks[info.value];
}
function makeRequest(url, data) {
let statusCode;
let responseText;
@ -223,8 +228,9 @@ function sendRequest(command, data, host = sessionHost) {
let error;
try {
let realmId = data.realmId;
let thisObject = deserialize(realmId, response.this);
let args = deserialize(realmId, response.arguments);
result = registeredCallbacks[callback].apply(null, args);
result = registeredCallbacks[callback].apply(thisObject, args);
result = serialize(realmId, result);
} catch (e) {
error = e.message || ('' + e);

View File

@ -66,23 +66,22 @@ function refreshAccessToken(user, localRealmPath, realmUrl) {
.then((responseAndJson) => {
const response = responseAndJson.response;
const json = responseAndJson.json;
if (response.status != 200) {
//FIXME: propagate error to session error handler
/*
let session = user._sessionForOnDiskPath(localRealmPath);
let errorHandler = session._errorHandler;
if (errorHandler) {
errorHandler(session, new AuthError(json));
}
*/
} else {
// Look up a fresh instance of the user.
// We do this because in React Native Remote Debugging
// `Realm.clearTestState()` will have invalidated the user object
let newUser = user.constructor.all[user.identity];
if (newUser) {
let session = newUser._sessionForOnDiskPath(localRealmPath);
if (session && session.state !== 'invalid') {
if (session) {
if (response.status != 200) {
let errorHandler = session.config.error;
let error = new AuthError(json);
if (errorHandler) {
errorHandler(session, error);
} else {
(console.error || console.log).call(console, `Unhandled session token refresh error: ${error}`);
}
} else if (session.state !== 'invalid') {
parsedRealmUrl.set('pathname', json.access_token.token_data.path);
session._refreshAccessToken(json.access_token.token, parsedRealmUrl.href);

View File

@ -81,6 +81,8 @@ public:
}
const std::function<void(Args...)>& func() const { return m_state->m_func; }
void operator()(Args... args)
{
if (m_thread == std::this_thread::get_id()) {

View File

@ -31,7 +31,7 @@ template<typename T>
using ConstructorType = void(typename T::Context, typename T::Object, size_t, const typename T::Value[]);
template<typename T>
using MethodType = void(typename T::Context, typename T::Object, size_t, const typename T::Value[], ReturnValue<T> &);
using MethodType = void(typename T::Context, typename T::Function, typename T::Object, size_t, const typename T::Value[], ReturnValue<T> &);
template<typename T>
struct PropertyType {

View File

@ -59,20 +59,20 @@ struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
static bool set_index(ContextType, ObjectType, uint32_t, ValueType);
// methods
static void push(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void pop(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void unshift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void shift(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void splice(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void snapshot(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
static void push(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void pop(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void unshift(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void shift(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void splice(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void snapshot(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void filtered(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void sorted(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void is_valid(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
// observable
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void add_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
std::string const name = "List";
@ -125,7 +125,7 @@ bool ListClass<T>::set_index(ContextType ctx, ObjectType object, uint32_t index,
}
template<typename T>
void ListClass<T>::push(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::push(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -137,7 +137,7 @@ void ListClass<T>::push(ContextType ctx, ObjectType this_object, size_t argc, co
}
template<typename T>
void ListClass<T>::pop(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::pop(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -156,7 +156,7 @@ void ListClass<T>::pop(ContextType ctx, ObjectType this_object, size_t argc, con
}
template<typename T>
void ListClass<T>::unshift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::unshift(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -168,7 +168,7 @@ void ListClass<T>::unshift(ContextType ctx, ObjectType this_object, size_t argc,
}
template<typename T>
void ListClass<T>::shift(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::shift(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -185,7 +185,7 @@ void ListClass<T>::shift(ContextType ctx, ObjectType this_object, size_t argc, c
}
template<typename T>
void ListClass<T>::splice(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::splice(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -221,7 +221,7 @@ void ListClass<T>::splice(ContextType ctx, ObjectType this_object, size_t argc,
}
template<typename T>
void ListClass<T>::snapshot(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::snapshot(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -229,7 +229,7 @@ void ListClass<T>::snapshot(ContextType ctx, ObjectType this_object, size_t argc
}
template<typename T>
void ListClass<T>::filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::filtered(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -237,7 +237,7 @@ void ListClass<T>::filtered(ContextType ctx, ObjectType this_object, size_t argc
}
template<typename T>
void ListClass<T>::sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::sorted(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1, 2);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -245,12 +245,12 @@ void ListClass<T>::sorted(ContextType ctx, ObjectType this_object, size_t argc,
}
template<typename T>
void ListClass<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::is_valid(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
return_value.set(get_internal<T, ListClass<T>>(this_object)->is_valid());
}
template<typename T>
void ListClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::add_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -271,7 +271,7 @@ void ListClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t
}
template<typename T>
void ListClass<T>::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
auto list = get_internal<T, ListClass<T>>(this_object);
@ -291,7 +291,7 @@ void ListClass<T>::remove_listener(ContextType ctx, ObjectType this_object, size
}
template<typename T>
void ListClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ListClass<T>::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto list = get_internal<T, ListClass<T>>(this_object);

View File

@ -151,16 +151,16 @@ public:
static FunctionType create_constructor(ContextType);
// methods
static void objects(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void object_for_primary_key(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void create(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void delete_one(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void delete_all(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void write(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void close(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void objects(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void object_for_primary_key(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void create(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void delete_one(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void delete_all(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void write(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void add_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void close(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
// properties
static void get_path(ContextType, ObjectType, ReturnValue &);
@ -173,9 +173,9 @@ public:
// static methods
static void constructor(ContextType, ObjectType, size_t, const ValueType[]);
static void schema_version(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void clear_test_state(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void copy_bundled_realm_files(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void schema_version(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void clear_test_state(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void copy_bundled_realm_files(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
// static properties
static void get_default_path(ContextType, ObjectType, ReturnValue &);
@ -437,7 +437,7 @@ void RealmClass<T>::constructor(ContextType ctx, ObjectType this_object, size_t
}
template<typename T>
void RealmClass<T>::schema_version(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::schema_version(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1, 2);
realm::Realm::Config config;
@ -459,14 +459,14 @@ void RealmClass<T>::schema_version(ContextType ctx, ObjectType this_object, size
template<typename T>
void RealmClass<T>::clear_test_state(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::clear_test_state(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
js::clear_test_state();
}
template<typename T>
void RealmClass<T>::copy_bundled_realm_files(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::copy_bundled_realm_files(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
realm::copy_bundled_realm_files();
}
@ -518,7 +518,7 @@ void RealmClass<T>::get_sync_session(ContextType ctx, ObjectType object, ReturnV
#endif
template<typename T>
void RealmClass<T>::objects(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::objects(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
@ -528,7 +528,7 @@ void RealmClass<T>::objects(ContextType ctx, ObjectType this_object, size_t argc
}
template<typename T>
void RealmClass<T>::object_for_primary_key(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::object_for_primary_key(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 2);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
@ -544,7 +544,7 @@ void RealmClass<T>::object_for_primary_key(ContextType ctx, ObjectType this_obje
}
template<typename T>
void RealmClass<T>::create(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::create(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 2, 3);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
@ -565,7 +565,7 @@ void RealmClass<T>::create(ContextType ctx, ObjectType this_object, size_t argc,
}
template<typename T>
void RealmClass<T>::delete_one(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::delete_one(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
@ -612,7 +612,7 @@ void RealmClass<T>::delete_one(ContextType ctx, ObjectType this_object, size_t a
}
template<typename T>
void RealmClass<T>::delete_all(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::delete_all(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
@ -627,7 +627,7 @@ void RealmClass<T>::delete_all(ContextType ctx, ObjectType this_object, size_t a
}
template<typename T>
void RealmClass<T>::write(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::write(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);
@ -647,7 +647,7 @@ void RealmClass<T>::write(ContextType ctx, ObjectType this_object, size_t argc,
}
template<typename T>
void RealmClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::add_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 2);
validated_notification_name(ctx, arguments[0]);
@ -661,7 +661,7 @@ void RealmClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t
}
template<typename T>
void RealmClass<T>::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 2);
validated_notification_name(ctx, arguments[0]);
@ -675,7 +675,7 @@ void RealmClass<T>::remove_listener(ContextType ctx, ObjectType this_object, siz
}
template<typename T>
void RealmClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0, 1);
if (argc) {
validated_notification_name(ctx, arguments[0]);
@ -689,7 +689,7 @@ void RealmClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object
}
template<typename T>
void RealmClass<T>::close(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmClass<T>::close(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
SharedRealm realm = *get_internal<T, RealmClass<T>>(this_object);

View File

@ -46,7 +46,7 @@ struct RealmObjectClass : ClassDefinition<T, realm::Object> {
static bool set_property(ContextType, ObjectType, const String &, ValueType);
static std::vector<String> get_property_names(ContextType, ObjectType);
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
static void is_valid(ContextType, FunctionType, ObjectType, size_t, const ValueType [], ReturnValue &);
const std::string name = "RealmObject";
@ -62,7 +62,7 @@ struct RealmObjectClass : ClassDefinition<T, realm::Object> {
};
template<typename T>
void RealmObjectClass<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void RealmObjectClass<T>::is_valid(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
return_value.set(get_internal<T, RealmObjectClass<T>>(this_object)->is_valid());
}

View File

@ -66,15 +66,15 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
static void get_length(ContextType, ObjectType, ReturnValue &);
static void get_index(ContextType, ObjectType, uint32_t, ReturnValue &);
static void snapshot(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void filtered(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void sorted(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
static void snapshot(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void filtered(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void sorted(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void is_valid(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
// observable
static void add_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_listener(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_all_listeners(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void add_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void remove_all_listeners(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
std::string const name = "Results";
@ -205,7 +205,7 @@ void ResultsClass<T>::get_index(ContextType ctx, ObjectType object, uint32_t ind
}
template<typename T>
void ResultsClass<T>::snapshot(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ResultsClass<T>::snapshot(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto results = get_internal<T, ResultsClass<T>>(this_object);
@ -213,7 +213,7 @@ void ResultsClass<T>::snapshot(ContextType ctx, ObjectType this_object, size_t a
}
template<typename T>
void ResultsClass<T>::filtered(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ResultsClass<T>::filtered(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count_at_least(argc, 1);
auto results = get_internal<T, ResultsClass<T>>(this_object);
@ -221,7 +221,7 @@ void ResultsClass<T>::filtered(ContextType ctx, ObjectType this_object, size_t a
}
template<typename T>
void ResultsClass<T>::sorted(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ResultsClass<T>::sorted(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1, 2);
auto results = get_internal<T, ResultsClass<T>>(this_object);
@ -229,12 +229,12 @@ void ResultsClass<T>::sorted(ContextType ctx, ObjectType this_object, size_t arg
}
template<typename T>
void ResultsClass<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ResultsClass<T>::is_valid(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
return_value.set(get_internal<T, ResultsClass<T>>(this_object)->is_valid());
}
template<typename T>
void ResultsClass<T>::add_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ResultsClass<T>::add_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
auto results = get_internal<T, ResultsClass<T>>(this_object);
@ -255,7 +255,7 @@ void ResultsClass<T>::add_listener(ContextType ctx, ObjectType this_object, size
}
template<typename T>
void ResultsClass<T>::remove_listener(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ResultsClass<T>::remove_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
auto results = get_internal<T, ResultsClass<T>>(this_object);
@ -275,7 +275,7 @@ void ResultsClass<T>::remove_listener(ContextType ctx, ObjectType this_object, s
}
template<typename T>
void ResultsClass<T>::remove_all_listeners(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void ResultsClass<T>::remove_all_listeners(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 0);
auto results = get_internal<T, ResultsClass<T>>(this_object);

View File

@ -70,7 +70,7 @@ public:
{"isAdmin", {wrap<is_admin>, nullptr}},
};
static void create_user(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void create_user(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
MethodMap<T> const static_methods = {
{"createUser", wrap<create_user>}
@ -84,8 +84,8 @@ public:
{"all", {wrap<all_users>, nullptr}},
};
static void logout(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void session_for_on_disk_path(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void logout(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void session_for_on_disk_path(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
MethodMap<T> const methods = {
{"logout", wrap<logout>},
@ -117,7 +117,7 @@ void UserClass<T>::is_admin(ContextType ctx, ObjectType object, ReturnValue &ret
}
template<typename T>
void UserClass<T>::create_user(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void UserClass<T>::create_user(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 3, 4);
SharedUser *user = new SharedUser(SyncManager::shared().get_user(
Value::validated_to_string(ctx, arguments[1]),
@ -161,8 +161,8 @@ void UserClass<T>::current_user(ContextType ctx, ObjectType object, ReturnValue
*/
template<typename T>
void UserClass<T>::logout(ContextType ctx, ObjectType object, size_t, const ValueType[], ReturnValue &) {
get_internal<T, UserClass<T>>(object)->get()->log_out();
void UserClass<T>::logout(ContextType ctx, FunctionType, ObjectType this_object, size_t, const ValueType[], ReturnValue &) {
get_internal<T, UserClass<T>>(this_object)->get()->log_out();
}
template<typename T>
@ -186,8 +186,8 @@ public:
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 &);
static void simulate_error(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void refresh_access_token(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
PropertyMap<T> const properties = {
{"config", {wrap<get_config>, nullptr}},
@ -203,8 +203,44 @@ public:
};
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);
class SyncSessionErrorHandlerFunctor {
public:
SyncSessionErrorHandlerFunctor(typename T::Context ctx, typename T::Function error_func)
: m_ctx(Context<T>::get_global_context(ctx))
, m_func(ctx, error_func)
{ }
typename T::Function func() const { return m_func; }
void operator()(std::shared_ptr<SyncSession> session, SyncError error) {
HANDLESCOPE
auto error_object = Object<T>::create_empty(m_ctx);
Object<T>::set_property(m_ctx, error_object, "message", Value<T>::from_string(m_ctx, error.message));
Object<T>::set_property(m_ctx, error_object, "isFatal", Value<T>::from_boolean(m_ctx, error.is_fatal));
Object<T>::set_property(m_ctx, error_object, "category", Value<T>::from_string(m_ctx, error.error_code.category().name()));
Object<T>::set_property(m_ctx, error_object, "code", Value<T>::from_number(m_ctx, error.error_code.value()));
auto user_info = Object<T>::create_empty(m_ctx);
for (auto& kvp : error.user_info) {
Object<T>::set_property(m_ctx, user_info, kvp.first, Value<T>::from_string(m_ctx, kvp.second));
}
Object<T>::set_property(m_ctx, error_object, "userInfo", user_info);
typename T::Value arguments[2];
arguments[0] = create_object<T, SessionClass<T>>(m_ctx, new WeakSession(session));
arguments[1] = error_object;
Function<T>::call(m_ctx, m_func, 2, arguments);
}
private:
const Protected<typename T::GlobalContext> m_ctx;
const Protected<typename T::Function> m_func;
};
template<typename T>
void UserClass<T>::session_for_on_disk_path(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
auto user = *get_internal<T, UserClass<T>>(this_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 {
@ -218,6 +254,10 @@ void SessionClass<T>::get_config(ContextType ctx, ObjectType object, ReturnValue
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));
if (auto* dispatcher = session->config().error_handler.template target<EventLoopDispatcher<SyncSessionErrorHandler>>()) {
auto& handler = *dispatcher->func().template target<SyncSessionErrorHandlerFunctor<T>>();
Object::set_property(ctx, config, "error", handler.func());
}
return_value.set(config);
} else {
return_value.set_undefined();
@ -263,10 +303,10 @@ void SessionClass<T>::get_state(ContextType ctx, ObjectType object, ReturnValue
}
template<typename T>
void SessionClass<T>::simulate_error(ContextType ctx, ObjectType object, size_t argc, const ValueType arguments[], ReturnValue &) {
void SessionClass<T>::simulate_error(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &) {
validate_argument_count(argc, 2);
if (auto session = get_internal<T, SessionClass<T>>(object)->lock()) {
if (auto session = get_internal<T, SessionClass<T>>(this_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]);
@ -275,10 +315,10 @@ void SessionClass<T>::simulate_error(ContextType ctx, ObjectType object, size_t
}
template<typename T>
void SessionClass<T>::refresh_access_token(ContextType ctx, ObjectType object, size_t argc, const ValueType arguments[], ReturnValue &) {
void SessionClass<T>::refresh_access_token(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &) {
validate_argument_count(argc, 2);
if (auto session = get_internal<T, SessionClass<T>>(object)->lock()) {
if (auto session = get_internal<T, SessionClass<T>>(this_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));
@ -304,8 +344,8 @@ public:
static FunctionType create_constructor(ContextType);
static void set_sync_log_level(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void set_verify_servers_ssl_certificate(ContextType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void set_sync_log_level(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
static void set_verify_servers_ssl_certificate(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
// private
static void populate_sync_config(ContextType, ObjectType realm_constructor, ObjectType config_object, Realm::Config&);
@ -335,7 +375,7 @@ inline typename T::Function SyncClass<T>::create_constructor(ContextType ctx) {
}
template<typename T>
void SyncClass<T>::set_sync_log_level(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void SyncClass<T>::set_sync_log_level(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
std::string log_level = Value::validated_to_string(ctx, arguments[0]);
std::istringstream in(log_level); // Throws
@ -349,7 +389,7 @@ void SyncClass<T>::set_sync_log_level(ContextType ctx, ObjectType this_object, s
}
template<typename T>
void SyncClass<T>::set_verify_servers_ssl_certificate(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
void SyncClass<T>::set_verify_servers_ssl_certificate(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
validate_argument_count(argc, 1);
bool verify_servers_ssl_certificate = Value::validated_to_boolean(ctx, arguments[0]);
realm::SyncManager::shared().set_client_should_validate_ssl(verify_servers_ssl_certificate);
@ -393,28 +433,7 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
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);
});
error_handler = EventLoopDispatcher<SyncSessionErrorHandler>(SyncSessionErrorHandlerFunctor<T>(ctx, Value::validated_to_function(ctx, error_func)));
}
ObjectType user = Object::validated_get_object(ctx, sync_config_object, "user");

View File

@ -360,7 +360,7 @@ template<jsc::MethodType F>
JSValueRef wrap(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], JSValueRef* exception) {
jsc::ReturnValue return_value(ctx);
try {
F(ctx, this_object, argc, arguments, return_value);
F(ctx, function, this_object, argc, arguments, return_value);
return return_value;
}
catch (std::exception &e) {

View File

@ -301,7 +301,7 @@ void wrap(const v8::FunctionCallbackInfo<v8::Value>& info) {
auto arguments = node::get_arguments(info);
try {
F(isolate, info.This(), arguments.size(), arguments.data(), return_value);
F(isolate, info.Callee(), info.This(), arguments.size(), arguments.data(), return_value);
}
catch (std::exception &e) {
Nan::ThrowError(node::Exception::value(isolate, e));

View File

@ -231,6 +231,7 @@ RPCServer::RPCServer() {
}
m_callbacks.clear();
m_callback_ids.clear();
JSGarbageCollect(m_context);
js::clear_test_state();
@ -249,22 +250,24 @@ RPCServer::~RPCServer() {
JSGlobalContextRelease(m_context);
}
void RPCServer::run_callback(JSContextRef ctx, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], jsc::ReturnValue &return_value) {
void RPCServer::run_callback(JSContextRef ctx, JSObjectRef function, JSObjectRef this_object, size_t argc, const JSValueRef arguments[], jsc::ReturnValue &return_value) {
RPCServer* server = get_rpc_server(JSContextGetGlobalContext(ctx));
if (!server) {
return;
}
// The first argument was curried to be the callback id.
RPCObjectID callback_id = jsc::Value::to_number(ctx, arguments[0]);
JSObjectRef arguments_array = jsc::Object::create_array(ctx, uint32_t(argc - 1), argc == 1 ? nullptr : arguments + 1);
RPCObjectID callback_id = server->m_callback_ids[function];
JSObjectRef arguments_array = jsc::Object::create_array(ctx, uint32_t(argc), arguments);
json arguments_json = server->serialize_json_value(arguments_array);
json this_json = server->serialize_json_value(this_object);
// The next task on the stack will instruct the JS to run this callback.
// This captures references since it will be executed before exiting this function.
server->m_worker.add_task([&]() -> json {
return {
{"callback", callback_id},
{"this", this_json},
{"arguments", arguments_json},
};
});
@ -422,6 +425,15 @@ json RPCServer::serialize_json_value(JSValueRef js_value) {
{"value", jsc::Value::to_number(m_context, js_object)},
};
}
else if (jsc::Value::is_function(m_context, js_object)) {
auto it = m_callback_ids.find(js_object);
if (it != m_callback_ids.end()) {
return {
{"type", RealmObjectTypesFunction},
{"value", it->second}
};
}
}
else {
// Serialize this JS object as a plain object since it doesn't match any known types above.
std::vector<jsc::String> js_keys = jsc::Object::get_property_names(m_context, js_object);
@ -474,13 +486,8 @@ JSValueRef RPCServer::deserialize_json_value(const json dict) {
if (!m_callbacks.count(callback_id)) {
JSObjectRef callback = JSObjectMakeFunctionWithCallback(m_context, nullptr, js::wrap<run_callback>);
// Curry the first argument to be the callback id.
JSValueRef bind_args[2] = {jsc::Value::from_null(m_context), jsc::Value::from_number(m_context, callback_id)};
JSValueRef bound_callback = jsc::Object::call_method(m_context, callback, "bind", 2, bind_args);
callback = jsc::Value::to_function(m_context, bound_callback);
m_callbacks.emplace(callback_id, js::Protected<JSObjectRef>(m_context, callback));
m_callback_ids.emplace(callback, callback_id);
}
return m_callbacks.at(callback_id);

View File

@ -67,11 +67,15 @@ class RPCServer {
std::map<std::string, RPCRequest> m_requests;
std::map<RPCObjectID, js::Protected<JSObjectRef>> m_objects;
std::map<RPCObjectID, js::Protected<JSObjectRef>> m_callbacks;
// The key here is the same as the value in m_callbacks. We use the raw pointer as a key here,
// because protecting the value in m_callbacks pins the function object and prevents it from being moved
// by the garbage collector upon compaction.
std::map<JSObjectRef, RPCObjectID> m_callback_ids;
ConcurrentDeque<json> m_callback_results;
RPCObjectID m_session_id;
RPCWorker m_worker;
static void run_callback(JSContextRef, JSObjectRef, size_t, const JSValueRef[], jsc::ReturnValue &);
static void run_callback(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], jsc::ReturnValue &);
RPCObjectID store_object(JSObjectRef object);

View File

@ -86,6 +86,7 @@ module.exports = {
let realm = new Realm(config);
let session = realm.syncSession;
TestCase.assertEqual(session.config.error, config.sync.error);
session._simulateError(123, 'simulated error');
return wait(100).then(() => {