Skip to content

Commit

Permalink
Wrapped enum
Browse files Browse the repository at this point in the history
  • Loading branch information
achamayou committed May 23, 2024
1 parent 325deba commit 7c550d5
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 3 deletions.
File renamed without changes.
244 changes: 243 additions & 1 deletion samples/apps/basic/custom_endpoints/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "ccf/endpoint.h"
#include "ccf/endpoints/authentication/js.h"
#include "ccf/js/core/context.h"
#include "ccf/js/core/wrapped_property_enum.h"
#include "ccf/js/extensions/ccf/consensus.h"
#include "ccf/js/extensions/ccf/converters.h"
#include "ccf/js/extensions/ccf/crypto.h"
Expand Down Expand Up @@ -303,8 +304,249 @@ namespace basicapp
return;
}

// TBD: Run fetched endpoint
CCF_APP_INFO("CUSTOM ENDPOINT: {}", endpoint->dispatch.uri_path);

// Call exported function;
auto request = request_extension->create_request_obj(
ctx, endpoint->full_uri_path, endpoint_ctx, this);

auto val = ctx.call_with_rt_options(
export_func,
{request},
&endpoint_ctx.tx,
ccf::js::core::RuntimeLimitsPolicy::NONE);

for (auto extension : local_extensions)
{
ctx.remove_extension(extension);
}

const auto& rt = ctx.runtime();

if (val.is_exception())
{
bool time_out = ctx.interrupt_data.request_timed_out;
std::string error_msg = "Exception thrown while executing.";
if (time_out)
{
error_msg = "Operation took too long to complete.";
}

auto [reason, trace] = ctx.error_message();

if (rt.log_exception_details)
{
CCF_APP_FAIL("{}: {}", reason, trace.value_or("<no trace>"));
}

if (rt.return_exception_details)
{
std::vector<nlohmann::json> details = {ccf::ODataJSExceptionDetails{
ccf::errors::JSException, reason, trace}};
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
std::move(error_msg),
std::move(details));
}
else
{
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
std::move(error_msg));
}

return;
}

// Handle return value: {body, headers, statusCode}
if (!val.is_obj())
{
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
"Invalid endpoint function return value (not an object).");
return;
}

// Response body (also sets a default response content-type header)
{
auto response_body_js = val["body"];
if (!response_body_js.is_undefined())
{
std::vector<uint8_t> response_body;
size_t buf_size;
size_t buf_offset;
auto typed_array_buffer = ctx.get_typed_array_buffer(
response_body_js, &buf_offset, &buf_size, nullptr);
uint8_t* array_buffer;
if (!typed_array_buffer.is_exception())
{
size_t buf_size_total;
array_buffer =
JS_GetArrayBuffer(ctx, &buf_size_total, typed_array_buffer.val);
array_buffer += buf_offset;
}
else
{
array_buffer =
JS_GetArrayBuffer(ctx, &buf_size, response_body_js.val);
}
if (array_buffer)
{
endpoint_ctx.rpc_ctx->set_response_header(
http::headers::CONTENT_TYPE,
http::headervalues::contenttype::OCTET_STREAM);
response_body =
std::vector<uint8_t>(array_buffer, array_buffer + buf_size);
}
else
{
std::optional<std::string> str;
if (response_body_js.is_str())
{
endpoint_ctx.rpc_ctx->set_response_header(
http::headers::CONTENT_TYPE,
http::headervalues::contenttype::TEXT);
str = ctx.to_str(response_body_js);
}
else
{
endpoint_ctx.rpc_ctx->set_response_header(
http::headers::CONTENT_TYPE,
http::headervalues::contenttype::JSON);
auto rval = ctx.json_stringify(response_body_js);
if (rval.is_exception())
{
auto [reason, trace] = ctx.error_message();

if (rt.log_exception_details)
{
CCF_APP_FAIL(
"Failed to convert return value to JSON:{} {}",
reason,
trace.value_or("<no trace>"));
}

if (rt.return_exception_details)
{
std::vector<nlohmann::json> details = {
ccf::ODataJSExceptionDetails{
ccf::errors::JSException, reason, trace}};
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
"Invalid endpoint function return value (error during JSON "
"conversion of body)",
std::move(details));
}
else
{
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
"Invalid endpoint function return value (error during JSON "
"conversion of body).");
}
return;
}
str = ctx.to_str(rval);
}

if (!str)
{
auto [reason, trace] = ctx.error_message();

if (rt.log_exception_details)
{
CCF_APP_FAIL(
"Failed to convert return value to JSON:{} {}",
reason,
trace.value_or("<no trace>"));
}

if (rt.return_exception_details)
{
std::vector<nlohmann::json> details = {
ccf::ODataJSExceptionDetails{
ccf::errors::JSException, reason, trace}};
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
"Invalid endpoint function return value (error during string "
"conversion of body).",
std::move(details));
}
else
{
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
"Invalid endpoint function return value (error during string "
"conversion of body).");
}
return;
}

