make it work
This commit is contained in:
parent
92a9113ef6
commit
c3c9cee0ee
|
@ -82,10 +82,14 @@ JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool cre
|
||||||
_bridge = bridge;
|
_bridge = bridge;
|
||||||
|
|
||||||
static GCDWebServer *s_webServer;
|
static GCDWebServer *s_webServer;
|
||||||
|
static realm_js::RPCServer *rpcServer;
|
||||||
|
|
||||||
if (s_webServer) {
|
if (s_webServer) {
|
||||||
[s_webServer stop];
|
[s_webServer stop];
|
||||||
[s_webServer removeAllHandlers];
|
[s_webServer removeAllHandlers];
|
||||||
s_webServer = nil;
|
s_webServer = nil;
|
||||||
|
|
||||||
|
delete rpcServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The executor could be a RCTWebSocketExecutor, in which case it won't have a JS context.
|
// The executor could be a RCTWebSocketExecutor, in which case it won't have a JS context.
|
||||||
|
@ -94,17 +98,16 @@ JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool cre
|
||||||
if ([executor isMemberOfClass:NSClassFromString(@"RCTWebSocketExecutor")]) {
|
if ([executor isMemberOfClass:NSClassFromString(@"RCTWebSocketExecutor")]) {
|
||||||
[GCDWebServer setLogLevel:3];
|
[GCDWebServer setLogLevel:3];
|
||||||
GCDWebServer *webServer = [[GCDWebServer alloc] init];
|
GCDWebServer *webServer = [[GCDWebServer alloc] init];
|
||||||
__block realm_js::RPCServer rpcServer;
|
rpcServer = new realm_js::RPCServer();
|
||||||
|
|
||||||
// Add a handler to respond to POST requests on any URL
|
// Add a handler to respond to POST requests on any URL
|
||||||
[webServer addDefaultHandlerForMethod:@"POST"
|
[webServer addDefaultHandlerForMethod:@"POST"
|
||||||
requestClass:[GCDWebServerDataRequest class]
|
requestClass:[GCDWebServerDataRequest class]
|
||||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||||
|
|
||||||
GCDWebServerResponse *response;
|
GCDWebServerResponse *response;
|
||||||
try {
|
try {
|
||||||
realm_js::json args = realm_js::json::parse([[(GCDWebServerDataRequest *)request text] UTF8String]);
|
realm_js::json args = realm_js::json::parse([[(GCDWebServerDataRequest *)request text] UTF8String]);
|
||||||
std::string response_text = rpcServer.perform_request(request.path.UTF8String, args).dump();
|
std::string response_text = rpcServer->perform_request(request.path.UTF8String, args).dump();
|
||||||
response = [[GCDWebServerDataResponse alloc] initWithData:[NSData dataWithBytes:response_text.c_str() length:response_text.length()] contentType:@"application/json"];
|
response = [[GCDWebServerDataResponse alloc] initWithData:[NSData dataWithBytes:response_text.c_str() length:response_text.length()] contentType:@"application/json"];
|
||||||
}
|
}
|
||||||
catch(std::exception &ex) {
|
catch(std::exception &ex) {
|
||||||
|
|
|
@ -35,7 +35,7 @@ class RPCServer {
|
||||||
public:
|
public:
|
||||||
RPCServer();
|
RPCServer();
|
||||||
~RPCServer();
|
~RPCServer();
|
||||||
json perform_request(std::string name, const json args);
|
json perform_request(std::string name, json &args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JSGlobalContextRef _context;
|
JSGlobalContextRef _context;
|
||||||
|
|
144
src/RealmRPC.mm
144
src/RealmRPC.mm
|
@ -48,7 +48,7 @@ RPCServer::RPCServer() {
|
||||||
setIncludesNativeCallStack(_context, false);
|
setIncludesNativeCallStack(_context, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_requests["/create_session"] = [=](const json dict) {
|
_requests["/create_session"] = [this](const json dict) {
|
||||||
[RealmJS initializeContext:_context];
|
[RealmJS initializeContext:_context];
|
||||||
|
|
||||||
JSStringRef realm_string = RJSStringForString("Realm");
|
JSStringRef realm_string = RJSStringForString("Realm");
|
||||||
|
@ -56,9 +56,9 @@ RPCServer::RPCServer() {
|
||||||
JSStringRelease(realm_string);
|
JSStringRelease(realm_string);
|
||||||
|
|
||||||
_sessionID = store_object(realm_constructor);
|
_sessionID = store_object(realm_constructor);
|
||||||
return json({"result", _sessionID});
|
return (json){{"result", _sessionID}};
|
||||||
};
|
};
|
||||||
_requests["/create_realm"] = [=](const json dict) {
|
_requests["/create_realm"] = [this](const json dict) {
|
||||||
JSObjectRef realm_constructor = _sessionID ? _objects[_sessionID] : NULL;
|
JSObjectRef realm_constructor = _sessionID ? _objects[_sessionID] : NULL;
|
||||||
if (!realm_constructor) {
|
if (!realm_constructor) {
|
||||||
throw std::runtime_error("Realm constructor not found!");
|
throw std::runtime_error("Realm constructor not found!");
|
||||||
|
@ -76,28 +76,28 @@ RPCServer::RPCServer() {
|
||||||
JSObjectRef realmObject = JSObjectCallAsConstructor(_context, realm_constructor, arg_count, arg_values, &exception);
|
JSObjectRef realmObject = JSObjectCallAsConstructor(_context, realm_constructor, arg_count, arg_values, &exception);
|
||||||
|
|
||||||
if (exception) {
|
if (exception) {
|
||||||
return (json){"error", RJSStringForValue(_context, exception)};
|
return (json){{"error", RJSStringForValue(_context, exception)}};
|
||||||
}
|
}
|
||||||
|
|
||||||
RPCObjectID realmId = store_object(realmObject);
|
RPCObjectID realmId = store_object(realmObject);
|
||||||
return (json){"result", realmId};
|
return (json){{"result", realmId}};
|
||||||
};
|
};
|
||||||
_requests["/begin_transaction"] = [=](const json dict) {
|
_requests["/begin_transaction"] = [this](const json dict) {
|
||||||
RPCObjectID realmId = dict["realmId"].get<RPCObjectID>();
|
RPCObjectID realmId = dict["realmId"].get<RPCObjectID>();
|
||||||
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->begin_transaction();
|
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->begin_transaction();
|
||||||
return nil;
|
return json::object();
|
||||||
};
|
};
|
||||||
_requests["/cancel_transaction"] = [=](const json dict) {
|
_requests["/cancel_transaction"] = [this](const json dict) {
|
||||||
RPCObjectID realmId = dict["realmId"].get<RPCObjectID>();
|
RPCObjectID realmId = dict["realmId"].get<RPCObjectID>();
|
||||||
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->cancel_transaction();
|
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->cancel_transaction();
|
||||||
return nil;
|
return json::object();
|
||||||
};
|
};
|
||||||
_requests["/commit_transaction"] = [=](const json dict) {
|
_requests["/commit_transaction"] = [this](const json dict) {
|
||||||
RPCObjectID realmId = dict["realmId"].get<RPCObjectID>();
|
RPCObjectID realmId = dict["realmId"].get<RPCObjectID>();
|
||||||
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->commit_transaction();
|
RJSGetInternal<realm::SharedRealm *>(_objects[realmId])->get()->commit_transaction();
|
||||||
return nil;
|
return json::object();
|
||||||
};
|
};
|
||||||
_requests["/call_method"] = [=](const json dict) {
|
_requests["/call_method"] = [this](const json dict) {
|
||||||
JSObjectRef object = _objects[dict["id"].get<RPCObjectID>()];
|
JSObjectRef object = _objects[dict["id"].get<RPCObjectID>()];
|
||||||
JSStringRef methodString = RJSStringForString(dict["name"].get<std::string>());
|
JSStringRef methodString = RJSStringForString(dict["name"].get<std::string>());
|
||||||
JSObjectRef function = RJSValidatedObjectProperty(_context, object, methodString);
|
JSObjectRef function = RJSValidatedObjectProperty(_context, object, methodString);
|
||||||
|
@ -114,11 +114,11 @@ RPCServer::RPCServer() {
|
||||||
JSValueRef result = JSObjectCallAsFunction(_context, function, object, argCount, argValues, &exception);
|
JSValueRef result = JSObjectCallAsFunction(_context, function, object, argCount, argValues, &exception);
|
||||||
|
|
||||||
if (exception) {
|
if (exception) {
|
||||||
return (json){"error", RJSStringForValue(_context, exception)};
|
return (json){{"error", RJSStringForValue(_context, exception)}};
|
||||||
}
|
}
|
||||||
return (json){"result", serialize_json_value(result)};
|
return (json){{"result", serialize_json_value(result)}};
|
||||||
};
|
};
|
||||||
_requests["/get_property"] = [=](const json dict) {
|
_requests["/get_property"] = [this](const json dict) {
|
||||||
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
||||||
json name = dict["name"];
|
json name = dict["name"];
|
||||||
JSValueRef value = NULL;
|
JSValueRef value = NULL;
|
||||||
|
@ -134,11 +134,11 @@ RPCServer::RPCServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exception) {
|
if (exception) {
|
||||||
return (json){"error", RJSStringForValue(_context, exception)};
|
return (json){{"error", RJSStringForValue(_context, exception)}};
|
||||||
}
|
}
|
||||||
return (json){"result", serialize_json_value(value)};
|
return (json){{"result", serialize_json_value(value)}};
|
||||||
};
|
};
|
||||||
_requests["/set_property"] = [=](const json dict) {
|
_requests["/set_property"] = [this](const json dict) {
|
||||||
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
||||||
json name = dict["name"];
|
json name = dict["name"];
|
||||||
JSValueRef value = deserialize_json_value(dict["value"]);
|
JSValueRef value = deserialize_json_value(dict["value"]);
|
||||||
|
@ -154,17 +154,17 @@ RPCServer::RPCServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exception) {
|
if (exception) {
|
||||||
return json({"error", RJSStringForValue(_context, exception)});
|
return (json){{"error", RJSStringForValue(_context, exception)}};
|
||||||
}
|
}
|
||||||
return json({});
|
return json::object();
|
||||||
};
|
};
|
||||||
_requests["/dispose_object"] = [=](const json dict) {
|
_requests["/dispose_object"] = [this](const json dict) {
|
||||||
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
RPCObjectID oid = dict["id"].get<RPCObjectID>();
|
||||||
JSValueUnprotect(_context, _objects[oid]);
|
JSValueUnprotect(_context, _objects[oid]);
|
||||||
_objects.erase(oid);
|
_objects.erase(oid);
|
||||||
return nil;
|
return json::object();
|
||||||
};
|
};
|
||||||
_requests["/clear_test_state"] = [=](const json dict) {
|
_requests["/clear_test_state"] = [this](const json dict) {
|
||||||
for (auto object : _objects) {
|
for (auto object : _objects) {
|
||||||
// The session ID points to the Realm constructor object, which should remain.
|
// The session ID points to the Realm constructor object, which should remain.
|
||||||
if (object.first == _sessionID) {
|
if (object.first == _sessionID) {
|
||||||
|
@ -176,7 +176,7 @@ RPCServer::RPCServer() {
|
||||||
}
|
}
|
||||||
JSGarbageCollect(_context);
|
JSGarbageCollect(_context);
|
||||||
[RealmJS clearTestState];
|
[RealmJS clearTestState];
|
||||||
return nil;
|
return json::object();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,25 +188,26 @@ RPCServer::~RPCServer() {
|
||||||
JSGlobalContextRelease(_context);
|
JSGlobalContextRelease(_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
json RPCServer::perform_request(std::string name, const json args) {
|
json RPCServer::perform_request(std::string name, json &args) {
|
||||||
// perform all realm ops on the main thread
|
// perform all realm ops on the main thread
|
||||||
|
__block json response;
|
||||||
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||||
|
try {
|
||||||
RPCRequest action = _requests[name];
|
RPCRequest action = _requests[name];
|
||||||
assert(action);
|
assert(action);
|
||||||
|
|
||||||
__block json response;
|
if (name == "/create_session" || _sessionID == args["sessionId"].get<RPCObjectID>()) {
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
if (_sessionID != args["sessionId"].get<RPCObjectID>()) {
|
|
||||||
response = {"error", "Invalid session ID"};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = action(args);
|
response = action(args);
|
||||||
|
assert(response.is_object());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response = {{"error", "Invalid session ID"}};
|
||||||
|
}
|
||||||
} catch (std::exception &exception) {
|
} catch (std::exception &exception) {
|
||||||
response = {"error", (std::string)"exception thrown: " + exception.what()};
|
response = {{"error", (std::string)"exception thrown: " + exception.what()}};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return response ?: json();
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
RPCObjectID RPCServer::store_object(JSObjectRef object) {
|
RPCObjectID RPCServer::store_object(JSObjectRef object) {
|
||||||
|
@ -220,15 +221,15 @@ RPCObjectID RPCServer::store_object(JSObjectRef object) {
|
||||||
json RPCServer::serialize_json_value(JSValueRef value) {
|
json RPCServer::serialize_json_value(JSValueRef value) {
|
||||||
switch (JSValueGetType(_context, value)) {
|
switch (JSValueGetType(_context, value)) {
|
||||||
case kJSTypeUndefined:
|
case kJSTypeUndefined:
|
||||||
return {};
|
return json::object();
|
||||||
case kJSTypeNull:
|
case kJSTypeNull:
|
||||||
return {"value", json(nullptr)};
|
return {{"value", json(nullptr)}};
|
||||||
case kJSTypeBoolean:
|
case kJSTypeBoolean:
|
||||||
return {"value", JSValueToBoolean(_context, value)};
|
return {{"value", JSValueToBoolean(_context, value)}};
|
||||||
case kJSTypeNumber:
|
case kJSTypeNumber:
|
||||||
return {"value", JSValueToNumber(_context, value, NULL)};
|
return {{"value", JSValueToNumber(_context, value, NULL)}};
|
||||||
case kJSTypeString:
|
case kJSTypeString:
|
||||||
return {"value", RJSStringForValue(_context, value).c_str()};
|
return {{"value", RJSStringForValue(_context, value).c_str()}};
|
||||||
case kJSTypeObject:
|
case kJSTypeObject:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -238,33 +239,33 @@ json RPCServer::serialize_json_value(JSValueRef value) {
|
||||||
if (JSValueIsObjectOfClass(_context, value, RJSObjectClass())) {
|
if (JSValueIsObjectOfClass(_context, value, RJSObjectClass())) {
|
||||||
realm::Object *object = RJSGetInternal<realm::Object *>(jsObject);
|
realm::Object *object = RJSGetInternal<realm::Object *>(jsObject);
|
||||||
return {
|
return {
|
||||||
"type", RJSTypeGet(realm::PropertyTypeObject).c_str(),
|
{"type", RJSTypeGet(realm::PropertyTypeObject).c_str()},
|
||||||
"id", store_object(jsObject),
|
{"id", store_object(jsObject)},
|
||||||
"schema", serialize_object_schema(object->object_schema)
|
{"schema", serialize_object_schema(object->object_schema)}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (JSValueIsObjectOfClass(_context, value, RJSListClass())) {
|
else if (JSValueIsObjectOfClass(_context, value, RJSListClass())) {
|
||||||
realm::List *list = RJSGetInternal<realm::List *>(jsObject);
|
realm::List *list = RJSGetInternal<realm::List *>(jsObject);
|
||||||
return {
|
return {
|
||||||
"type", RJSTypeGet(realm::PropertyTypeArray),
|
{"type", RJSTypeGet(realm::PropertyTypeArray)},
|
||||||
"id", store_object(jsObject),
|
{"id", store_object(jsObject)},
|
||||||
"size", list->link_view->size(),
|
{"size", list->link_view->size()},
|
||||||
"schema", serialize_object_schema(list->object_schema)
|
{"schema", serialize_object_schema(list->object_schema)}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (JSValueIsObjectOfClass(_context, value, RJSResultsClass())) {
|
else if (JSValueIsObjectOfClass(_context, value, RJSResultsClass())) {
|
||||||
realm::Results *results = RJSGetInternal<realm::Results *>(jsObject);
|
realm::Results *results = RJSGetInternal<realm::Results *>(jsObject);
|
||||||
return {
|
return {
|
||||||
"type", RealmObjectTypesResults,
|
{"type", RealmObjectTypesResults},
|
||||||
"id", store_object(jsObject),
|
{"id", store_object(jsObject)},
|
||||||
"size", results->size(),
|
{"size", results->size()},
|
||||||
"schema", serialize_object_schema(results->object_schema)
|
{"schema", serialize_object_schema(results->object_schema)}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (JSValueIsObjectOfClass(_context, value, RJSNotificationClass())) {
|
else if (JSValueIsObjectOfClass(_context, value, RJSNotificationClass())) {
|
||||||
return {
|
return {
|
||||||
"type", RealmObjectTypesNotification,
|
{"type", RealmObjectTypesNotification},
|
||||||
"id", store_object(jsObject),
|
{"id", store_object(jsObject)},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (RJSIsValueArray(_context, value)) {
|
else if (RJSIsValueArray(_context, value)) {
|
||||||
|
@ -273,43 +274,44 @@ json RPCServer::serialize_json_value(JSValueRef value) {
|
||||||
for (unsigned int i = 0; i < length; i++) {
|
for (unsigned int i = 0; i < length; i++) {
|
||||||
array.push_back(serialize_json_value(JSObjectGetPropertyAtIndex(_context, jsObject, i, NULL)));
|
array.push_back(serialize_json_value(JSObjectGetPropertyAtIndex(_context, jsObject, i, NULL)));
|
||||||
}
|
}
|
||||||
return {"value", array};
|
return {{"value", array}};
|
||||||
}
|
}
|
||||||
else if (RJSIsValueDate(_context, value)) {
|
else if (RJSIsValueDate(_context, value)) {
|
||||||
return {
|
return {
|
||||||
"type", RJSTypeGet(realm::PropertyTypeDate),
|
{"type", RJSTypeGet(realm::PropertyTypeDate)},
|
||||||
"value", RJSValidatedValueToNumber(_context, value),
|
{"value", RJSValidatedValueToNumber(_context, value)},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
json RPCServer::serialize_object_schema(realm::ObjectSchema &objectSchema) {
|
json RPCServer::serialize_object_schema(realm::ObjectSchema &objectSchema) {
|
||||||
json properties({});
|
json properties = json::array();
|
||||||
for (realm::Property prop : objectSchema.properties) {
|
for (realm::Property prop : objectSchema.properties) {
|
||||||
properties.push_back({
|
properties.push_back({
|
||||||
"name", prop.name,
|
{"name", prop.name},
|
||||||
"type", RJSTypeGet(prop.type),
|
{"type", RJSTypeGet(prop.type)},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"name", objectSchema.name,
|
{"name", objectSchema.name},
|
||||||
"properties", properties,
|
{"properties", properties},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValueRef RPCServer::deserialize_json_value(const json dict)
|
JSValueRef RPCServer::deserialize_json_value(const json dict)
|
||||||
{
|
{
|
||||||
RPCObjectID oid = dict["id"].get<long>();
|
json oid = dict["id"];
|
||||||
if (oid) {
|
if (oid.is_number()) {
|
||||||
return _objects[oid];
|
return _objects[oid.get<RPCObjectID>()];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string type = dict["type"].get<std::string>();
|
|
||||||
json value = dict["value"];
|
json value = dict["value"];
|
||||||
|
json type = dict["type"];
|
||||||
if (type == RealmObjectTypesFunction) {
|
if (type.is_string()) {
|
||||||
|
std::string type_string = type.get<std::string>();
|
||||||
|
if (type_string == RealmObjectTypesFunction) {
|
||||||
// FIXME: Make this actually call the function by its id once we need it to.
|
// FIXME: Make this actually call the function by its id once we need it to.
|
||||||
JSStringRef jsBody = JSStringCreateWithUTF8CString("");
|
JSStringRef jsBody = JSStringCreateWithUTF8CString("");
|
||||||
JSObjectRef jsFunction = JSObjectMakeFunction(_context, NULL, 0, NULL, jsBody, NULL, 1, NULL);
|
JSObjectRef jsFunction = JSObjectMakeFunction(_context, NULL, 0, NULL, jsBody, NULL, 1, NULL);
|
||||||
|
@ -317,7 +319,7 @@ JSValueRef RPCServer::deserialize_json_value(const json dict)
|
||||||
|
|
||||||
return jsFunction;
|
return jsFunction;
|
||||||
}
|
}
|
||||||
else if (type == RJSTypeGet(realm::PropertyTypeDate)) {
|
else if (type_string == RJSTypeGet(realm::PropertyTypeDate)) {
|
||||||
JSValueRef exception = NULL;
|
JSValueRef exception = NULL;
|
||||||
JSValueRef time = JSValueMakeNumber(_context, value.get<double>());
|
JSValueRef time = JSValueMakeNumber(_context, value.get<double>());
|
||||||
JSObjectRef date = JSObjectMakeDate(_context, 1, &time, &exception);
|
JSObjectRef date = JSObjectMakeDate(_context, 1, &time, &exception);
|
||||||
|
@ -327,11 +329,9 @@ JSValueRef RPCServer::deserialize_json_value(const json dict)
|
||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return JSValueMakeUndefined(_context);
|
|
||||||
}
|
}
|
||||||
else if (value.is_null()) {
|
|
||||||
|
if (value.is_null()) {
|
||||||
return JSValueMakeNull(_context);
|
return JSValueMakeNull(_context);
|
||||||
}
|
}
|
||||||
else if (value.is_boolean()) {
|
else if (value.is_boolean()) {
|
||||||
|
|
Loading…
Reference in New Issue