From 958cf3fa9a95ab5ad23cf1b0dadc5b55a36db498 Mon Sep 17 00:00:00 2001 From: Sven Wang Date: Wed, 30 Jun 2021 16:53:32 +0800 Subject: [PATCH] add javascript interfaces https://gitee.com/openharmony/distributeddatamgr_datamgr/issues/I3YC8O Signed-off-by: Sven Wang --- README.md | 1 + README_zh.md | 1 + .../distributeddata/include/async_call.h | 104 ++++++ .../distributeddata/include/js_util.h | 71 ++++ .../distributeddata/include/kv_manager.h | 37 ++ .../distributeddata/include/single_kv_store.h | 105 ++++++ .../distributeddata/src/async_call.cpp | 146 ++++++++ .../distributeddata/src/entry_point.cpp | 45 +++ .../distributeddata/src/js_util.cpp | 296 ++++++++++++++++ .../distributeddata/src/kv_manager.cpp | 155 ++++++++ .../distributeddata/src/single_kv_store.cpp | 332 ++++++++++++++++++ interfaces/jskits/distributeddata/BUILD.gn | 72 ++++ ohos.build | 4 + 13 files changed, 1369 insertions(+) create mode 100644 frameworks/jskitsimpl/distributeddata/include/async_call.h create mode 100644 frameworks/jskitsimpl/distributeddata/include/js_util.h create mode 100644 frameworks/jskitsimpl/distributeddata/include/kv_manager.h create mode 100644 frameworks/jskitsimpl/distributeddata/include/single_kv_store.h create mode 100644 frameworks/jskitsimpl/distributeddata/src/async_call.cpp create mode 100644 frameworks/jskitsimpl/distributeddata/src/entry_point.cpp create mode 100644 frameworks/jskitsimpl/distributeddata/src/js_util.cpp create mode 100644 frameworks/jskitsimpl/distributeddata/src/kv_manager.cpp create mode 100644 frameworks/jskitsimpl/distributeddata/src/single_kv_store.cpp create mode 100644 interfaces/jskits/distributeddata/BUILD.gn diff --git a/README.md b/README.md index 2d8851fc4..8d01804cf 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ You call APIs of DDS to create, access, and subscribe to distributed databases. /foundation/distributeddatamgr/distributeddatamgr ├── interfaces # APIs │ └── innerkits # Native APIs +│ └── jskits # JavaScript APIs ├── services # Service code │ └── distributeddataservice # DDS implementation └── test # Test case resources diff --git a/README_zh.md b/README_zh.md index 48aabe1d9..da3846775 100644 --- a/README_zh.md +++ b/README_zh.md @@ -46,6 +46,7 @@ /foundation/distributeddatamgr/distributeddatamgr ├── interfaces # 接口层代码 │ └── innerkits # Native接口 +│ └── jskits # JavaScript接口 ├── services # 服务层代码 │ └── distributeddataservice # 分布式数据服务实现 └── test # 测试用例资源 diff --git a/frameworks/jskitsimpl/distributeddata/include/async_call.h b/frameworks/jskitsimpl/distributeddata/include/async_call.h new file mode 100644 index 000000000..e491ea8c7 --- /dev/null +++ b/frameworks/jskitsimpl/distributeddata/include/async_call.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021 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 OHOS_ASYNC_CALL_H +#define OHOS_ASYNC_CALL_H + +#include +#include +#include "js_util.h" +#include "napi/native_common.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" + +namespace OHOS::DistributedData { +class AsyncCall final { +public: + class Context { + public: + using InputAction = std::function; + using OutputAction = std::function; + using ExecAction = std::function; + Context(InputAction input, OutputAction output): input_(std::move(input)), output_(std::move(output)) {}; + virtual ~Context() {}; + void SetAction(InputAction input, OutputAction output = nullptr) + { + input_ = input; + output_ = output; + } + + void SetAction(OutputAction output) + { + SetAction(nullptr, std::move(output)); + } + + virtual napi_status operator()(napi_env env, size_t argc, napi_value *argv, napi_value self) + { + if (input_ == nullptr) { + return napi_ok; + } + return input_(env, argc, argv, self); + } + + virtual napi_status operator()(napi_env env, napi_value *result) + { + if (output_ == nullptr) { + *result = nullptr; + return napi_ok; + } + return output_(env, result); + } + + virtual void Exec() + { + if (exec_ == nullptr) { + return; + } + exec_(this); + }; + protected: + friend class AsyncCall; + InputAction input_ = nullptr; + OutputAction output_ = nullptr; + ExecAction exec_ = nullptr; + }; + + // The default AsyncCallback in the parameters is at the end position. + static constexpr size_t ASYNC_DEFAULT_POS = -1; + AsyncCall(napi_env env, napi_callback_info info, std::shared_ptr context, size_t pos = ASYNC_DEFAULT_POS); + ~AsyncCall(); + napi_value Call(napi_env env, Context::ExecAction exec = nullptr); + napi_value SyncCall(napi_env env, Context::ExecAction exec = nullptr); +private: + enum { + ARG_ERROR, + ARG_DATA, + ARG_BUTT + }; + static void OnExecute(napi_env env, void *data); + static void OnComplete(napi_env env, napi_status status, void *data); + struct AsyncContext { + std::shared_ptr ctx = nullptr; + napi_ref callback = nullptr; + napi_deferred defer = nullptr; + napi_async_work work = nullptr; + }; + static void DeleteContext(napi_env env, AsyncContext *context); + + AsyncContext *context_ = nullptr; + napi_env env_ = nullptr; +}; +} + +#endif // OHOS_ASYNC_CALL_H diff --git a/frameworks/jskitsimpl/distributeddata/include/js_util.h b/frameworks/jskitsimpl/distributeddata/include/js_util.h new file mode 100644 index 000000000..5e457ffbc --- /dev/null +++ b/frameworks/jskitsimpl/distributeddata/include/js_util.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 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 OHOS_JS_UTIL_H +#define OHOS_JS_UTIL_H + +#include +#include +#include +#include "napi/native_common.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "types.h" + +#define DECLARE_NAPI_METHOD(name, func) { name, 0, func, 0, 0, 0, napi_default, 0 } +namespace OHOS::DistributedData { +class JSUtil { +public: + static constexpr int32_t MAX_ARGC = 6; + static constexpr int32_t MAX_NUMBER_BYTES = 8; + static constexpr int32_t MAX_LEN = 4096; + + static std::string Convert2String(napi_env env, napi_value jsString); + static napi_value Convert2JSString(napi_env env, const std::string &cString); + static napi_value Convert2JSString(napi_env env, const std::vector &key); + static napi_value Convert2JSValue(napi_env env, const std::vector &data); + static napi_value Convert2JSNumber(napi_env env, const std::vector &data); + static napi_value Convert2JSNotification(napi_env env, const DistributedKv::ChangeNotification ¬ification); + static napi_value Convert2JSTupleArray(napi_env env, std::map &data); + static DistributedKv::Options Convert2Options(napi_env env, napi_value jsOptions); + static std::vector Convert2Vector(napi_env env, napi_value jsValue); + static std::vector ConvertUint8Array2Vector(napi_env env, napi_value jsValue); + static std::vector ConvertString2Vector(napi_env env, napi_value jsValue); + static std::vector ConvertNumber2Vector(napi_env env, napi_value jsValue); + static std::vector ConvertBool2Vector(napi_env env, napi_value jsValue); + static std::vector Convert2StringArray(napi_env env, napi_value jsValue); +private: + static napi_value GetJSEntries(napi_env env, const std::list &entries); + enum ValueType : uint8_t { + /** Indicates that the value type is string. */ + STRING = 0, + /** Indicates that the value type is int. */ + INTEGER = 1, + /** Indicates that the value type is float. */ + FLOAT = 2, + /** Indicates that the value type is byte array. */ + BYTE_ARRAY = 3, + /** Indicates that the value type is boolean. */ + BOOLEAN = 4, + /** Indicates that the value type is double. */ + DOUBLE = 5, + + INVALID = 255, + }; + static constexpr int32_t TYPE_POS = 0; + static constexpr int32_t DATA_POS = TYPE_POS + 1; + static constexpr int32_t TUPLE_SIZE = 2; +}; +} +#endif // OHOS_JS_UTIL_H diff --git a/frameworks/jskitsimpl/distributeddata/include/kv_manager.h b/frameworks/jskitsimpl/distributeddata/include/kv_manager.h new file mode 100644 index 000000000..be8751a1b --- /dev/null +++ b/frameworks/jskitsimpl/distributeddata/include/kv_manager.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 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 OHOS_KV_MANAGER_H +#define OHOS_KV_MANAGER_H + +#include +#include "napi/native_common.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "distributed_kv_data_manager.h" +namespace OHOS::DistributedData { +class KVManager { +public: + static napi_value CreateKVManager(napi_env env, napi_callback_info info); + static napi_value GetKVStore(napi_env env, napi_callback_info info); +private: + static napi_value GetCtor(napi_env env); + static napi_value Initialize(napi_env env, napi_callback_info info); + + DistributedKv::DistributedKvDataManager kvDataManager_ {}; + std::string bundleName_ {}; + static napi_ref ctor_; +}; +} +#endif // OHOS_KV_MANAGER_H diff --git a/frameworks/jskitsimpl/distributeddata/include/single_kv_store.h b/frameworks/jskitsimpl/distributeddata/include/single_kv_store.h new file mode 100644 index 000000000..47c0b990d --- /dev/null +++ b/frameworks/jskitsimpl/distributeddata/include/single_kv_store.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021 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 OHOS_SINGLE_KV_STORE_H +#define OHOS_SINGLE_KV_STORE_H +#include +#include "napi/native_common.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "kv_manager.h" +#include "async_call.h" +#include "single_kvstore.h" + +namespace OHOS::DistributedData { +class SingleKVStore final { +public: + SingleKVStore() = default; + ~SingleKVStore(); + SingleKVStore &operator=(std::unique_ptr &&singleKvStore); + bool operator==(const std::unique_ptr &singleKvStore); + static napi_value GetCtor(napi_env env); + static napi_value OnEvent(napi_env env, napi_callback_info info); + static napi_value Sync(napi_env env, napi_callback_info info); + static napi_value Put(napi_env env, napi_callback_info info); + static napi_value Get(napi_env env, napi_callback_info info); + static napi_value Delete(napi_env env, napi_callback_info info); +private: + enum JSSubscribeType { + SUBSCRIBE_LOCAL = 0, + SUBSCRIBE_REMOTE = 1, + SUBSCRIBE_ALL = 2, + }; + + struct ContextInfo : public AsyncCall::Context { + SingleKVStore *proxy = nullptr; + std::string key; + std::vector value; + napi_status status = napi_generic_failure; + ContextInfo() : Context(nullptr, nullptr) { }; + ContextInfo(InputAction input, OutputAction output) : Context(std::move(input), std::move(output)) { }; + virtual ~ContextInfo() {}; + + napi_status operator()(napi_env env, size_t argc, napi_value *argv, napi_value self) override + { + NAPI_ASSERT_BASE(env, self != nullptr, "self is nullptr", napi_invalid_arg); + NAPI_CALL_BASE(env, napi_unwrap(env, self, reinterpret_cast(&proxy)), napi_invalid_arg); + NAPI_ASSERT_BASE(env, proxy != nullptr, "there is no native kv store", napi_invalid_arg); + return Context::operator()(env, argc, argv, self); + } + napi_status operator()(napi_env env, napi_value *result) override + { + if (status != napi_ok) { + return status; + } + return Context::operator()(env, result); + } + }; + + using Exec = std::function; + static napi_value Initialize(napi_env env, napi_callback_info info); + + static napi_status OnDataChange(napi_env env, size_t argc, napi_value *argv, napi_value self, napi_value *result); + static napi_status OnSyncComplete(napi_env env, size_t argc, napi_value *argv, napi_value self, napi_value *result); + static std::map eventHandlers_; + static napi_ref ctor_; + + std::unique_ptr kvStore_ = nullptr; + std::shared_ptr syncObserver_ = nullptr; + std::shared_ptr dataObserver_[SUBSCRIBE_ALL + 1]; +}; + +class DataObserver : public DistributedKv::KvStoreObserver { +public: + DataObserver(napi_env env, napi_value callback); + virtual ~DataObserver(); + void OnChange(const DistributedKv::ChangeNotification ¬ification, + std::unique_ptr snapshot) override; + void OnChange(const DistributedKv::ChangeNotification ¬ification) override; +private: + napi_ref callback_ = nullptr; + napi_env env_; +}; + +class SyncObserver : public DistributedKv::KvStoreSyncCallback { +public: + SyncObserver(napi_env env, napi_value callback); + virtual ~SyncObserver(); + void SyncCompleted(const std::map &results) override; +private: + napi_ref callback_ = nullptr; + napi_env env_; +}; +} +#endif // OHOS_SINGLE_KV_STORE_H diff --git a/frameworks/jskitsimpl/distributeddata/src/async_call.cpp b/frameworks/jskitsimpl/distributeddata/src/async_call.cpp new file mode 100644 index 000000000..4682e1dea --- /dev/null +++ b/frameworks/jskitsimpl/distributeddata/src/async_call.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2021 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 "AsyncCall" +#include "async_call.h" +#include +#include "log_print.h" + +using namespace OHOS::DistributedKv; +namespace OHOS::DistributedData { +AsyncCall::AsyncCall(napi_env env, napi_callback_info info, std::shared_ptr context, size_t pos) + : env_(env) +{ + context_ = new AsyncContext(); + size_t argc = JSUtil::MAX_ARGC; + napi_value self = nullptr; + napi_value argv[JSUtil::MAX_ARGC]; + NAPI_CALL_RETURN_VOID(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr)); + NAPI_ASSERT_BASE(env, pos <= argc, " Invalid Args!", NAPI_RETVAL_NOTHING); + pos = (pos == ASYNC_DEFAULT_POS) ? argc - 1 : pos; + if (0 <= pos && pos < argc) { + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[pos], &valueType); + if (valueType == napi_function) { + napi_create_reference(env, argv[pos], 1, &context_->callback); + argc = pos; + } + } + context_->ctx = std::move(context); + NAPI_CALL_RETURN_VOID(env, (*context_->ctx)(env, argc, argv, self)); +} + +AsyncCall::~AsyncCall() +{ + if (context_ == nullptr) { + return; + } + + DeleteContext(env_, context_); +} + +napi_value AsyncCall::Call(napi_env env, Context::ExecAction exec) +{ + if (context_ == nullptr) { + return nullptr; + } + ZLOGD("async call exec"); + context_->ctx->exec_ = std::move(exec); + napi_value promise = nullptr; + if (context_->callback == nullptr) { + napi_create_promise(env, &context_->defer, &promise); + } else { + napi_get_undefined(env, &promise); + } + napi_async_work work = context_->work; + napi_value resource = nullptr; + napi_create_string_utf8(env, "AsyncCall", NAPI_AUTO_LENGTH, &resource); + napi_create_async_work(env, nullptr, resource, AsyncCall::OnExecute, AsyncCall::OnComplete, context_, &work); + context_->work = work; + context_ = nullptr; + napi_queue_async_work(env, work); + ZLOGD("async call exec"); + return promise; +} + +napi_value AsyncCall::SyncCall(napi_env env, AsyncCall::Context::ExecAction exec) +{ + if (context_ == nullptr) { + return nullptr; + } + + context_->ctx->exec_ = std::move(exec); + napi_value promise = nullptr; + if (context_->callback == nullptr) { + napi_create_promise(env, &context_->defer, &promise); + } else { + napi_get_undefined(env, &promise); + } + AsyncCall::OnExecute(env, context_); + AsyncCall::OnComplete(env, napi_ok, context_); + return promise; +} + +void AsyncCall::OnExecute(napi_env env, void *data) +{ + ZLOGD("run the async runnable"); + AsyncContext *context = reinterpret_cast(data); + context->ctx->Exec(); +} + +void AsyncCall::OnComplete(napi_env env, napi_status status, void *data) +{ + ZLOGD("run the js callback function"); + AsyncContext *context = reinterpret_cast(data); + napi_value output = nullptr; + napi_status runStatus = (*context->ctx)(env, &output); + napi_value result[ARG_BUTT] = { 0 }; + if (status == napi_ok && runStatus == napi_ok) { + napi_get_undefined(env, &result[ARG_ERROR]); + if (output != nullptr) { + result[ARG_DATA] = output; + } else { + napi_get_undefined(env, &result[ARG_DATA]); + } + } else { + napi_value message = nullptr; + napi_create_string_utf8(env, "async call failed", NAPI_AUTO_LENGTH, &message); + napi_create_error(env, nullptr, message, &result[ARG_ERROR]); + napi_get_undefined(env, &result[ARG_DATA]); + } + if (context->defer != nullptr) { + // promise + if (status == napi_ok && runStatus == napi_ok) { + napi_resolve_deferred(env, context->defer, result[ARG_DATA]); + } else { + napi_reject_deferred(env, context->defer, result[ARG_ERROR]); + } + } else { + // callback + napi_value callback = nullptr; + napi_get_reference_value(env, context->callback, &callback); + napi_value returnValue; + napi_call_function(env, nullptr, callback, ARG_BUTT, result, &returnValue); + } + DeleteContext(env, context); +} +void AsyncCall::DeleteContext(napi_env env, AsyncContext *context) +{ + if (env != nullptr) { + napi_delete_reference(env, context->callback); + napi_delete_async_work(env, context->work); + } + delete context; +} +} \ No newline at end of file diff --git a/frameworks/jskitsimpl/distributeddata/src/entry_point.cpp b/frameworks/jskitsimpl/distributeddata/src/entry_point.cpp new file mode 100644 index 000000000..6bb8085e6 --- /dev/null +++ b/frameworks/jskitsimpl/distributeddata/src/entry_point.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 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 "napi/native_api.h" +#include "napi/native_node_api.h" +#include "kv_manager.h" +#include "js_util.h" +#include "log_print.h" +using namespace OHOS::DistributedData; +using namespace OHOS::DistributedKv; +static napi_value Init(napi_env env, napi_value exports) +{ + napi_property_descriptor desc = DECLARE_NAPI_METHOD("createKVManager", KVManager::CreateKVManager); + napi_status status = napi_define_properties(env, exports, 1, &desc); + ZLOGI("init distributedDataKit %{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.distributedDataKit", + .nm_priv = ((void *)0), + .reserved = { 0 } + }; + napi_module_register(&module); + ZLOGI("module register data.distributedDataKit"); +} + diff --git a/frameworks/jskitsimpl/distributeddata/src/js_util.cpp b/frameworks/jskitsimpl/distributeddata/src/js_util.cpp new file mode 100644 index 000000000..179cc645a --- /dev/null +++ b/frameworks/jskitsimpl/distributeddata/src/js_util.cpp @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2021 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 "JSUtil" + +#include "js_util.h" +#include +#include "log_print.h" + +using namespace OHOS::DistributedKv; +namespace OHOS::DistributedData { +DistributedKv::Options JSUtil::Convert2Options(napi_env env, napi_value jsOptions) +{ + DistributedKv::Options options; + napi_value value = nullptr; + napi_get_named_property(env, jsOptions, "createIfMissing", &value); + if (value != nullptr) { + napi_get_value_bool(env, value, &options.createIfMissing); + } + value = nullptr; + napi_get_named_property(env, jsOptions, "encrypt", &value); + if (value != nullptr) { + napi_get_value_bool(env, value, &options.encrypt); + } + value = nullptr; + napi_get_named_property(env, jsOptions, "backup", &value); + if (value != nullptr) { + napi_get_value_bool(env, value, &options.backup); + } + value = nullptr; + napi_get_named_property(env, jsOptions, "autoSync", &value); + if (value != nullptr) { + napi_get_value_bool(env, value, &options.autoSync); + } + value = nullptr; + napi_get_named_property(env, jsOptions, "kvStoreType", &value); + if (value != nullptr) { + int32_t storeType = DistributedKv::DEVICE_COLLABORATION; + napi_get_value_int32(env, value, &storeType); + options.kvStoreType = static_cast(storeType); + } + value = nullptr; + napi_get_named_property(env, jsOptions, "securityLevel", &value); + if (value != nullptr) { + napi_get_value_int32(env, value, &options.securityLevel); + } + return options; +} + +std::string JSUtil::Convert2String(napi_env env, napi_value jsString) +{ + char *buf = new char[JSUtil::MAX_LEN + 1]; + size_t len = 0; + auto status = napi_get_value_string_utf8(env, jsString, buf, JSUtil::MAX_LEN, &len); + if (status != napi_ok) { + GET_AND_THROW_LAST_ERROR((env)); + } + buf[len] = 0; + std::string value(buf); + delete[] buf; + return value; +} + +napi_value JSUtil::Convert2JSNotification(napi_env env, const DistributedKv::ChangeNotification ¬ification) +{ + napi_value result = nullptr; + napi_create_object(env, &result); + napi_set_named_property(env, result, "deviceId", Convert2JSString(env, notification.GetDeviceId())); + napi_set_named_property(env, result, "insertEntries", GetJSEntries(env, notification.GetInsertEntries())); + napi_set_named_property(env, result, "updateEntries", GetJSEntries(env, notification.GetUpdateEntries())); + napi_set_named_property(env, result, "deleteEntries", GetJSEntries(env, notification.GetDeleteEntries())); + return result; +} + +napi_value JSUtil::Convert2JSTupleArray(napi_env env, std::map &data) +{ + napi_value result = nullptr; + napi_create_array_with_length(env, data.size(), &result); + int index = 0; + for (const auto &[key, value] : data) { + napi_value element = nullptr; + napi_create_array_with_length(env, TUPLE_SIZE, &element); + napi_value jsKey = nullptr; + napi_create_string_utf8(env, key.c_str(), key.size(), &jsKey); + napi_set_element(env, element, 0, jsKey); + napi_value jsValue = nullptr; + napi_create_int32(env, value, &jsValue); + napi_set_element(env, element, 1, jsValue); + napi_set_element(env, result, index++, element); + } + return result; +} + +std::vector JSUtil::Convert2StringArray(napi_env env, napi_value jsValue) +{ + bool isArray = false; + napi_is_array(env, jsValue, &isArray); + NAPI_ASSERT_BASE(env, isArray, "not array", { }); + uint32_t length = 0; + napi_get_array_length(env, jsValue, &length); + std::vector devices; + for (uint32_t i = 0; i < length; ++i) { + napi_value deviceId = nullptr; + napi_get_element(env, jsValue, i, &deviceId); + if (deviceId == nullptr) { + continue; + } + devices.push_back(Convert2String(env, deviceId)); + } + return devices; +} + +napi_value JSUtil::Convert2JSString(napi_env env, const std::vector &key) +{ + std::string realkey(key.begin(), key.end()); + napi_value jsKey = nullptr; + napi_create_string_utf8(env, realkey.c_str(), realkey.size(), &jsKey); + return jsKey; +} + +napi_value JSUtil::Convert2JSValue(napi_env env, const std::vector &data) +{ + napi_value result = nullptr; + napi_get_undefined(env, &result); + NAPI_ASSERT_BASE(env, data.size() > 1, "invalid data", result); + napi_create_object(env, &result); + napi_value jsType = nullptr; + napi_create_int32(env, data[0], &jsType); + napi_set_named_property(env, result, "type", jsType); + napi_value jsValue = nullptr; + switch (data[0]) { + case STRING: + napi_create_string_utf8(env, reinterpret_cast(data.data() + DATA_POS), data.size() - DATA_POS, + &jsValue); + break; + case BOOLEAN: + napi_get_boolean(env, *(reinterpret_cast(data.data() + DATA_POS)), &jsValue); + break; + case BYTE_ARRAY: { + uint8_t *native = nullptr; + napi_value buffer = nullptr; + napi_create_arraybuffer(env, data.size() - DATA_POS, reinterpret_cast(&native), &buffer); + memcpy_s(native, data.size() - DATA_POS, data.data() + DATA_POS, data.size() - DATA_POS); + napi_create_typedarray(env, napi_uint8_array, data.size() - DATA_POS, buffer, 0, &jsValue); + break; + } + default: + jsValue = Convert2JSNumber(env, data); + break; + } + napi_set_named_property(env, result, "value", jsValue); + return result; +} + +std::vector JSUtil::Convert2Vector(napi_env env, napi_value jsValue) +{ + napi_valuetype valueType; + napi_typeof(env, jsValue, &valueType); + switch (valueType) { + case napi_boolean: + return ConvertBool2Vector(env, jsValue); + case napi_number: + return ConvertNumber2Vector(env, jsValue); + case napi_string: + return ConvertString2Vector(env, jsValue); + case napi_object: + return ConvertUint8Array2Vector(env, jsValue); + default: + break; + } + return {INVALID}; +} + +std::vector JSUtil::ConvertUint8Array2Vector(napi_env env, napi_value jsValue) +{ + bool isTypedArray = false; + if (napi_is_typedarray(env, jsValue, &isTypedArray) != napi_ok || !isTypedArray) { + return {INVALID}; + } + + napi_typedarray_type type; + size_t length; + napi_value buffer; + size_t offset; + NAPI_CALL_BASE(env, napi_get_typedarray_info(env, jsValue, &type, &length, nullptr, &buffer, &offset), { INVALID }); + if (type != napi_uint8_array) { + return {INVALID}; + } + uint8_t *data; + size_t total; + NAPI_CALL_BASE(env, napi_get_arraybuffer_info(env, buffer, reinterpret_cast(&data), &total), { INVALID }); + length = std::min(length, total - offset); + std::vector result(sizeof(uint8_t) + length); + result[TYPE_POS] = BYTE_ARRAY; + memcpy_s(result.data() + DATA_POS, result.size() - DATA_POS, &data[offset], length); + return result; +} + +std::vector JSUtil::ConvertString2Vector(napi_env env, napi_value jsValue) +{ + std::string value = Convert2String(env, jsValue); + std::vector result(sizeof(uint8_t) + value.size()); + result[TYPE_POS] = STRING; + if (memcpy_s(result.data() + DATA_POS, result.size() - DATA_POS, value.data(), value.size()) != EOK) { + result[TYPE_POS] = INVALID; + } + return result; +} + +std::vector JSUtil::ConvertNumber2Vector(napi_env env, napi_value jsValue) +{ + // the JavaScript number is 64 bits double. + double value = 0; + auto status = napi_get_value_double(env, jsValue, &value); + if (status == napi_ok) { + std::vector result(sizeof(uint8_t) + sizeof(double)); + result[TYPE_POS] = DOUBLE; + uint8_t byteValue[MAX_NUMBER_BYTES]; + memcpy_s(byteValue, MAX_NUMBER_BYTES, &value, sizeof(double)); + memcpy_s(result.data() + DATA_POS, sizeof(double), byteValue, sizeof(byteValue)); + return result; + } + + return {INVALID}; +} + +std::vector JSUtil::ConvertBool2Vector(napi_env env, napi_value jsValue) +{ + std::vector result; + result.resize(sizeof(uint8_t) + sizeof(int32_t)); + result[TYPE_POS] = BOOLEAN; + bool value = false; + napi_get_value_bool(env, jsValue, &value); + *reinterpret_cast(result.data() + DATA_POS) = value ? 1 : 0; + return result; +} + +napi_value JSUtil::GetJSEntries(napi_env env, const std::list &entries) +{ + napi_value jsValue = nullptr; + napi_create_array_with_length(env, entries.size(), &jsValue); + int index = 0; + for (const auto &data : entries) { + napi_value entry = nullptr; + napi_create_object(env, &entry); + napi_set_named_property(env, entry, "key", Convert2JSString(env, data.key.Data())); + napi_set_named_property(env, entry, "value", Convert2JSValue(env, data.value.Data())); + napi_set_element(env, jsValue, index++, entry); + } + return jsValue; +} + +napi_value JSUtil::Convert2JSString(napi_env env, const std::string &cString) +{ + napi_value jsValue = nullptr; + napi_create_string_utf8(env, cString.c_str(), cString.size(), &jsValue); + return jsValue; +} + +napi_value JSUtil::Convert2JSNumber(napi_env env, const std::vector &data) +{ + napi_value jsValue = nullptr; + uint8_t byteValue[MAX_NUMBER_BYTES]; + double value; + switch (data[0]) { + case INTEGER: + memcpy_s(byteValue, sizeof(byteValue), data.data() + DATA_POS, sizeof(int32_t)); + value = *(reinterpret_cast(byteValue)); + break; + case FLOAT: + memcpy_s(byteValue, sizeof(byteValue), data.data() + DATA_POS, sizeof(float)); + value = *(reinterpret_cast(byteValue)); + break; + case DOUBLE: + memcpy_s(byteValue, sizeof(byteValue), data.data() + DATA_POS, sizeof(double)); + value = *(reinterpret_cast(byteValue)); + break; + default: + napi_get_undefined(env, &jsValue); + return jsValue; + } + napi_create_double(env, value, &jsValue); + return jsValue; +} +} \ No newline at end of file diff --git a/frameworks/jskitsimpl/distributeddata/src/kv_manager.cpp b/frameworks/jskitsimpl/distributeddata/src/kv_manager.cpp new file mode 100644 index 000000000..4609114c4 --- /dev/null +++ b/frameworks/jskitsimpl/distributeddata/src/kv_manager.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021 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 "KVManager" + +#include "kv_manager.h" +#include +#include "js_util.h" +#include "distributed_kv_data_manager.h" +#include "async_call.h" +#include "single_kv_store.h" +#include "log_print.h" + +using namespace OHOS::DistributedKv; +namespace OHOS::DistributedData { +napi_ref KVManager::ctor_ = nullptr; +napi_value KVManager::CreateKVManager(napi_env env, napi_callback_info info) +{ + ZLOGD("get kv manager!"); + // function createKVManager(config: KVManagerConfig, callback: AsyncCallback): void; + // AsyncCallback at position 1 + struct ContextInfo { + napi_ref ref = nullptr; + napi_env env = nullptr; + ~ContextInfo() + { + if (env != nullptr) { + napi_delete_reference(env, ref); + } + } + }; + auto ctxInfo = std::make_shared(); + auto input = [ctxInfo](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + ZLOGD("CreateKVManager parser to native params %{public}d!", argc); + NAPI_ASSERT_BASE(env, 0 < argc && argc <= 2, " should 1 or 2 parameters!", napi_invalid_arg); + napi_value kvManger = nullptr; + napi_status status = napi_new_instance(env, GetCtor(env), argc, argv, &kvManger); + napi_create_reference(env, kvManger, 1, &(ctxInfo->ref)); + return status; + }; + auto output = [ctxInfo](napi_env env, napi_value *result) -> napi_status { + return napi_get_reference_value(env, ctxInfo->ref, result); + }; + auto context = std::make_shared(input, output); + AsyncCall asyncCall(env, info, context, 1); + return asyncCall.Call(env); +} + +napi_value KVManager::GetKVStore(napi_env env, napi_callback_info info) +{ + ZLOGD("get kv store!"); + // getKVStore(storeId: string, options: Options, callback: AsyncCallback): void; + // AsyncCallback at position 2 + struct ContextInfo { + std::string storeId; + Options options; + KVManager *proxy = nullptr; + SingleKVStore *kvStore = nullptr; + napi_ref ref = nullptr; + napi_env env = nullptr; + ~ContextInfo() + { + if (env != nullptr) { + napi_delete_reference(env, ref); + } + } + }; + auto ctxInfo = std::make_shared(); + auto input = [ctxInfo](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + ZLOGD("GetKVStore parser to native params %{public}d!", argc); + NAPI_ASSERT_BASE(env, self != nullptr, "self is nullptr", napi_invalid_arg); + NAPI_CALL_BASE(env, napi_unwrap(env, self, reinterpret_cast(&ctxInfo->proxy)), napi_invalid_arg); + NAPI_ASSERT_BASE(env, ctxInfo->proxy != nullptr, "there is no native kv store", napi_invalid_arg); + ctxInfo->storeId = JSUtil::Convert2String(env, argv[0]); + ctxInfo->options = JSUtil::Convert2Options(env, argv[1]); + napi_value kvStore = nullptr; + napi_status status = napi_new_instance(env, SingleKVStore::GetCtor(env), argc, argv, &kvStore); + napi_unwrap(env, kvStore, reinterpret_cast(&ctxInfo->kvStore)); + if (ctxInfo->kvStore == nullptr) { + return napi_invalid_arg; + } + napi_create_reference(env, kvStore, 1, &(ctxInfo->ref)); + return status; + }; + auto output = [ctxInfo](napi_env env, napi_value *result) -> napi_status { + if (*(ctxInfo->kvStore) == nullptr) { + ZLOGE("get kv store failed!"); + *result = nullptr; + return napi_object_expected; + } + ZLOGD("get kv store success!"); + return napi_get_reference_value(env, ctxInfo->ref, result); + }; + auto exec = [ctxInfo](AsyncCall::Context *ctx) { + ctxInfo->proxy->kvDataManager_.GetSingleKvStore( + ctxInfo->options, {ctxInfo->proxy->bundleName_}, {ctxInfo->storeId}, + [ctxInfo](Status, std::unique_ptr kvStore) { + *(ctxInfo->kvStore) = std::move(kvStore); + }); + }; + auto context = std::make_shared(input, output); + AsyncCall asyncCall(env, info, context, 2); + return asyncCall.Call(env, exec); +} + +napi_value KVManager::GetCtor(napi_env env) +{ + napi_value cons; + if (ctor_ != nullptr) { + NAPI_CALL(env, napi_get_reference_value(env, ctor_, &cons)); + return cons; + } + + napi_property_descriptor clzDes[] = { + DECLARE_NAPI_METHOD("getKVStore", GetKVStore) + }; + NAPI_CALL(env, napi_define_class(env, "KVManager", NAPI_AUTO_LENGTH, Initialize, nullptr, + sizeof(clzDes) / sizeof(napi_property_descriptor), clzDes, &cons)); + NAPI_CALL(env, napi_create_reference(env, cons, 1, &ctor_)); + return cons; +} + +napi_value KVManager::Initialize(napi_env env, napi_callback_info info) +{ + ZLOGD("constructor kv manager!"); + napi_value self = nullptr; + size_t argc = JSUtil::MAX_ARGC; + napi_value argv[JSUtil::MAX_ARGC]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr)); + napi_value bundle = nullptr; + NAPI_CALL(env, napi_get_named_property(env, argv[0], "bundleName", &bundle)); + auto *proxy = new KVManager(); + proxy->bundleName_ = JSUtil::Convert2String(env, bundle); + auto finalize = [](napi_env env, void* data, void* hint) { + KVManager *proxy = reinterpret_cast(data); + delete proxy; + }; + if (napi_wrap(env, self, proxy, finalize, nullptr, nullptr) != napi_ok) { + finalize(env, proxy, nullptr); + return nullptr; + } + return self; +} +} \ No newline at end of file diff --git a/frameworks/jskitsimpl/distributeddata/src/single_kv_store.cpp b/frameworks/jskitsimpl/distributeddata/src/single_kv_store.cpp new file mode 100644 index 000000000..374010db6 --- /dev/null +++ b/frameworks/jskitsimpl/distributeddata/src/single_kv_store.cpp @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2021 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 "SingleKVStore" + +#include "single_kv_store.h" +#include "js_util.h" +#include "single_kvstore.h" +#include "async_call.h" +#include "log_print.h" + +using namespace OHOS::DistributedKv; +namespace OHOS::DistributedData { +napi_ref SingleKVStore::ctor_ = nullptr; +std::map SingleKVStore::eventHandlers_ = { + {"dataChange", SingleKVStore::OnDataChange}, + {"syncComplete", SingleKVStore::OnSyncComplete}, +}; + +SingleKVStore::~SingleKVStore() +{ + if (kvStore_ == nullptr) { + return; + } + + for (int i = 0; i < SUBSCRIBE_ALL + 1; ++i) { + if (dataObserver_[i] == nullptr) { + continue; + } + kvStore_->UnSubscribeKvStore(static_cast(i + 1), dataObserver_[i]); + } + + if (syncObserver_ != nullptr) { + kvStore_->UnRegisterSyncCallback(); + } +} + +napi_value SingleKVStore::GetCtor(napi_env env) +{ + if (ctor_ != nullptr) { + napi_value cons = nullptr; + NAPI_CALL(env, napi_get_reference_value(env, ctor_, &cons)); + return cons; + } + + napi_property_descriptor clzDes[] = { + DECLARE_NAPI_METHOD("put", SingleKVStore::Put), + DECLARE_NAPI_METHOD("get", SingleKVStore::Get), + DECLARE_NAPI_METHOD("delete", SingleKVStore::Delete), + DECLARE_NAPI_METHOD("on", SingleKVStore::OnEvent), + DECLARE_NAPI_METHOD("sync", SingleKVStore::Sync), + }; + napi_value cons; + NAPI_CALL(env, napi_define_class(env, "SingleKVStore", NAPI_AUTO_LENGTH, Initialize, nullptr, + sizeof(clzDes) / sizeof(napi_property_descriptor), clzDes, &cons)); + NAPI_CALL(env, napi_create_reference(env, cons, 1, &ctor_)); + return cons; +} + +napi_value SingleKVStore::Put(napi_env env, napi_callback_info info) +{ + auto context = std::make_shared(); + auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + NAPI_ASSERT_BASE(env, argc >= 2, " should 2 or more parameters!", napi_invalid_arg); + context->key = JSUtil::Convert2String(env, argv[0]); + context->value = JSUtil::Convert2Vector(env, argv[1]); + return napi_ok; + }; + auto exec = [context](AsyncCall::Context *ctx) { + OHOS::DistributedKv::Key key(context->key); + OHOS::DistributedKv::Value value(context->value); + Status status = context->proxy->kvStore_->Put(key, value); + context->status = (status != Status::SUCCESS) ? napi_generic_failure : napi_ok; + }; + context->SetAction(std::move(input)); + AsyncCall asyncCall(env, info, std::dynamic_pointer_cast(context), 2); + return asyncCall.Call(env, exec); +} + +napi_value SingleKVStore::Get(napi_env env, napi_callback_info info) +{ + auto context = std::make_shared(); + auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + NAPI_ASSERT_BASE(env, argc >= 1, " should 1 or more parameters!", napi_invalid_arg); + context->key = JSUtil::Convert2String(env, argv[0]); + return napi_ok; + }; + auto output = [context](napi_env env, napi_value *result) -> napi_status { + napi_value data = JSUtil::Convert2JSValue(env, context->value); + return napi_get_named_property(env, data, "value", result); + }; + auto exec = [context](AsyncCall::Context *ctx) { + OHOS::DistributedKv::Key key(context->key); + OHOS::DistributedKv::Value value; + Status status = context->proxy->kvStore_->Get(key, value); + if (status == Status::SUCCESS) { + context->status = napi_ok; + context->value = value.Data(); + } + }; + context->SetAction(std::move(input), std::move(output)); + AsyncCall asyncCall(env, info, std::dynamic_pointer_cast(context), 1); + return asyncCall.Call(env, exec); +} + +napi_value SingleKVStore::Delete(napi_env env, napi_callback_info info) +{ + auto context = std::make_shared(); + auto input = [context](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { + NAPI_ASSERT_BASE(env, argc >= 1, " should 1 or more parameters!", napi_invalid_arg); + context->key = JSUtil::Convert2String(env, argv[0]); + return napi_ok; + }; + auto exec = [context](AsyncCall::Context *ctx) { + OHOS::DistributedKv::Key key(context->key); + Status status = context->proxy->kvStore_->Delete(key); + if (status == Status::SUCCESS) { + context->status = napi_ok; + } + }; + context->SetAction(std::move(input)); + AsyncCall asyncCall(env, info, std::dynamic_pointer_cast(context), 1); + return asyncCall.Call(env, exec); +} + +napi_value SingleKVStore::OnEvent(napi_env env, napi_callback_info info) +{ + napi_value self = nullptr; + size_t argc = JSUtil::MAX_ARGC; + napi_value argv[JSUtil::MAX_ARGC] = {0}; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr)); + NAPI_ASSERT(env, argc > 0, "there is no args"); + napi_valuetype type; + NAPI_CALL(env, napi_typeof(env, argv[0], &type)); + NAPI_ASSERT(env, type == napi_string, "key not string type"); + std::string event = JSUtil::Convert2String(env, argv[0]); + + ZLOGI("subscribe event:%{public}s", event.c_str()); + auto handle = eventHandlers_.find(event); + NAPI_ASSERT(env, handle != eventHandlers_.end(), "invalid event"); + napi_value result = nullptr; + handle->second(env, argc - 1, &argv[1], self, &result); + return nullptr; +} + +napi_value SingleKVStore::Sync(napi_env env, napi_callback_info info) +{ + napi_value self = nullptr; + size_t argc = JSUtil::MAX_ARGC; + napi_value argv[JSUtil::MAX_ARGC]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr)); + NAPI_ASSERT_BASE(env, argc >= 2, "args is out of range", nullptr); + NAPI_ASSERT_BASE(env, self != nullptr, "self is nullptr", nullptr); + SingleKVStore *proxy = nullptr; + NAPI_CALL_BASE(env, napi_unwrap(env, self, reinterpret_cast(&proxy)), nullptr); + NAPI_ASSERT_BASE(env, proxy != nullptr, "there is no native kv store", nullptr); + + std::vector devices = JSUtil::Convert2StringArray(env, argv[0]); + int32_t mode = int32_t(SyncMode::PUSH_PULL); + napi_get_value_int32(env, argv[1], &mode); + uint32_t delay = 0; + if (argc >= 3) { + napi_get_value_uint32(env, argv[2], &delay); + } + ZLOGD("sync data %{public}d, mode:%{public}d, devices:%{public}d", argc, mode, devices.size()); + + Status status = proxy->kvStore_->Sync(devices, static_cast(mode), delay); + NAPI_ASSERT_BASE(env, status == Status::SUCCESS, "call sync failed", nullptr); + return nullptr; +} + +napi_value SingleKVStore::Initialize(napi_env env, napi_callback_info info) +{ + ZLOGD("constructor single kv store!"); + napi_value self = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr)); + auto *proxy = new SingleKVStore(); + auto release = [proxy]() -> napi_value { + delete proxy; + return nullptr; + }; + auto finalize = [](napi_env env, void *data, void *hint) { + auto *proxy = reinterpret_cast(data); + delete proxy; + }; + NAPI_CALL_BASE(env, napi_wrap(env, self, proxy, finalize, nullptr, nullptr), release()); + return self; +} + +napi_status SingleKVStore::OnDataChange(napi_env env, size_t argc, napi_value *argv, napi_value self, + napi_value *result) +{ + // on(event: 'dataChange', type: SubscribeType, observer: Callback): void; + // except event, there are 2 args + NAPI_ASSERT_BASE(env, argc >= 2, "args is out of range", napi_invalid_arg); + NAPI_ASSERT_BASE(env, self != nullptr, "self is nullptr", napi_invalid_arg); + int32_t type = 0; + napi_get_value_int32(env, argv[0], &type); + NAPI_ASSERT_BASE(env, SUBSCRIBE_LOCAL <= type && type <= SUBSCRIBE_ALL, "type is out of range", napi_invalid_arg); + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[1], &valueType); + NAPI_ASSERT_BASE(env, valueType == napi_function, "callback is not a function", napi_invalid_arg); + + SingleKVStore *proxy = nullptr; + NAPI_CALL_BASE(env, napi_unwrap(env, self, reinterpret_cast(&proxy)), napi_invalid_arg); + NAPI_ASSERT_BASE(env, proxy != nullptr, "there is no native kv store", napi_invalid_arg); + + ZLOGI("subscribe data change type %{public}d", type); + std::shared_ptr observer = std::make_shared(env, argv[1]); + Status status = proxy->kvStore_->SubscribeKvStore(static_cast(type + 1), observer); + if (status != Status::SUCCESS) { + return napi_generic_failure; + } + if (proxy->dataObserver_[type] != nullptr) { + proxy->kvStore_->UnSubscribeKvStore(static_cast(type + 1), proxy->dataObserver_[type]); + } + proxy->dataObserver_[type] = std::move(observer); + return napi_ok; +} + +napi_status SingleKVStore::OnSyncComplete(napi_env env, size_t argc, napi_value *argv, napi_value self, + napi_value *result) +{ + // on(event: 'syncComplete', syncCallback: Callback>): void; + // except event, there is 1 arg + NAPI_ASSERT_BASE(env, argc >= 1, "args is out of range", napi_invalid_arg); + NAPI_ASSERT_BASE(env, self != nullptr, "self is nullptr", napi_invalid_arg); + napi_valuetype valueType = napi_undefined; + napi_typeof(env, argv[0], &valueType); + NAPI_ASSERT_BASE(env, valueType == napi_function, "callback is not function", napi_invalid_arg); + + SingleKVStore *proxy = nullptr; + NAPI_CALL_BASE(env, napi_unwrap(env, self, reinterpret_cast(&proxy)), napi_invalid_arg); + NAPI_ASSERT_BASE(env, proxy != nullptr, "there is no native kv store", napi_invalid_arg); + std::shared_ptr observer = std::make_shared(env, argv[0]); + Status status = proxy->kvStore_->RegisterSyncCallback(observer); + if (status != Status::SUCCESS) { + return napi_generic_failure; + } + proxy->syncObserver_ = observer; + return napi_ok; +} + +SingleKVStore &SingleKVStore::operator=(std::unique_ptr &&singleKvStore) +{ + if (kvStore_ == singleKvStore) { + return *this; + } + kvStore_ = std::move(singleKvStore); + return *this; +} + +bool SingleKVStore::operator==(const std::unique_ptr &singleKvStore) +{ + return kvStore_ == singleKvStore; +} + +DataObserver::DataObserver(napi_env env, napi_value callback) + : env_(env) +{ + napi_create_reference(env, callback, 1, &callback_); +} + +DataObserver::~DataObserver() +{ + napi_delete_reference(env_, callback_); +} + +void DataObserver::OnChange(const ChangeNotification ¬ification, std::unique_ptr snapshot) +{ + ZLOGD("data change insert:%{public}d, update:%{public}d, delete:%{public}d", + notification.GetInsertEntries().size(), notification.GetUpdateEntries().size(), + notification.GetDeleteEntries().size()); +} + +void DataObserver::OnChange(const ChangeNotification ¬ification) +{ + ZLOGD("data change insert:%{public}d, update:%{public}d, delete:%{public}d", + notification.GetInsertEntries().size(), notification.GetUpdateEntries().size(), + notification.GetDeleteEntries().size()); + KvStoreObserver::OnChange(notification); + napi_value jsNotification = JSUtil::Convert2JSNotification(env_, notification); + napi_value callback = nullptr; + napi_value args[1] = {jsNotification}; + napi_get_reference_value(env_, callback_, &callback); + napi_value global = nullptr; + napi_get_global(env_, &global); + napi_value result; + napi_status status = napi_call_function(env_, global, callback, 1, args, &result); + if (status != napi_ok) { + ZLOGE("notify data change failed status:%{public}d callback:%{public}p", status, callback); + } +} + +SyncObserver::SyncObserver(napi_env env, napi_value callback) + : env_(env) +{ + napi_create_reference(env, callback, 1, &callback_); +} + +SyncObserver::~SyncObserver() +{ + napi_delete_reference(env_, callback_); +} + +void SyncObserver::SyncCompleted(const std::map &results) +{ + std::map dataMap; + for (const auto &[key, value] : results) { + dataMap.emplace(key, int32_t(value)); + } + napi_value notification = JSUtil::Convert2JSTupleArray(env_, dataMap); + napi_value callback = nullptr; + napi_value args[1] = {notification}; + napi_get_reference_value(env_, callback_, &callback); + napi_value global = nullptr; + napi_value result = nullptr; + napi_get_global(env_, &global); + napi_call_function(env_, global, callback, 1, args, &result); +} +} diff --git a/interfaces/jskits/distributeddata/BUILD.gn b/interfaces/jskits/distributeddata/BUILD.gn new file mode 100644 index 000000000..355e9529f --- /dev/null +++ b/interfaces/jskits/distributeddata/BUILD.gn @@ -0,0 +1,72 @@ +# Copyright (c) 2021 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") + +group("build_module") { + deps = [ ":distributeddatakit" ] +} + +js_declaration("distributeddatamgr_js") { + part_name = "distributeddatamgr" + sources = [ + "./api/@ohos.data.distributeddatakit.d.ts", + "./api/@ohos.data", + "./api/data/distributeddatakit/kvmanager_config.d.ts", + "./api/data/distributeddatakit/kvstore.d.ts", + "./api/data/distributeddatakit/plain_ordinary_js_objects.d.ts", + "./api/data/distributeddatakit/single_kvstore.d.ts", + ] +} + +ohos_copy("distributeddatamgr_declaration") { + sources = [ + "./api" + ] + outputs = [ target_out_dir + "/$target_name/" ] + module_source_dir = target_out_dir + "/$target_name" + module_install_name = "" +} + +ohos_shared_library("distributeddatakit") { + include_dirs = [ + "//third_party/node/src", + "//utils/native/base/include", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/frameworks/jskitsimpl/distributeddata/include", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/log", + "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/adapter/include/autils", + ] + + sources = [ + "../../../frameworks/jskitsimpl/distributeddata/src/async_call.cpp", + "../../../frameworks/jskitsimpl/distributeddata/src/entry_point.cpp", + "../../../frameworks/jskitsimpl/distributeddata/src/js_util.cpp", + "../../../frameworks/jskitsimpl/distributeddata/src/kv_manager.cpp", + "../../../frameworks/jskitsimpl/distributeddata/src/single_kv_store.cpp", + ] + + deps = [ + "//foundation/ace/napi:ace_napi", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata:distributeddata", + "//utils/native/base:utils" + ] + + external_deps = [ + "hiviewdfx_hilog_native:libhilog" + ] + + subsystem_name = "distributeddatamgr" + relative_install_dir = "module/data" +} \ No newline at end of file diff --git a/ohos.build b/ohos.build index 4fe6e2d64..ef084ddce 100755 --- a/ohos.build +++ b/ohos.build @@ -10,6 +10,7 @@ "module_list": [ "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/app_distributeddata:app_distributeddata", "//foundation/distributeddatamgr/distributeddatamgr/interfaces/innerkits/distributeddata:build_module", + "//foundation/distributeddatamgr/distributeddatamgr/interfaces/jskits/distributeddata:build_module", "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/app:build_module", "//foundation/distributeddatamgr/distributeddatamgr/services/distributeddataservice/libs/distributeddb:build_module" ], @@ -59,6 +60,9 @@ "system_kits": [], "test_list": [ "//foundation/distributeddatamgr/distributeddatamgr:build_native_test" + ], + "system_capabilities":[ + "SystemCapability.Data.DATA_DISTRIBUTEDDATAMGR" ] } } -- Gitee