• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #define LOG_TAG "JsDeviceKVStore"
16 #include "js_device_kv_store.h"
17 #include <iomanip>
18 #include "js_kv_store_resultset.h"
19 #include "js_query.h"
20 #include "js_util.h"
21 #include "log_print.h"
22 #include "napi_queue.h"
23 #include "uv_queue.h"
24 #include "distributed_kv_data_manager.h"
25 
26 using namespace OHOS::DistributedKv;
27 using namespace OHOS::DataShare;
28 namespace OHOS::DistributedKVStore {
29 constexpr int DEVICEID_WIDTH = 4;
GetDeviceKey(const std::string & deviceId,const std::string & key)30 static std::string GetDeviceKey(const std::string& deviceId, const std::string& key)
31 {
32     std::ostringstream oss;
33     if (!deviceId.empty()) {
34         oss << std::setfill('0') << std::setw(DEVICEID_WIDTH) << deviceId.length() << deviceId;
35     }
36     oss << key;
37     return oss.str();
38 }
39 
JsDeviceKVStore(const std::string & storeId)40 JsDeviceKVStore::JsDeviceKVStore(const std::string& storeId)
41     : JsSingleKVStore(storeId)
42 {
43 }
44 
Constructor(napi_env env)45 napi_value JsDeviceKVStore::Constructor(napi_env env)
46 {
47     auto lambda = []() -> std::vector<napi_property_descriptor>{
48         std::vector<napi_property_descriptor> properties = {
49             DECLARE_NAPI_FUNCTION("put", JsSingleKVStore::Put),
50             DECLARE_NAPI_FUNCTION("delete", JsSingleKVStore::Delete),
51             DECLARE_NAPI_FUNCTION("putBatch", JsSingleKVStore::PutBatch),
52             DECLARE_NAPI_FUNCTION("deleteBatch", JsSingleKVStore::DeleteBatch),
53             DECLARE_NAPI_FUNCTION("startTransaction", JsSingleKVStore::StartTransaction),
54             DECLARE_NAPI_FUNCTION("commit", JsSingleKVStore::Commit),
55             DECLARE_NAPI_FUNCTION("rollback", JsSingleKVStore::Rollback),
56             DECLARE_NAPI_FUNCTION("enableSync", JsSingleKVStore::EnableSync),
57             DECLARE_NAPI_FUNCTION("setSyncRange", JsSingleKVStore::SetSyncRange),
58             DECLARE_NAPI_FUNCTION("backup", JsSingleKVStore::Backup),
59             DECLARE_NAPI_FUNCTION("restore", JsSingleKVStore::Restore),
60             /* JsDeviceKVStore externs JsSingleKVStore */
61             DECLARE_NAPI_FUNCTION("get", JsDeviceKVStore::Get),
62             DECLARE_NAPI_FUNCTION("getEntries", JsDeviceKVStore::GetEntries),
63             DECLARE_NAPI_FUNCTION("getResultSet", JsDeviceKVStore::GetResultSet),
64             DECLARE_NAPI_FUNCTION("getResultSize", JsDeviceKVStore::GetResultSize),
65             DECLARE_NAPI_FUNCTION("closeResultSet", JsSingleKVStore::CloseResultSet),
66             DECLARE_NAPI_FUNCTION("removeDeviceData", JsSingleKVStore::RemoveDeviceData),
67             DECLARE_NAPI_FUNCTION("sync", JsSingleKVStore::Sync),
68             DECLARE_NAPI_FUNCTION("on", JsSingleKVStore::OnEvent), /* same to JsSingleKVStore */
69             DECLARE_NAPI_FUNCTION("off", JsSingleKVStore::OffEvent) /* same to JsSingleKVStore */
70         };
71         return properties;
72     };
73     return JSUtil::DefineClass(env, "ohos.data.distributedKVStore", "DeviceKVStore", lambda, JsDeviceKVStore::New);
74 }
75 
76 /*
77  * [JS API Prototype]
78  * [AsyncCallback]
79  *      get(deviceId:string, key:string, callback:AsyncCallback<boolean|string|number|Uint8Array>):void;
80  * [Promise]
81  *      get(deviceId:string, key:string):Promise<boolean|string|number|Uint8Array>;
82  */
Get(napi_env env,napi_callback_info info)83 napi_value JsDeviceKVStore::Get(napi_env env, napi_callback_info info)
84 {
85     struct GetContext : public ContextBase {
86         std::string deviceId;
87         std::string key;
88         JSUtil::KvStoreVariant value;
89     };
90     auto ctxt = std::make_shared<GetContext>();
91     auto input = [env, ctxt](size_t argc, napi_value* argv) {
92         // number 2 means: required 2 arguments, <deviceId> + <key>
93         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT,
94             "Parameter error:Mandatory parameters are left unspecified");
95         if (argc > 1) {
96             ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->deviceId);
97             ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, Status::INVALID_ARGUMENT,
98                 "Parameter error:parameter deviceId must be string and not allow empty");
99         }
100         int32_t pos = (argc == 1) ? 0 : 1;
101         ctxt->status = JSUtil::GetValue(env, argv[pos], ctxt->key);
102         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, Status::INVALID_ARGUMENT,
103             "Parameter error:type of key must be string and not allow empty");
104     };
105     ctxt->GetCbInfo(env, info, input);
106     ASSERT_NULL(!ctxt->isThrowError, "DeviceGet exits");
107 
108     auto execute = [ctxt]() {
109         std::string deviceKey = GetDeviceKey(ctxt->deviceId, ctxt->key);
110         OHOS::DistributedKv::Key key(deviceKey);
111         OHOS::DistributedKv::Value value;
112         auto kvStore = reinterpret_cast<JsDeviceKVStore*>(ctxt->native)->GetKvStorePtr();
113         if (kvStore == nullptr) {
114             return;
115         }
116         ASSERT_STATUS(ctxt, "kvStore->result() failed!");
117         bool isSchemaStore = reinterpret_cast<JsDeviceKVStore*>(ctxt->native)->IsSchemaStore();
118         Status status = kvStore->Get(key, value);
119         ZLOGD("kvStore->Get return %{public}d", status);
120         ctxt->value = isSchemaStore ? value.ToString() : JSUtil::Blob2VariantValue(value);
121         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
122             napi_ok : napi_generic_failure;
123     };
124     auto output = [env, ctxt](napi_value& result) {
125         ctxt->status = JSUtil::SetValue(env, ctxt->value, result);
126         ASSERT_STATUS(ctxt, "output failed");
127     };
128     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
129 }
130 
131 struct VariantArgs {
132     /* input arguments' combinations */
133     DataQuery dataQuery;
134     std::string errMsg = "";
135 };
136 
GetVariantArgs(napi_env env,size_t argc,napi_value * argv,VariantArgs & va)137 static JSUtil::StatusMsg GetVariantArgs(napi_env env, size_t argc, napi_value* argv, VariantArgs& va)
138 {
139     int32_t pos = (argc == 1) ? 0 : 1;
140     napi_valuetype type = napi_undefined;
141     JSUtil::StatusMsg statusMsg = napi_typeof(env, argv[pos], &type);
142     if (statusMsg != napi_ok || (type != napi_string && type != napi_object)) {
143         va.errMsg = "Parameter error:parameters keyPrefix/query must be string or object";
144         return statusMsg != napi_ok ? statusMsg.status : napi_invalid_arg;
145     }
146     if (type == napi_string) {
147         std::string keyPrefix;
148         statusMsg = JSUtil::GetValue(env, argv[pos], keyPrefix);
149         if (keyPrefix.empty()) {
150             va.errMsg = "Parameter error:parameters keyPrefix must be string";
151             return napi_invalid_arg;
152         }
153         va.dataQuery.KeyPrefix(keyPrefix);
154     } else {
155         bool result = false;
156         statusMsg = napi_instanceof(env, argv[pos], JsQuery::Constructor(env), &result);
157         if ((statusMsg.status == napi_ok) && (result != false)) {
158             JsQuery *jsQuery = nullptr;
159             statusMsg = JSUtil::Unwrap(env, argv[pos], reinterpret_cast<void **>(&jsQuery), JsQuery::Constructor(env));
160             if (jsQuery == nullptr) {
161                 va.errMsg = "Parameter error:The parameters query must be string";
162                 return napi_invalid_arg;
163             }
164             va.dataQuery = jsQuery->GetDataQuery();
165         } else {
166             statusMsg = JSUtil::GetValue(env, argv[pos], va.dataQuery);
167             ZLOGD("kvStoreDataShare->GetResultSet return %{public}d", statusMsg.status);
168             statusMsg.jsApiType = JSUtil::DATASHARE;
169         }
170     }
171     std::string deviceId;
172     if (argc > 1) {
173         JSUtil::GetValue(env, argv[0], deviceId);
174         va.dataQuery.DeviceId(deviceId);
175     }
176     return statusMsg;
177 };
178 
179 /*
180  * [JS API Prototype]
181  *  getEntries(deviceId:string, keyPrefix:string, callback:AsyncCallback<Entry[]>):void
182  *  getEntries(deviceId:string, keyPrefix:string):Promise<Entry[]>
183  *
184  *  getEntries(query:Query, callback:AsyncCallback<Entry[]>):void
185  *  getEntries(query:Query) : Promise<Entry[]>
186  *
187  *  getEntries(deviceId:string, query:Query):callback:AsyncCallback<Entry[]>):void
188  *  getEntries(deviceId:string, query:Query):Promise<Entry[]>
189  */
GetEntries(napi_env env,napi_callback_info info)190 napi_value JsDeviceKVStore::GetEntries(napi_env env, napi_callback_info info)
191 {
192     struct GetEntriesContext : public ContextBase {
193         VariantArgs va;
194         std::vector<Entry> entries;
195     };
196     auto ctxt = std::make_shared<GetEntriesContext>();
197     auto input = [env, ctxt](size_t argc, napi_value* argv) {
198         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT,
199             "Parameter error:Mandatory parameters are left unspecified");
200         ctxt->status = GetVariantArgs(env, argc, argv, ctxt->va);
201         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, Status::INVALID_ARGUMENT, ctxt->va.errMsg);
202     };
203     ctxt->GetCbInfo(env, info, input);
204     ASSERT_NULL(!ctxt->isThrowError, "GetEntries exit");
205 
206     auto execute = [ctxt]() {
207         auto kvStore = reinterpret_cast<JsDeviceKVStore*>(ctxt->native)->GetKvStorePtr();
208         if (kvStore == nullptr) {
209             return;
210         }
211         Status status = kvStore->GetEntries(ctxt->va.dataQuery, ctxt->entries);
212         ZLOGD("kvStore->GetEntries() return %{public}d", status);
213         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
214             napi_ok : napi_generic_failure;
215     };
216     auto output = [env, ctxt](napi_value& result) {
217         auto isSchemaStore = reinterpret_cast<JsDeviceKVStore*>(ctxt->native)->IsSchemaStore();
218         ctxt->status = JSUtil::SetValue(env, ctxt->entries, result, isSchemaStore);
219         ASSERT_STATUS(ctxt, "output failed!");
220     };
221     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
222 }
223 
224 /*
225  * [JS API Prototype]
226  *  getResultSet(deviceId:string, keyPrefix:string, callback:AsyncCallback<KvStoreResultSet>):void
227  *  getResultSet(deviceId:string, keyPrefix:string):Promise<KvStoreResultSet>
228  *
229  *  getResultSet(query:Query, callback:AsyncCallback<KvStoreResultSet>):void
230  *  getResultSet(query:Query):Promise<KvStoreResultSet>
231  *
232  *  getResultSet(deviceId:string, query:Query, callback:AsyncCallback<KvStoreResultSet>):void
233  *  getResultSet(deviceId:string, query:Query):Promise<KvStoreResultSet>
234  */
GetResultSet(napi_env env,napi_callback_info info)235 napi_value JsDeviceKVStore::GetResultSet(napi_env env, napi_callback_info info)
236 {
237     struct GetResultSetContext : public ContextBase {
238         VariantArgs va;
239         JsKVStoreResultSet* resultSet = nullptr;
240         napi_ref ref = nullptr;
241     };
242     auto ctxt = std::make_shared<GetResultSetContext>();
243     auto input = [env, ctxt](size_t argc, napi_value* argv) {
244         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT,
245             "Parameter error:Mandatory parameters are left unspecified");
246         JSUtil::StatusMsg statusMsg = GetVariantArgs(env, argc, argv, ctxt->va);
247         ctxt->status = statusMsg.status;
248         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, Status::INVALID_ARGUMENT, ctxt->va.errMsg);
249         ASSERT_PERMISSION_ERR(ctxt,
250             !JSUtil::IsSystemApi(statusMsg.jsApiType) ||
251                 reinterpret_cast<JsSingleKVStore *>(ctxt->native)->IsSystemApp(), Status::PERMISSION_DENIED, "");
252         ctxt->ref = JSUtil::NewWithRef(env, 0, nullptr, reinterpret_cast<void **>(&ctxt->resultSet),
253             JsKVStoreResultSet::Constructor(env));
254         ASSERT_BUSINESS_ERR(ctxt, ctxt->resultSet != nullptr, Status::INVALID_ARGUMENT,
255             "Parameter error:resultSet is null");
256     };
257     ctxt->GetCbInfo(env, info, input);
258     ASSERT_NULL(!ctxt->isThrowError, "GetResultSet exit");
259 
260     auto execute = [ctxt]() {
261         std::shared_ptr<KvStoreResultSet> kvResultSet;
262         auto kvStore = reinterpret_cast<JsDeviceKVStore*>(ctxt->native)->GetKvStorePtr();
263         if (kvStore == nullptr) {
264             return;
265         }
266         Status status = kvStore->GetResultSet(ctxt->va.dataQuery, kvResultSet);
267         ZLOGD("kvStore->GetResultSet() return %{public}d", status);
268         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
269             napi_ok : napi_generic_failure;
270         ctxt->resultSet->SetInstance(kvResultSet);
271         bool isSchema = reinterpret_cast<JsDeviceKVStore*>(ctxt->native)->IsSchemaStore();
272         ctxt->resultSet->SetSchema(isSchema);
273     };
274     auto output = [env, ctxt](napi_value& result) {
275         ctxt->status = napi_get_reference_value(env, ctxt->ref, &result);
276         napi_delete_reference(env, ctxt->ref);
277         ASSERT_STATUS(ctxt, "output KvResultSet failed");
278     };
279     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
280 }
281 
282 /*
283  * [JS API Prototype]
284  *  getResultSize(query:Query, callback: AsyncCallback<number>):void
285  *  getResultSize(query:Query):Promise<number>
286  *
287  *  getResultSize(deviceId:string, query:Query, callback: AsyncCallback<number>):void
288  *  getResultSize(deviceId:string, query:Query):Promise<number>
289  */
GetResultSize(napi_env env,napi_callback_info info)290 napi_value JsDeviceKVStore::GetResultSize(napi_env env, napi_callback_info info)
291 {
292     struct ResultSizeContext : public ContextBase {
293         VariantArgs va;
294         int resultSize = 0;
295     };
296     auto ctxt = std::make_shared<ResultSizeContext>();
297     auto input = [env, ctxt](size_t argc, napi_value* argv) {
298         ASSERT_BUSINESS_ERR(ctxt, argc >= 1, Status::INVALID_ARGUMENT,
299             "Parameter error:Mandatory parameters are left unspecified");
300         ctxt->status = GetVariantArgs(env, argc, argv, ctxt->va);
301         ASSERT_BUSINESS_ERR(ctxt, ctxt->status != napi_invalid_arg, Status::INVALID_ARGUMENT, ctxt->va.errMsg);
302     };
303 
304     ctxt->GetCbInfo(env, info, input);
305     ASSERT_NULL(!ctxt->isThrowError, "GetResultSize exit");
306 
307     auto execute = [ctxt]() {
308         auto kvStore = reinterpret_cast<JsDeviceKVStore*>(ctxt->native)->GetKvStorePtr();
309         if (kvStore == nullptr) {
310             return;
311         }
312         Status status = kvStore->GetCount(ctxt->va.dataQuery, ctxt->resultSize);
313         ZLOGD("kvStore->GetCount() return %{public}d", status);
314         ctxt->status = (GenerateNapiError(status, ctxt->jsCode, ctxt->error) == Status::SUCCESS) ?
315             napi_ok : napi_generic_failure;
316     };
317     auto output = [env, ctxt](napi_value& result) {
318         ctxt->status = JSUtil::SetValue(env, static_cast<int32_t>(ctxt->resultSize), result);
319         ASSERT_STATUS(ctxt, "output resultSize failed!");
320     };
321     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
322 }
323 
New(napi_env env,napi_callback_info info)324 napi_value JsDeviceKVStore::New(napi_env env, napi_callback_info info)
325 {
326     ZLOGD("Constructor single kv store!");
327     std::string storeId;
328     auto ctxt = std::make_shared<ContextBase>();
329     auto input = [env, ctxt, &storeId](size_t argc, napi_value* argv) {
330         // required 2 arguments :: <storeId> <options>
331         ASSERT_BUSINESS_ERR(ctxt, argc >= 2, Status::INVALID_ARGUMENT,
332             "Parameter error:Mandatory parameters are left unspecified");
333         ctxt->status = JSUtil::GetValue(env, argv[0], storeId);
334         ASSERT_BUSINESS_ERR(ctxt, (ctxt->status == napi_ok) && !storeId.empty(), Status::INVALID_ARGUMENT,
335             "The type of storeId must be string.");
336     };
337     ctxt->GetCbInfoSync(env, info, input);
338     ASSERT_NULL(!ctxt->isThrowError, "New JsDeviceKVStore exit");
339 
340     JsDeviceKVStore* kvStore = new (std::nothrow) JsDeviceKVStore(storeId);
341     ASSERT_ERR(env, kvStore != nullptr, Status::INVALID_ARGUMENT, "Parameter error:kvStore is nullptr");
342 
343     auto finalize = [](napi_env env, void* data, void* hint) {
344         ZLOGI("deviceKvStore finalize.");
345         auto* kvStore = reinterpret_cast<JsDeviceKVStore*>(data);
346         ASSERT_VOID(kvStore != nullptr, "kvStore is null!");
347         delete kvStore;
348     };
349     ASSERT_CALL(env, napi_wrap(env, ctxt->self, kvStore, finalize, nullptr, nullptr), kvStore);
350     return ctxt->self;
351 }
352 } // namespace OHOS::DistributedKVStore
353