response_body = std::vector<uint8_t>(str->begin(), str->end());
}
endpoint_ctx.rpc_ctx->set_response_body(std::move(response_body));
}
}

// Response headers
{
auto response_headers_js = val["headers"];
if (response_headers_js.is_obj())
{
ccf::js::core::JSWrappedPropertyEnum prop_enum(
ctx, response_headers_js);
for (size_t i = 0; i < prop_enum.size(); i++)
{
auto prop_name = ctx.to_str(prop_enum[i]);
if (!prop_name)
{
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
"Invalid endpoint function return value (header type).");
return;
}
auto prop_val = response_headers_js[*prop_name];
auto prop_val_str = ctx.to_str(prop_val);
if (!prop_val_str)
{
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
"Invalid endpoint function return value (header value type).");
return;
}
endpoint_ctx.rpc_ctx->set_response_header(
*prop_name, *prop_val_str);
}
}
}

// Response status code
int response_status_code = HTTP_STATUS_OK;
{
auto status_code_js = val["statusCode"];
if (!status_code_js.is_undefined() && !JS_IsNull(status_code_js.val))
{
if (JS_VALUE_GET_TAG(status_code_js.val) != JS_TAG_INT)
{
endpoint_ctx.rpc_ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError,
"Invalid endpoint function return value (status code type).");
return;
}
response_status_code = JS_VALUE_GET_INT(status_code_js.val);
}
endpoint_ctx.rpc_ctx->set_response_status(response_status_code);
}
}

void execute_request(
Expand Down
2 changes: 1 addition & 1 deletion src/apps/js_generic/js_generic_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "ccf/endpoints/authentication/all_of_auth.h"
#include "ccf/historical_queries_adapter.h"
#include "ccf/js/core/context.h"
#include "ccf/js/core/wrapped_property_enum.h"
#include "ccf/js/extensions/ccf/consensus.h"
#include "ccf/js/extensions/ccf/converters.h"
#include "ccf/js/extensions/ccf/crypto.h"
Expand All @@ -22,7 +23,6 @@
#include "ccf/service/tables/jsengine.h"
#include "ccf/version.h"
#include "enclave/enclave_time.h"
#include "js/core/wrapped_property_enum.h"
#include "js/global_class_ids.h"
#include "js/interpreter_cache_interface.h"
#include "node/rpc/rpc_context_impl.h"
Expand Down
3 changes: 2 additions & 1 deletion tests/programmability.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
return {
statusCode: 200,
body: {
error: "Test content",
payload: "Test content",
},
};
}
Expand Down Expand Up @@ -60,6 +60,7 @@ def test_custom_endpoints(network, args):
with primary.client() as c:
r = c.get("/app/content")
assert r.status_code == http.HTTPStatus.OK.value, r.status_code
assert r.body.json()["payload"] == "Test content", r.body.json()

return network

Expand Down

0 comments on commit 7c550d5

Please sign in to comment.