diff --git a/examples/server.js b/examples/server.js index 2e7d0d3..33a56e3 100755 --- a/examples/server.js +++ b/examples/server.js @@ -5,7 +5,7 @@ const server = new Server({ dest: "MME_GATEWAY" }, { dest: "MME" }); const delay = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000)); -function my_stfc_connection(request_context, abap_input) { +async function my_stfc_connection(request_context, abap_input) { const connection_attributes = request_context["connection_attributes"]; console.log( "[js] stfc context :", @@ -16,14 +16,15 @@ function my_stfc_connection(request_context, abap_input) { connection_attributes["progName"] ); console.log("[js] stfc request :", abap_input); - for (let i = 1; i < 1000000000; i++) x = i / 3; - //const x = (async () => await delay(5))(); + // for (let i = 1; i < 1000000000; i++) x = i / 3; + await delay(3); abap_output = { REQUTEXT: abap_input.REQUTEXT, ECHOTEXT: abap_input.REQUTEXT, RESPTEXT: `~~~ ${abap_input.REQUTEXT} ~~~`, }; console.log("[js] stfc response:", abap_output); + //throw new Error("some error"); return abap_output; } diff --git a/src/cpp/Log.h b/src/cpp/Log.h index 0c1308e..71b33cc 100644 --- a/src/cpp/Log.h +++ b/src/cpp/Log.h @@ -13,8 +13,8 @@ namespace node_rfc { #define SERVER_LOGFILE "_noderfc.log" -enum class logClass { client, pool, server, throughput }; -enum class logSeverity { info, warning, error }; +enum logClass { client = 0, pool, server, throughput }; +enum logSeverity { info = 0, warning, error }; long long timestamp() { using namespace std; @@ -24,44 +24,16 @@ long long timestamp() { } template -void _log(logClass component, logSeverity severity, Args&&... args) { +void _log(logClass component_id, logSeverity severity_id, Args&&... args) { using namespace std; - string component_name; - string severity_name; - switch (component) { - case logClass::client: - component_name = "client"; - break; - case logClass::server: - component_name = "server"; - break; - case logClass::pool: - component_name = "pool"; - break; - case logClass::throughput: - component_name = "throughput"; - break; - default: - component_name = "component?"; - } - switch (severity) { - case logSeverity::info: - severity_name = "info"; - break; - case logSeverity::warning: - severity_name = "warning"; - break; - case logSeverity::error: - severity_name = "error"; - break; - default: - severity_name = "severity?"; - } + const string component_names[4] = {"client", "pool", "server", "throughput"}; + const string severity_names[3] = {"info", "warning", "error"}; ofstream ofs; ofs.open(SERVER_LOGFILE, ofstream::out | ios::app); time_t now = time(nullptr); ofs << put_time(localtime(&now), "%F %T [") << timestamp() << "] "; - ofs << component_name << " (" << severity_name << ") "; + ofs << component_names[component_id] << " (" << severity_names[severity_id] + << ") "; (ofs << ... << args); ofs << endl; ofs.close(); diff --git a/src/cpp/Server.cc b/src/cpp/Server.cc index c18840c..dabe33e 100644 --- a/src/cpp/Server.cc +++ b/src/cpp/Server.cc @@ -693,13 +693,46 @@ void Server::UnlockMutex() { // uv_sem_post(&invocationMutex); } +using DataType = ServerRequestBaton*; + +// Transform JavaScript parameters' data to ABAP +Napi::Value processResult(Napi::Env env, + Napi::Value jsResult, + DataType requestBaton) { + Napi::Value errorObj = env.Undefined(); + Napi::Object params = jsResult.As(); + Napi::Array paramNames = params.GetPropertyNames(); + uint_t paramSize = paramNames.Length(); + + for (uint_t i = 0; i < paramSize; i++) { + Napi::String name = paramNames.Get(i).ToString(); + Napi::Value value = params.Get(name); + // DEBUG(name, value); + errorObj = setRfmParameter(requestBaton->func_desc_handle, + requestBaton->func_handle, + name, + value, + &requestBaton->errorPath, + &requestBaton->client_options); + + if (!errorObj.IsUndefined()) { + break; + } + } + // Let genericRequestHandler return result to ABAP + requestBaton->done(); + + // Return error, if any + return errorObj; +} + // Thread safe call of JavaScript handler. -// Request "baton" is used to pass ABAP data and for -// waiting until JavaScript handler processing completed +// Request "baton" is used to pass ABAP data and +// wait until JavaScript handler processing completed void JSFunctionCall(Napi::Env env, Napi::Function callback, std::nullptr_t* context, - ServerRequestBaton* requestBaton) { + DataType requestBaton) { UNUSED(context); // set server context @@ -727,43 +760,46 @@ void JSFunctionCall(Napi::Env env, // Call JavaScript handler _log(logClass::server, logSeverity::info, - "JS call start ", + "JS call ", requestBaton->jsFunctionName, " with ABAP function handle ", - (uintptr_t)requestBaton->func_handle); - Napi::Value result = callback.Call({requestContext, abapArgs}); + (uintptr_t)requestBaton->func_handle, + " started"); + + Napi::Value jsResult = callback.Call({requestContext, abapArgs}); + _log(logClass::server, logSeverity::info, - "JS call end ", + "JS call ", requestBaton->jsFunctionName, " with ABAP function handle ", - (uintptr_t)requestBaton->func_handle); - - // Transform JavaScript parameters' data to ABAP - Napi::Object params = result.ToObject(); - Napi::Array paramNames = params.GetPropertyNames(); - uint_t paramSize = paramNames.Length(); - - errorObj = env.Undefined(); - for (uint_t i = 0; i < paramSize; i++) { - Napi::String name = paramNames.Get(i).ToString(); - Napi::Value value = params.Get(name); - // DEBUG(name, value); - errorObj = setRfmParameter(requestBaton->func_desc_handle, - requestBaton->func_handle, - name, - value, - &requestBaton->errorPath, - &requestBaton->client_options); + (uintptr_t)requestBaton->func_handle, + " returned ", + (jsResult.IsPromise()) ? "promise" : "data"); + + if (jsResult.IsPromise()) { + // JS server function returned promise + Napi::Promise jsPromise = jsResult.As(); + Napi::Function jsThen = jsPromise.Get("then").As(); + jsThen.Call(jsPromise, + {Napi::Function::New(env, + [=](const CallbackInfo& info) { + Object result = info[0].As(); + processResult(env, result, requestBaton); + }), + Napi::Function::New(env, + [=](const CallbackInfo& info) { + Object result = info[0].As(); + UNUSED(result); + // todo error handling + }) + + }); - if (!errorObj.IsUndefined()) { - break; - } + } else { + // JS server function returned data + processResult(env, jsResult, requestBaton); } - - // Release the mutex, so that genericRequestHandler can return the result - // to ABAP - requestBaton->done(); } } // namespace node_rfc \ No newline at end of file diff --git a/src/cpp/noderfc.h b/src/cpp/noderfc.h index 59b3706..6862ebd 100644 --- a/src/cpp/noderfc.h +++ b/src/cpp/noderfc.h @@ -16,7 +16,7 @@ // // Logging // -// #define LOG_RFC_CLIENT 1 +#define LOG_RFC_CLIENT 1 #ifdef LOG_RFC_CLIENT // Version unit test will fail, preventing the release with activated logging