From 8156aa55b969099fe4fd13c7567f526327e6abcb Mon Sep 17 00:00:00 2001 From: ylq121 Date: Mon, 8 May 2023 19:20:59 +0800 Subject: [PATCH] fix : napi Signed-off-by: ylq121 --- .../frameworks/js/napi/cloud_data/BUILD.gn | 57 ++++ .../js/napi/cloud_data/include/js_config.h | 50 ++++ .../cloud_data/include/js_const_properties.h | 25 ++ .../napi/cloud_data/include/js_error_utils.h | 69 +++++ .../js/napi/cloud_data/include/napi_queue.h | 142 +++++++++ .../js/napi/cloud_data/src/entry_point.cpp | 41 +++ .../js/napi/cloud_data/src/js_config.cpp | 271 ++++++++++++++++++ .../cloud_data/src/js_const_properties.cpp | 53 ++++ .../js/napi/cloud_data/src/js_error_utils.cpp | 91 ++++++ .../js/napi/cloud_data/src/napi_queue.cpp | 163 +++++++++++ .../js/napi/common/include/js_utils.h | 2 + .../js/napi/common/src/js_utils.cpp | 54 ++++ .../interfaces/inner_api/rdb/BUILD.gn | 5 + 13 files changed, 1023 insertions(+) create mode 100644 relational_store/frameworks/js/napi/cloud_data/include/js_config.h create mode 100644 relational_store/frameworks/js/napi/cloud_data/include/js_const_properties.h create mode 100644 relational_store/frameworks/js/napi/cloud_data/include/js_error_utils.h create mode 100644 relational_store/frameworks/js/napi/cloud_data/include/napi_queue.h create mode 100644 relational_store/frameworks/js/napi/cloud_data/src/entry_point.cpp create mode 100644 relational_store/frameworks/js/napi/cloud_data/src/js_config.cpp create mode 100644 relational_store/frameworks/js/napi/cloud_data/src/js_const_properties.cpp create mode 100644 relational_store/frameworks/js/napi/cloud_data/src/js_error_utils.cpp create mode 100644 relational_store/frameworks/js/napi/cloud_data/src/napi_queue.cpp diff --git a/relational_store/frameworks/js/napi/cloud_data/BUILD.gn b/relational_store/frameworks/js/napi/cloud_data/BUILD.gn index e69de29b..c45229c2 100644 --- a/relational_store/frameworks/js/napi/cloud_data/BUILD.gn +++ b/relational_store/frameworks/js/napi/cloud_data/BUILD.gn @@ -0,0 +1,57 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# 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. +import("//build/ohos.gni") +import("//build/ohos/ace/ace.gni") +import("//foundation/distributeddatamgr/relational_store/relational_store.gni") + +ohos_copy("relational_store_declaration") { + sources = [ "./api" ] + outputs = [ target_out_dir + "/$target_name/" ] + module_source_dir = target_out_dir + "/$target_name" + module_install_name = "" +} + +ohos_shared_library("clouddata") { + sources = [ + "../common/src/js_utils.cpp", + "src/entry_point.cpp", + "src/js_config.cpp", + "src/js_const_properties.cpp", + "src/js_error_utils.cpp", + "src/napi_queue.cpp", + ] + include_dirs = [ + "include", + "${relational_store_js_common_path}/include", + "${relational_store_napi_path}/cloud_data/include", + "${relational_store_innerapi_path}/cloud_data/include", + "//foundation/distributeddatamgr/kv_store/frameworks/common", + "${relational_store_napi_path}/common/include", + ] + defines = [ "SQLITE_DISTRIBUTE_RELATIONAL" ] + + external_deps = [ + "ability_runtime:abilitykit_native", + "ability_runtime:napi_base_context", + "c_utils:utils", + "hitrace_native:hitrace_meter", + "hiviewdfx_hilog_native:libhilog", + "napi:ace_napi", + "relational_store:native_appdatafwk", + "relational_store:native_rdb", + ] + + subsystem_name = "distributeddatamgr" + part_name = "relational_store" + relative_install_dir = "module/data" +} diff --git a/relational_store/frameworks/js/napi/cloud_data/include/js_config.h b/relational_store/frameworks/js/napi/cloud_data/include/js_config.h new file mode 100644 index 00000000..e0b1c2bf --- /dev/null +++ b/relational_store/frameworks/js/napi/cloud_data/include/js_config.h @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2023 Huawei Device Co., Ltd. +* 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. +*/ + +#ifndef LDBPROJ_JS_CONFIG_H +#define LDBPROJ_JS_CONFIG_H +#include "cloud_manager.h" +#include "js_const_properties.h" +#include "log_print.h" + +namespace OHOS::CloudData { +class JsConfig { +public: + JsConfig(); + ~JsConfig(); + static napi_value InitConfig(napi_env env, napi_value exports); + static napi_value New(napi_env env, napi_callback_info info); + + enum { + /* exported js Action is (CloudData::Action-1) */ + CLEAR_CLOUD_INFO = 0, + CLEAR_CLOUD_DATA_AND_INFO = 1, + }; + + static bool ValidSubscribeType(int32_t type) + { + return (CLEAR_CLOUD_INFO <= type) && (type <= CLEAR_CLOUD_DATA_AND_INFO); + } + + static napi_value EnableCloud(napi_env env, napi_callback_info info); + static napi_value DisableCloud(napi_env env, napi_callback_info info); + static napi_value ChangeAppCloudSwitch(napi_env env, napi_callback_info info); + static napi_value Clean(napi_env env, napi_callback_info info); + static napi_value NotifyDataChange(napi_env env, napi_callback_info info); +}; + +} // namespace OHOS::CloudData + +#endif //LDBPROJ_JS_CONFIG_H diff --git a/relational_store/frameworks/js/napi/cloud_data/include/js_const_properties.h b/relational_store/frameworks/js/napi/cloud_data/include/js_const_properties.h new file mode 100644 index 00000000..6245a158 --- /dev/null +++ b/relational_store/frameworks/js/napi/cloud_data/include/js_const_properties.h @@ -0,0 +1,25 @@ +/* +* Copyright (c) 2023 Huawei Device Co., Ltd. +* 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. +*/ + +#ifndef LDBPROJ_JS_CONST_PROPERTIES_H +#define LDBPROJ_JS_CONST_PROPERTIES_H +#include "napi/native_api.h" +#include "napi/native_common.h" +#include "napi/native_node_api.h" + +namespace OHOS::CloudData { +napi_status InitConstProperties(napi_env env, napi_value exports); +} // namespace OHOS::CloudData +#endif //LDBPROJ_JS_CONST_PROPERTIES_H diff --git a/relational_store/frameworks/js/napi/cloud_data/include/js_error_utils.h b/relational_store/frameworks/js/napi/cloud_data/include/js_error_utils.h new file mode 100644 index 00000000..fbbbc6b7 --- /dev/null +++ b/relational_store/frameworks/js/napi/cloud_data/include/js_error_utils.h @@ -0,0 +1,69 @@ +/* +* Copyright (c) 2023 Huawei Device Co., Ltd. +* 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. +*/ + +#ifndef LDBPROJ_JS_ERROR_UTILS_H +#define LDBPROJ_JS_ERROR_UTILS_H +#include +#include + +#include "cloud_service.h" +#include "js_native_api.h" +#include "log_print.h" +#include "napi/native_common.h" + +namespace OHOS { +namespace CloudData { +using Status = OHOS::CloudData::CloudService::Status; + +struct JsErrorCode { + int32_t status; + int32_t jsCode; + const char *message; +}; + +const std::optional GetJsErrorCode(int32_t errorCode); +Status GenerateNapiError(Status status, int32_t &errCode, std::string &errMessage); +void ThrowNapiError(napi_env env, int32_t errCode, std::string errMessage, bool isParamsCheck = true); +napi_value GenerateErrorMsg(napi_env env, JsErrorCode jsInfo); + +#define ASSERT_ERR(env, assertion, errorCode, message) \ + do { \ + if (!(assertion)) { \ + ThrowNapiError(env, errorCode, message); \ + return nullptr; \ + } \ + } while (0) + +#define ASSERT_BUSINESS_ERR(ctxt, assertion, errorCode, message) \ + do { \ + if (!(assertion)) { \ + (ctxt)->isThrowError = true; \ + ThrowNapiError((ctxt)->env, errorCode, message); \ + return; \ + } \ + } while (0) + +#define ASSERT_PERMISSION_ERR(ctxt, assertion, errorCode, message) \ + do { \ + if (!(assertion)) { \ + (ctxt)->isThrowError = true; \ + ThrowNapiError((ctxt)->env, errorCode, message, false); \ + return; \ + } \ + } while (0) + +} // namespace CloudData +} // namespace OHOS +#endif //LDBPROJ_JS_ERROR_UTILS_H diff --git a/relational_store/frameworks/js/napi/cloud_data/include/napi_queue.h b/relational_store/frameworks/js/napi/cloud_data/include/napi_queue.h new file mode 100644 index 00000000..e21cbc83 --- /dev/null +++ b/relational_store/frameworks/js/napi/cloud_data/include/napi_queue.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * 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. + */ +#ifndef LDBPROJ_NAPI_QUEUE_H +#define LDBPROJ_NAPI_QUEUE_H +#include +#include +#include + +#include "log_print.h" +#include "napi/native_api.h" +#include "napi/native_common.h" +#include "napi/native_node_api.h" + +namespace OHOS::CloudData { +constexpr size_t ARGC_MAX = 6; +using NapiCbInfoParser = std::function; +using NapiAsyncExecute = std::function; +using NapiAsyncComplete = std::function; + +struct ContextBase { + virtual ~ContextBase(); + void GetCbInfo( + napi_env env, napi_callback_info info, NapiCbInfoParser parse = NapiCbInfoParser(), bool sync = false); + + inline void GetCbInfoSync(napi_env env, napi_callback_info info, NapiCbInfoParser parse = NapiCbInfoParser()) + { + /* sync = true, means no callback, not AsyncWork. */ + GetCbInfo(env, info, parse, true); + } + + napi_env env = nullptr; + napi_value output = nullptr; + napi_status status = napi_invalid_arg; + std::string error; + int32_t jsCode = 0; + bool isThrowError = false; + + napi_value self = nullptr; + void *native = nullptr; + +private: + napi_ref callbackRef = nullptr; + napi_ref selfRef = nullptr; + friend class NapiQueue; +}; + +/* check condition related to argc/argv, return and logging. */ +#define ASSERT_ARGS(ctxt, condition, message) \ + do { \ + if (!(condition)) { \ + (ctxt)->status = napi_invalid_arg; \ + (ctxt)->error = std::string(message); \ + ZLOGE("test (" #condition ") failed: " message); \ + return; \ + } \ + } while (0) + +#define ASSERT_STATUS(ctxt, message) \ + do { \ + if ((ctxt)->status != napi_ok) { \ + (ctxt)->error = std::string(message); \ + ZLOGE("test (ctxt->status == napi_ok) failed: " message); \ + return; \ + } \ + } while (0) + +/* check condition, return and logging if condition not true. */ +#define ASSERT(condition, message, retVal) \ + do { \ + if (!(condition)) { \ + ZLOGE("test (" #condition ") failed: " message); \ + return retVal; \ + } \ + } while (0) + +#define ASSERT_VOID(condition, message) \ + do { \ + if (!(condition)) { \ + ZLOGE("test (" #condition ") failed: " message); \ + return; \ + } \ + } while (0) + +#define ASSERT_NULL(condition, message) ASSERT(condition, message, nullptr) + +#define ASSERT_CALL(env, theCall, object) \ + do { \ + if ((theCall) != napi_ok) { \ + delete (object); \ + GET_AND_THROW_LAST_ERROR((env)); \ + return nullptr; \ + } \ + } while (0) + +class NapiQueue { +public: + static napi_value AsyncWork(napi_env env, std::shared_ptr ctxt, const std::string &name, + NapiAsyncExecute execute = NapiAsyncExecute(), NapiAsyncComplete complete = NapiAsyncComplete()); + +private: + enum { + /* AsyncCallback / Promise output result index */ + RESULT_ERROR = 0, + RESULT_DATA = 1, + RESULT_ALL = 2 + }; + + struct AsyncContext { + napi_env env = nullptr; + std::shared_ptr ctx; + NapiAsyncExecute execute = nullptr; + NapiAsyncComplete complete = nullptr; + napi_deferred deferred = nullptr; + napi_async_work work = nullptr; + ~AsyncContext() + { + execute = nullptr; + complete = nullptr; + ctx = nullptr; + if (env != nullptr) { + if (work != nullptr) { + napi_delete_async_work(env, work); + } + } + } + }; + static void GenerateOutput(AsyncContext &ctx, napi_value output); +}; +} // namespace OHOS::CloudData +#endif //LDBPROJ_NAPI_QUEUE_H diff --git a/relational_store/frameworks/js/napi/cloud_data/src/entry_point.cpp b/relational_store/frameworks/js/napi/cloud_data/src/entry_point.cpp new file mode 100644 index 00000000..a6f5e84a --- /dev/null +++ b/relational_store/frameworks/js/napi/cloud_data/src/entry_point.cpp @@ -0,0 +1,41 @@ +/* +* Copyright (c) 2023 Huawei Device Co., Ltd. +* 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. +*/ +#define LOG_TAG "EntryPoint" +#include "js_config.h" +#include "js_const_properties.h" +#include "log_print.h" + +using namespace OHOS::CloudData; + +static napi_value Init(napi_env env, napi_value exports) +{ + exports = JsConfig::InitConfig(env, exports); + napi_status status = InitConstProperties(env, exports); + ZLOGI("init Enumerate Constants %{public}d", status); + return exports; +} + +static __attribute__((constructor)) void RegisterModule() +{ + static napi_module module = { .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "data.cloudData", + .nm_priv = ((void *)0), + .reserved = { 0 } }; + napi_module_register(&module); + ZLOGI("module register data.cloudData"); +} diff --git a/relational_store/frameworks/js/napi/cloud_data/src/js_config.cpp b/relational_store/frameworks/js/napi/cloud_data/src/js_config.cpp new file mode 100644 index 00000000..36873d07 --- /dev/null +++ b/relational_store/frameworks/js/napi/cloud_data/src/js_config.cpp @@ -0,0 +1,271 @@ +/* +* Copyright (c) 2023 Huawei Device Co., Ltd. +* 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. +*/ +#define LOG_TAG "JsConfig" +#include "js_config.h" + +#include "cloud_manager.h" +#include "js_error_utils.h" +#include "js_utils.h" +#include "log_print.h" +#include "napi_queue.h" + +using namespace OHOS::CloudData; +using namespace OHOS::AppDataMgrJsKit; +JsConfig::JsConfig() +{ +} + +JsConfig::~JsConfig() +{ +} + +/* + * [JS API Prototype] + * [AsyncCallback] + * enableCloud(accountId: string, switches: {[bundleName: string]: boolean}, callback: AsyncCallback): void; + * [Promise] + * enableCloud(accountId: string, switches: {[bundleName: string]: boolean}): Promise; + */ +napi_value JsConfig::EnableCloud(napi_env env, napi_callback_info info) +{ + struct EnableCloudContext : public ContextBase { + std::string accountId; + std::map tempSwitches; + std::map switches; + }; + auto ctxt = std::make_shared(); + ctxt->GetCbInfo(env, info, [env, ctxt](size_t argc, napi_value *argv) { + // required 2 arguments :: + ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect."); + int status = JSUtils::Convert2String(env, argv[0], ctxt->accountId); + ASSERT_BUSINESS_ERR( + ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, "The type of accountId must be string."); + status = JSUtils::Covert2StingBoolMap(env, argv[1], ctxt->tempSwitches); + ASSERT_BUSINESS_ERR(ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, + "The type of switches must be {[bundleName: string]: boolean}."); + for (auto item : ctxt->tempSwitches) { + ctxt->switches.insert(std::pair(item.first, static_cast(item.second))); + } + }); + + ASSERT_NULL(!ctxt->isThrowError, "EnableCloud exit"); + + auto execute = [ctxt]() { + auto proxy = CloudManager::GetInstance().GetCloudService(); + int32_t cStatus = proxy->EnableCloud(ctxt->accountId, ctxt->switches); + ZLOGD("EnableCloud return %{public}d", cStatus); + ctxt->status = (GenerateNapiError(static_cast(cStatus), ctxt->jsCode, ctxt->error) == Status::SUCCESS) + ? napi_ok + : napi_generic_failure; + }; + return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute); +} + +/* + * [JS API Prototype] + * [AsyncCallback] + * disableCloud(accountId: string, callback: AsyncCallback): void; + * [Promise] + * disableCloud(accountId: string): Promise; + */ +napi_value JsConfig::DisableCloud(napi_env env, napi_callback_info info) +{ + struct DisableCloudContext : public ContextBase { + std::string accountId; + }; + auto ctxt = std::make_shared(); + ctxt->GetCbInfo(env, info, [env, ctxt](size_t argc, napi_value *argv) { + // required 1 arguments :: + ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT, "The number of parameters is incorrect."); + int status = JSUtils::Convert2String(env, argv[0], ctxt->accountId); + ASSERT_BUSINESS_ERR( + ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, "The type of accountId must be string."); + }); + + ASSERT_NULL(!ctxt->isThrowError, "DisableCloud exit"); + + auto execute = [ctxt]() { + auto proxy = CloudManager::GetInstance().GetCloudService(); + int32_t cStatus = proxy->DisableCloud(ctxt->accountId); + ZLOGD("DisableCloud return %{public}d", cStatus); + ctxt->status = (GenerateNapiError(static_cast(cStatus), ctxt->jsCode, ctxt->error) == Status::SUCCESS) + ? napi_ok + : napi_generic_failure; + }; + return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute); +} + +/* + * [JS API Prototype] + * [AsyncCallback] + * changeAppCloudSwitch(accountId: string, bundleName: string, status :boolean, callback: AsyncCallback): void; + * [Promise] + * changeAppCloudSwitch(accountId: string, bundleName: string, status :boolean): Promise; + */ +napi_value JsConfig::ChangeAppCloudSwitch(napi_env env, napi_callback_info info) +{ + struct ChangeAppSwitchContext : public ContextBase { + std::string accountId; + std::string bundleName; + bool state; + }; + auto ctxt = std::make_shared(); + ctxt->GetCbInfo(env, info, [env, ctxt](size_t argc, napi_value *argv) { + // required 3 arguments :: + ASSERT_BUSINESS_ERR(ctxt, argc >= 3, Status::INVALID_ARGUMENT, "The number of parameters is incorrect."); + int status = JSUtils::Convert2String(env, argv[0], ctxt->accountId); + ASSERT_BUSINESS_ERR( + ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, "The type of accountId must be string."); + status = JSUtils::Convert2String(env, argv[1], ctxt->bundleName); + ASSERT_BUSINESS_ERR( + ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, "The type of bundleName must be string."); + status = JSUtils::Convert2Bool(env, argv[2], ctxt->state); + ASSERT_BUSINESS_ERR( + ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, "The type of bundleName must be boolean."); + }); + + ASSERT_NULL(!ctxt->isThrowError, "ChangeAppCloudSwitch exit"); + + auto execute = [ctxt]() { + auto proxy = CloudManager::GetInstance().GetCloudService(); + int32_t cStatus = proxy->ChangeAppSwitch(ctxt->accountId, ctxt->bundleName, ctxt->state); + ZLOGD("ChangeAppCloudSwitch return %{public}d", cStatus); + ctxt->status = (GenerateNapiError(static_cast(cStatus), ctxt->jsCode, ctxt->error) == Status::SUCCESS) + ? napi_ok + : napi_generic_failure; + }; + return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute); +} + +/* + * [JS API Prototype] + * [AsyncCallback] + * clean(accountId: string, appActions: {[bundleName: string]: Action}, callback: AsyncCallback): void; + * [Promise] + * clean(accountId: string, appActions: {[bundleName: string]: Action}): Promise; + */ +napi_value JsConfig::Clean(napi_env env, napi_callback_info info) +{ + struct CleanContext : public ContextBase { + std::string accountId; + std::map actions; + }; + auto ctxt = std::make_shared(); + ctxt->GetCbInfo(env, info, [env, ctxt](size_t argc, napi_value *argv) { + // required 2 arguments :: + ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect."); + int status = JSUtils::Convert2String(env, argv[0], ctxt->accountId); + ASSERT_BUSINESS_ERR( + ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, "The type of accountId must be string."); + status = JSUtils::Convert2StringInt32Map(env, argv[1], ctxt->actions); + ASSERT_BUSINESS_ERR(ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, + "The type of actions must be {[bundleName: string]: int32_t}."); + for (auto item : ctxt->actions) { + ASSERT_BUSINESS_ERR( + ctxt, ValidSubscribeType(item.second), Status::INVALID_ARGUMENT, "The type of map is incorrect."); + } + }); + + ASSERT_NULL(!ctxt->isThrowError, "Clean exit"); + + auto execute = [ctxt]() { + auto proxy = CloudManager::GetInstance().GetCloudService(); + int32_t cStatus = proxy->Clean(ctxt->accountId, ctxt->actions); + ZLOGD("Clean return %{public}d", cStatus); + ctxt->status = (GenerateNapiError(static_cast(cStatus), ctxt->jsCode, ctxt->error) == Status::SUCCESS) + ? napi_ok + : napi_generic_failure; + }; + return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute); +} + +/* + * [JS API Prototype] + * [AsyncCallback] + * notifyDataChange(accountId: string, bundleName: string, callback: AsyncCallback): void; + * [Promise] + * notifyDataChange(accountId: string, bundleName: string): Promise; + */ +napi_value JsConfig::NotifyDataChange(napi_env env, napi_callback_info info) +{ + struct ChangeAppSwitchContext : public ContextBase { + std::string accountId; + std::string bundleName; + }; + auto ctxt = std::make_shared(); + ctxt->GetCbInfo(env, info, [env, ctxt](size_t argc, napi_value *argv) { + // required 2 arguments :: + ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT, "The number of parameters is incorrect."); + int status = JSUtils::Convert2String(env, argv[0], ctxt->accountId); + ASSERT_BUSINESS_ERR( + ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, "The type of accountId must be string."); + status = JSUtils::Convert2String(env, argv[1], ctxt->bundleName); + ASSERT_BUSINESS_ERR( + ctxt, status == JSUtils::OK, Status::INVALID_ARGUMENT, "The type of bundleName must be string."); + }); + + ASSERT_NULL(!ctxt->isThrowError, "NotifyDataChange exit"); + + auto execute = [ctxt]() { + auto proxy = CloudManager::GetInstance().GetCloudService(); + int32_t cStatus = proxy->NotifyDataChange(ctxt->accountId, ctxt->bundleName); + ZLOGD("NotifyDataChange return %{public}d", cStatus); + ctxt->status = (GenerateNapiError(static_cast(cStatus), ctxt->jsCode, ctxt->error) == Status::SUCCESS) + ? napi_ok + : napi_generic_failure; + }; + return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute); +} + +napi_value JsConfig::New(napi_env env, napi_callback_info info) +{ + napi_value self; + NAPI_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &self, nullptr)); + auto finalize = [](napi_env env, void *data, void *hint) { + ZLOGD("cloudConfig finalize."); + auto *config = reinterpret_cast(data); + ASSERT_VOID(config != nullptr, "finalize null!"); + delete config; + }; + JsConfig *cloudConfig = new (std::nothrow) JsConfig(); + ASSERT_ERR(env, cloudConfig != nullptr, Status::INVALID_ARGUMENT, "no memory for cloudConfig."); + napi_status status = napi_wrap(env, self, cloudConfig, finalize, nullptr, nullptr); + if (status != napi_ok) { + ZLOGE("JsConfig::Initialize napi_wrap failed! code:%{public}d!", status); + finalize(env, cloudConfig, nullptr); + return nullptr; + } + return self; +} + +napi_value JsConfig::InitConfig(napi_env env, napi_value exports) +{ + const napi_property_descriptor desc[] = { + DECLARE_NAPI_STATIC_FUNCTION("enableCloud", JsConfig::EnableCloud), + DECLARE_NAPI_STATIC_FUNCTION("disableCloud", JsConfig::DisableCloud), + DECLARE_NAPI_STATIC_FUNCTION("changeAppCloudSwitch", JsConfig::ChangeAppCloudSwitch), + DECLARE_NAPI_STATIC_FUNCTION("clean", JsConfig::Clean), + DECLARE_NAPI_STATIC_FUNCTION("notifyDataChange", JsConfig::NotifyDataChange), + + }; + size_t count = sizeof(desc) / sizeof(desc[0]); + napi_status status = napi_define_properties(env, exports, count, desc); + ZLOGI("init cloudData config %{public}d", status); + napi_value cons = nullptr; + NAPI_CALL(env, napi_define_class(env, "Config", NAPI_AUTO_LENGTH, New, nullptr, + sizeof(desc) / sizeof(napi_property_descriptor), desc, &cons)); + NAPI_CALL(env, napi_set_named_property(env, exports, "Config", cons)); + return exports; +} \ No newline at end of file diff --git a/relational_store/frameworks/js/napi/cloud_data/src/js_const_properties.cpp b/relational_store/frameworks/js/napi/cloud_data/src/js_const_properties.cpp new file mode 100644 index 00000000..60f4df42 --- /dev/null +++ b/relational_store/frameworks/js/napi/cloud_data/src/js_const_properties.cpp @@ -0,0 +1,53 @@ +/* +* Copyright (c) 2023 Huawei Device Co., Ltd. +* 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. +*/ +#define LOG_TAG "Const_Properties" +#include "js_const_properties.h" + +#include "cloud_service.h" +#include "log_print.h" +#include "napi_queue.h" + +using Action = OHOS::CloudData::CloudService::Action; +namespace OHOS::CloudData { +static napi_status SetNamedProperty(napi_env env, napi_value &obj, const std::string &name, int32_t value) +{ + napi_value property = nullptr; + napi_status status = napi_create_int32(env, value, &property); + ASSERT(status == napi_ok, "int32_t to napi_value failed!", status); + status = napi_set_named_property(env, obj, name.c_str(), property); + ASSERT(status == napi_ok, "napi_set_named_property failed!", status); + return status; +} + +static napi_value ExportAction(napi_env env) +{ + napi_value action = nullptr; + napi_create_object(env, &action); + SetNamedProperty(env, action, "CLEAR_CLOUD_INFO", (int32_t)Action::CLEAR_CLOUD_INFO); + SetNamedProperty(env, action, "CLEAR_CLOUD_DATA_AND_INFO", (int32_t)Action::CLEAR_CLOUD_DATA_AND_INFO); + napi_object_freeze(env, action); + return action; +} + +napi_status InitConstProperties(napi_env env, napi_value exports) +{ + const napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("Action", ExportAction(env)), + }; + size_t count = sizeof(properties) / sizeof(properties[0]); + + return napi_define_properties(env, exports, count, properties); +} +} // namespace OHOS::CloudData diff --git a/relational_store/frameworks/js/napi/cloud_data/src/js_error_utils.cpp b/relational_store/frameworks/js/napi/cloud_data/src/js_error_utils.cpp new file mode 100644 index 00000000..ce8a797e --- /dev/null +++ b/relational_store/frameworks/js/napi/cloud_data/src/js_error_utils.cpp @@ -0,0 +1,91 @@ +/* +* Copyright (c) 2023 Huawei Device Co., Ltd. +* 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. +*/ +#define LOG_TAG "JS_ERROR_UTILS" + +#include "js_error_utils.h" + +#include + +namespace OHOS::CloudData { +using JsErrorCode = OHOS::CloudData::JsErrorCode; + +static constexpr JsErrorCode JS_ERROR_CODE_MSGS[] = { + { Status::INVALID_ARGUMENT, 401, "Parameter error." }, +}; + +const std::optional GetJsErrorCode(int32_t errorCode) +{ + auto jsErrorCode = JsErrorCode{ errorCode, -1, "" }; + auto iter = std::lower_bound(JS_ERROR_CODE_MSGS, + JS_ERROR_CODE_MSGS + sizeof(JS_ERROR_CODE_MSGS) / sizeof(JS_ERROR_CODE_MSGS[0]), jsErrorCode, + [](const JsErrorCode &jsErrorCode1, const JsErrorCode &jsErrorCode2) { + return jsErrorCode1.status < jsErrorCode2.status; + }); + if (iter < JS_ERROR_CODE_MSGS + sizeof(JS_ERROR_CODE_MSGS) / sizeof(JS_ERROR_CODE_MSGS[0]) + && iter->status == errorCode) { + return *iter; + } + return std::nullopt; +} + +Status GenerateNapiError(Status status, int32_t &errCode, std::string &errMessage) +{ + auto errorMsg = GetJsErrorCode(status); + if (errorMsg.has_value()) { + auto napiError = errorMsg.value(); + errCode = napiError.jsCode; + errMessage = napiError.message; + } else { + // unmatched status return unified error code + errCode = -1; + errMessage = ""; + } + ZLOGD("GenerateNapiError errCode is %{public}d", errCode); + if (errCode == 0) { + return Status::SUCCESS; + } + return status; +} + +void ThrowNapiError(napi_env env, int32_t status, std::string errMessage, bool isParamsCheck) +{ + ZLOGD("ThrowNapiError message: %{public}s", errMessage.c_str()); + if (status == Status::SUCCESS) { + return; + } + auto errorMsg = GetJsErrorCode(status); + JsErrorCode napiError; + if (errorMsg.has_value()) { + napiError = errorMsg.value(); + } else { + napiError.jsCode = -1; + napiError.message = ""; + } + + std::string message(napiError.message); + if (isParamsCheck) { + napiError.jsCode = 401; + message += errMessage; + } + + std::string jsCode; + if (napiError.jsCode == -1) { + jsCode = ""; + } else { + jsCode = std::to_string(napiError.jsCode); + } + napi_throw_error(env, jsCode.c_str(), message.c_str()); +} +} // namespace OHOS::CloudData diff --git a/relational_store/frameworks/js/napi/cloud_data/src/napi_queue.cpp b/relational_store/frameworks/js/napi/cloud_data/src/napi_queue.cpp new file mode 100644 index 00000000..4db2a0ba --- /dev/null +++ b/relational_store/frameworks/js/napi/cloud_data/src/napi_queue.cpp @@ -0,0 +1,163 @@ +/* +* Copyright (c) 2023 Huawei Device Co., Ltd. +* 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. +*/ +#define LOG_TAG "NapiQueue" +#include "napi_queue.h" + +namespace OHOS::CloudData { +ContextBase::~ContextBase() +{ + ZLOGD("no memory leak after callback or promise[resolved/rejected]"); + if (env != nullptr) { + if (callbackRef != nullptr) { + auto status = napi_delete_reference(env, callbackRef); + ZLOGD("status:%{public}d", status); + } + if (selfRef != nullptr) { + auto status = napi_delete_reference(env, selfRef); + ZLOGD("status:%{public}d", status); + } + env = nullptr; + } +} + +void ContextBase::GetCbInfo(napi_env envi, napi_callback_info info, NapiCbInfoParser parse, bool sync) +{ + env = envi; + size_t argc = ARGC_MAX; + napi_value argv[ARGC_MAX] = { nullptr }; + status = napi_get_cb_info(env, info, &argc, argv, &self, nullptr); + ASSERT_STATUS(this, "napi_get_cb_info failed!"); + ASSERT_ARGS(this, argc <= ARGC_MAX, "too many arguments!"); + ASSERT_ARGS(this, self != nullptr, "no JavaScript this argument!"); + if (!sync) { + napi_create_reference(env, self, 1, &selfRef); + } + status = napi_unwrap(env, self, &native); + ASSERT_STATUS(this, "self unwrap failed!"); + + if (!sync && (argc > 0)) { + // get the last arguments :: + size_t index = argc - 1; + napi_valuetype type = napi_undefined; + napi_status tyst = napi_typeof(env, argv[index], &type); + if ((tyst == napi_ok) && (type == napi_function)) { + status = napi_create_reference(env, argv[index], 1, &callbackRef); + ASSERT_STATUS(this, "ref callback failed!"); + argc = index; + ZLOGD("async callback, no promise"); + } else { + ZLOGD("no callback, async pormose"); + } + } + + if (parse) { + parse(argc, argv); + } else { + ASSERT_ARGS(this, argc == 0, "required no arguments!"); + } +} + +napi_value NapiQueue::AsyncWork(napi_env env, std::shared_ptr ctxt, const std::string &name, + NapiAsyncExecute execute, NapiAsyncComplete complete) +{ + ZLOGD("name=%{public}s", name.c_str()); + AsyncContext *aCtx = new AsyncContext; + aCtx->env = env; + aCtx->ctx = std::move(ctxt); + aCtx->execute = std::move(execute); + aCtx->complete = std::move(complete); + napi_value promise = nullptr; + if (aCtx->ctx->callbackRef == nullptr) { + napi_create_promise(env, &aCtx->deferred, &promise); + ZLOGD("create deferred promise"); + } else { + napi_get_undefined(env, &promise); + } + + napi_value resource = nullptr; + napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &resource); + napi_create_async_work( + env, nullptr, resource, + [](napi_env env, void *data) { + ASSERT_VOID(data != nullptr, "napi_async_execute_callback nullptr"); + auto actx = reinterpret_cast(data); + ZLOGD("napi_async_execute_callback ctxt->status=%{public}d", actx->ctx->status); + if (actx->execute && actx->ctx->status == napi_ok) { + actx->execute(); + } + }, + [](napi_env env, napi_status status, void *data) { + ASSERT_VOID(data != nullptr, "napi_async_complete_callback nullptr"); + auto actx = reinterpret_cast(data); + ZLOGD("napi_async_complete_callback status=%{public}d, ctxt->status=%{public}d", status, actx->ctx->status); + if ((status != napi_ok) && (actx->ctx->status == napi_ok)) { + actx->ctx->status = status; + } + napi_value output = nullptr; + if ((actx->complete) && (status == napi_ok) && (actx->ctx->status == napi_ok)) { + actx->complete(output); + } + GenerateOutput(*actx, output); + delete actx; + }, + reinterpret_cast(aCtx), &aCtx->work); + auto status = napi_queue_async_work(env, aCtx->work); + if (status != napi_ok) { + napi_get_undefined(env, &promise); + delete aCtx; + } + return promise; +} + +void NapiQueue::GenerateOutput(AsyncContext &ctx, napi_value output) +{ + napi_value result[RESULT_ALL] = { nullptr }; + if (ctx.ctx->status == napi_ok) { + napi_get_undefined(ctx.env, &result[RESULT_ERROR]); + if (output == nullptr) { + napi_get_undefined(ctx.env, &output); + } + result[RESULT_DATA] = output; + } else { + napi_value message = nullptr; + napi_value errorCode = nullptr; + if (ctx.ctx->jsCode != 0 && ctx.ctx->jsCode != -1) { + napi_create_string_utf8(ctx.env, std::to_string(ctx.ctx->jsCode).c_str(), NAPI_AUTO_LENGTH, &errorCode); + } + if (ctx.ctx->jsCode == -1) { + std::string jscode = ""; + napi_create_string_utf8(ctx.env, jscode.c_str(), NAPI_AUTO_LENGTH, &errorCode); + } + napi_create_string_utf8(ctx.env, ctx.ctx->error.c_str(), NAPI_AUTO_LENGTH, &message); + napi_create_error(ctx.env, errorCode, message, &result[RESULT_ERROR]); + napi_get_undefined(ctx.env, &result[RESULT_DATA]); + } + if (ctx.deferred != nullptr) { + if (ctx.ctx->status == napi_ok) { + ZLOGD("deferred promise resolved"); + napi_resolve_deferred(ctx.env, ctx.deferred, result[RESULT_DATA]); + } else { + ZLOGD("deferred promise rejected"); + napi_reject_deferred(ctx.env, ctx.deferred, result[RESULT_ERROR]); + } + } else { + napi_value callback = nullptr; + napi_get_reference_value(ctx.env, ctx.ctx->callbackRef, &callback); + napi_value callbackResult = nullptr; + ZLOGD("call callback function"); + napi_call_function(ctx.env, nullptr, callback, RESULT_ALL, result, &callbackResult); + } +} +} // namespace OHOS::CloudData diff --git a/relational_store/frameworks/js/napi/common/include/js_utils.h b/relational_store/frameworks/js/napi/common/include/js_utils.h index 8de89fe8..50596fb8 100644 --- a/relational_store/frameworks/js/napi/common/include/js_utils.h +++ b/relational_store/frameworks/js/napi/common/include/js_utils.h @@ -63,6 +63,8 @@ int32_t Convert2Value(napi_env env, napi_value jsValue, double &output); int32_t Convert2Value(napi_env env, napi_value jsValue, std::string &output); int32_t Convert2Value(napi_env env, napi_value jsValue, std::vector &output); int32_t Convert2Value(napi_env env, napi_value jsValue, std::monostate &value); +int Covert2StingBoolMap(napi_env env, napi_value jsValue, std::map &output); +int Convert2StringInt32Map(napi_env env, napi_value jsValue, std::map &output); template int32_t Convert2Value(napi_env env, napi_value jsValue, T &output); diff --git a/relational_store/frameworks/js/napi/common/src/js_utils.cpp b/relational_store/frameworks/js/napi/common/src/js_utils.cpp index 86a5db95..26456654 100644 --- a/relational_store/frameworks/js/napi/common/src/js_utils.cpp +++ b/relational_store/frameworks/js/napi/common/src/js_utils.cpp @@ -217,6 +217,60 @@ int32_t JSUtils::Convert2Value(napi_env env, napi_value jsValue, std::monostate return ERR; } +int JSUtils::Covert2StingBoolMap(napi_env env, napi_value jsValue, std::map &output) +{ + LOG_DEBUG("napi_value -> std::map "); + output.clear(); + napi_value jsMapList = nullptr; + uint32_t jsCount = 0; + napi_status status = napi_get_property_names(env, jsValue, &jsMapList); + status = napi_get_array_length(env, jsMapList, &jsCount); + LOG_DEBUG("yq-COUNT: %{public}d", jsCount); + NAPI_ASSERT_BASE(env, (status == napi_ok) && (jsCount > 0), "get_map failed!", ERR); + napi_value jsKey = nullptr; + napi_value jsVal = nullptr; + for (uint32_t index = 0; index < jsCount; index++) { + status = napi_get_element(env, jsMapList, index, &jsKey); + NAPI_ASSERT_BASE(env, (jsKey != nullptr) && (status == napi_ok), "no element", ERR); + std::string key; + Convert2String(env, jsKey, key); + LOG_DEBUG("yq-key: %{public}s", key.c_str()); + status = napi_get_property(env, jsValue, jsKey, &jsVal); + NAPI_ASSERT_BASE(env, (jsVal != nullptr) && (status == napi_ok), "no element", ERR); + bool val; + Convert2Bool(env, jsVal, val); + output.insert(std::pair(key, val)); + } + return OK; +} + +int JSUtils::Convert2StringInt32Map(napi_env env, napi_value jsValue, std::map &output) +{ + LOG_DEBUG("napi_value -> std::map "); + output.clear(); + napi_value jsMapList = nullptr; + uint32_t jsCount = 0; + napi_status status = napi_get_property_names(env, jsValue, &jsMapList); + status = napi_get_array_length(env, jsMapList, &jsCount); + LOG_DEBUG("yq-COUNT: %{public}d", jsCount); + NAPI_ASSERT_BASE(env, (status == napi_ok) && (jsCount > 0), "get_map failed!", ERR); + napi_value jsKey = nullptr; + napi_value jsVal = nullptr; + for (uint32_t index = 0; index < jsCount; index++) { + status = napi_get_element(env, jsMapList, index, &jsKey); + NAPI_ASSERT_BASE(env, (jsKey != nullptr) && (status == napi_ok), "no element", ERR); + std::string key; + Convert2String(env, jsKey, key); + LOG_DEBUG("yq-key: %{public}s", key.c_str()); + status = napi_get_property(env, jsValue, jsKey, &jsVal); + NAPI_ASSERT_BASE(env, (jsVal != nullptr) && (status == napi_ok), "no element", ERR); + int32_t val; + Convert2Int32(env, jsVal, val); + output.insert(std::pair(key, val)); + } + return OK; +} + napi_value JSUtils::Convert2JSValue(napi_env env, const std::vector &value) { napi_value jsValue; diff --git a/relational_store/interfaces/inner_api/rdb/BUILD.gn b/relational_store/interfaces/inner_api/rdb/BUILD.gn index 4003c7ef..1e90fddc 100644 --- a/relational_store/interfaces/inner_api/rdb/BUILD.gn +++ b/relational_store/interfaces/inner_api/rdb/BUILD.gn @@ -35,6 +35,9 @@ config("native_rdb_config") { "//foundation/distributeddatamgr/kv_store/frameworks/libs/distributeddb/include/", "//foundation/distributeddatamgr/kv_store/frameworks/libs/distributeddb/interfaces/include/", "//foundation/distributeddatamgr/kv_store/frameworks/libs/distributeddb/interfaces/include/relational", + "${relational_store_innerapi_path}/cloud_data/include", + "${relational_store_native_path}/cloud_data/include", + "${relational_store_base_path}/interfaces/inner_api/cloud_data/include" ] } @@ -162,6 +165,8 @@ ohos_shared_library("native_rdb") { "${relational_store_native_path}/rdb/src/shared_block_serializer_info.cpp", "${relational_store_native_path}/rdb/src/sqlite_shared_result_set.cpp", "${relational_store_native_path}/rdb_device_manager_adapter/src/rdb_device_manager_adapter.cpp", + "${relational_store_native_path}/cloud_data/src/cloud_manager.cpp", + "${relational_store_native_path}/cloud_data/src/cloud_service_proxy.cpp", ] public_deps = -- Gitee