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 
16 #include "js_distributedobject.h"
17 
18 #include <cstring>
19 
20 #include "js_common.h"
21 #include "js_object_wrapper.h"
22 #include "js_util.h"
23 #include "logger.h"
24 #include "napi_queue.h"
25 #include "object_error.h"
26 #include "objectstore_errors.h"
27 
28 namespace OHOS::ObjectStore {
29 constexpr size_t KEY_SIZE = 64;
30 
JSConstructor(napi_env env,napi_callback_info info)31 napi_value JSDistributedObject::JSConstructor(napi_env env, napi_callback_info info)
32 {
33     LOG_INFO("start");
34     napi_value thisVar = nullptr;
35     void *data = nullptr;
36     napi_status status = napi_get_cb_info(env, info, nullptr, 0, &thisVar, &data);
37     NOT_MATCH_RETURN_NULL(status == napi_ok);
38     return thisVar;
39 }
40 
41 // get(key: string): ValueType;
JSGet(napi_env env,napi_callback_info info)42 napi_value JSDistributedObject::JSGet(napi_env env, napi_callback_info info)
43 {
44     size_t requireArgc = 1;
45     size_t argc = 1;
46     napi_value argv[1] = { 0 };
47     napi_value thisVar = nullptr;
48     void *data = nullptr;
49     char key[KEY_SIZE] = { 0 };
50     size_t keyLen = 0;
51     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
52     NOT_MATCH_RETURN_NULL(status == napi_ok && argc >= requireArgc);
53 
54     status = napi_get_value_string_utf8(env, argv[0], key, KEY_SIZE, &keyLen);
55     NOT_MATCH_RETURN_NULL(status == napi_ok);
56 
57     JSObjectWrapper *wrapper = nullptr;
58     status = napi_unwrap(env, thisVar, (void **)&wrapper);
59     NOT_MATCH_RETURN_NULL(status == napi_ok && wrapper != nullptr && wrapper->GetObject() != nullptr);
60     napi_value result = nullptr;
61     if (wrapper->IsUndefined(key)) {
62         napi_get_undefined(env, &result);
63         return result;
64     }
65     DoGet(env, wrapper, key, result);
66     return result;
67 }
68 
69 // put(key: string, value: ValueType): void;
JSPut(napi_env env,napi_callback_info info)70 napi_value JSDistributedObject::JSPut(napi_env env, napi_callback_info info)
71 {
72     size_t requireArgc = 2;
73     size_t argc = 2;
74     napi_value argv[2] = { 0 };
75     napi_value thisVar = nullptr;
76     char key[KEY_SIZE] = { 0 };
77     size_t keyLen = 0;
78     napi_valuetype valueType;
79     napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
80     NOT_MATCH_RETURN_NULL(status == napi_ok && argc >= requireArgc);
81 
82     status = napi_get_value_string_utf8(env, argv[0], key, KEY_SIZE, &keyLen);
83     NOT_MATCH_RETURN_NULL(status == napi_ok);
84 
85     JSObjectWrapper *wrapper = nullptr;
86     status = napi_unwrap(env, thisVar, (void **)&wrapper);
87     NOT_MATCH_RETURN_NULL(status == napi_ok && wrapper != nullptr && wrapper->GetObject() != nullptr);
88 
89     status = napi_typeof(env, argv[1], &valueType);
90     NOT_MATCH_RETURN_NULL(status == napi_ok);
91     if (valueType == napi_undefined) {
92         wrapper->AddUndefined(key);
93         return nullptr;
94     }
95     wrapper->DeleteUndefined(key);
96     DoPut(env, wrapper, key, valueType, argv[1]);
97     LOG_INFO("put %{public}s success", key);
98     return nullptr;
99 }
100 
GetCons(napi_env env)101 napi_value JSDistributedObject::GetCons(napi_env env)
102 {
103     static thread_local napi_ref g_instance = nullptr;
104     napi_value distributedObjectClass = nullptr;
105     if (g_instance != nullptr) {
106         napi_status status = napi_get_reference_value(env, g_instance, &distributedObjectClass);
107         NOT_MATCH_RETURN_NULL(status == napi_ok);
108         return distributedObjectClass;
109     }
110     const char *distributedObjectName = "DistributedObject";
111     napi_property_descriptor distributedObjectDesc[] = {
112         DECLARE_NAPI_FUNCTION("put", JSDistributedObject::JSPut),
113         DECLARE_NAPI_FUNCTION("get", JSDistributedObject::JSGet),
114         DECLARE_NAPI_FUNCTION("save", JSDistributedObject::JSSave),
115         DECLARE_NAPI_FUNCTION("revokeSave", JSDistributedObject::JSRevokeSave),
116         DECLARE_NAPI_FUNCTION("bindAssetStore", JSDistributedObject::JSBindAssetStore),
117     };
118 
119     napi_status status = napi_define_class(env, distributedObjectName, strlen(distributedObjectName),
120         JSDistributedObject::JSConstructor, nullptr, sizeof(distributedObjectDesc) / sizeof(distributedObjectDesc[0]),
121         distributedObjectDesc, &distributedObjectClass);
122     NOT_MATCH_RETURN_NULL(status == napi_ok);
123     if (g_instance == nullptr) {
124         status = napi_create_reference(env, distributedObjectClass, 1, &g_instance);
125         NOT_MATCH_RETURN_NULL(status == napi_ok);
126     }
127     return distributedObjectClass;
128 }
129 
DoPut(napi_env env,JSObjectWrapper * wrapper,char * key,napi_valuetype type,napi_value value)130 void JSDistributedObject::DoPut(
131     napi_env env, JSObjectWrapper *wrapper, char *key, napi_valuetype type, napi_value value)
132 {
133     std::string keyString = key;
134     switch (type) {
135         case napi_boolean: {
136             bool putValue = false;
137             napi_status status = JSUtil::GetValue(env, value, putValue);
138             NOT_MATCH_RETURN_VOID(status == napi_ok);
139             wrapper->GetObject()->PutBoolean(keyString, putValue);
140             break;
141         }
142         case napi_number: {
143             double putValue = 0;
144             napi_status status = JSUtil::GetValue(env, value, putValue);
145             NOT_MATCH_RETURN_VOID(status == napi_ok);
146             wrapper->GetObject()->PutDouble(keyString, putValue);
147             break;
148         }
149         case napi_string: {
150             std::string putValue;
151             napi_status status = JSUtil::GetValue(env, value, putValue);
152             NOT_MATCH_RETURN_VOID(status == napi_ok);
153             wrapper->GetObject()->PutString(keyString, putValue);
154             break;
155         }
156         case napi_object: {
157             std::vector<uint8_t> putValue;
158             napi_status status = JSUtil::GetValue(env, value, putValue);
159             NOT_MATCH_RETURN_VOID(status == napi_ok);
160             wrapper->GetObject()->PutComplex(keyString, putValue);
161             break;
162         }
163         default: {
164             LOG_ERROR("error type! %{public}d", type);
165             break;
166         }
167     }
168 }
169 
DoGet(napi_env env,JSObjectWrapper * wrapper,char * key,napi_value & value)170 void JSDistributedObject::DoGet(napi_env env, JSObjectWrapper *wrapper, char *key, napi_value &value)
171 {
172     std::string keyString = key;
173     Type type = TYPE_STRING;
174     wrapper->GetObject()->GetType(keyString, type);
175     LOG_DEBUG("get type %{public}s %{public}d", key, type);
176     switch (type) {
177         case TYPE_STRING: {
178             std::string result;
179             uint32_t ret = wrapper->GetObject()->GetString(keyString, result);
180             NOT_MATCH_RETURN_VOID(ret == SUCCESS);
181             napi_status status = JSUtil::SetValue(env, result, value);
182             NOT_MATCH_RETURN_VOID(status == napi_ok);
183             break;
184         }
185         case TYPE_DOUBLE: {
186             double result;
187             uint32_t ret = wrapper->GetObject()->GetDouble(keyString, result);
188             LOG_DEBUG("%{public}f", result);
189             NOT_MATCH_RETURN_VOID(ret == SUCCESS);
190             napi_status status = JSUtil::SetValue(env, result, value);
191             NOT_MATCH_RETURN_VOID(status == napi_ok);
192             break;
193         }
194         case TYPE_BOOLEAN: {
195             bool result;
196             uint32_t ret = wrapper->GetObject()->GetBoolean(keyString, result);
197             LOG_DEBUG("%{public}d", result);
198             NOT_MATCH_RETURN_VOID(ret == SUCCESS);
199             napi_status status = JSUtil::SetValue(env, result, value);
200             NOT_MATCH_RETURN_VOID(status == napi_ok);
201             break;
202         }
203         case TYPE_COMPLEX: {
204             std::vector<uint8_t> result;
205             uint32_t ret = wrapper->GetObject()->GetComplex(keyString, result);
206             NOT_MATCH_RETURN_VOID(ret == SUCCESS);
207             napi_status status = JSUtil::SetValue(env, result, value);
208             NOT_MATCH_RETURN_VOID(status == napi_ok);
209             break;
210         }
211         default: {
212             LOG_ERROR("error type! %{public}d", type);
213             break;
214         }
215     }
216 }
217 
218 // save(deviceId: string, version: number, callback?:AsyncCallback<SaveSuccessResponse>): void;
219 // save(deviceId: string, version: number): Promise<SaveSuccessResponse>;
JSSave(napi_env env,napi_callback_info info)220 napi_value JSDistributedObject::JSSave(napi_env env, napi_callback_info info)
221 {
222     LOG_DEBUG("JSSave()");
223     struct SaveContext : public ContextBase {
224         double version;
225         std::string deviceId;
226         JSObjectWrapper *wrapper;
227     };
228     auto ctxt = std::make_shared<SaveContext>();
229     std::function<void(size_t argc, napi_value * argv)> getCbOpe = [env, ctxt](size_t argc, napi_value *argv) {
230         INVALID_ARGS_RETURN_ERROR(ctxt, argc >= 2, "arguments error", std::make_shared<ParametersNum>("1 or 2"));
231         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->deviceId);
232         INVALID_ARGS_RETURN_ERROR(ctxt, ctxt->status == napi_ok, "arguments error",
233             std::make_shared<ParametersType>("deviceId", "string"));
234 
235         ctxt->status = JSUtil::GetValue(env, argv[1], ctxt->version);
236         INVALID_STATUS_RETURN_ERROR(ctxt, "invalid arg[1], i.e. invalid version!");
237         JSObjectWrapper *wrapper = nullptr;
238         napi_status status = napi_unwrap(env, ctxt->self, (void **)&wrapper);
239         NOT_MATCH_RETURN_VOID(status == napi_ok && wrapper != nullptr && wrapper->GetObject() != nullptr);
240         ctxt->wrapper = wrapper;
241     };
242     ctxt->GetCbInfo(env, info, getCbOpe);
243     NAPI_ASSERT_ERRCODE(env, ctxt->status != napi_invalid_arg, ctxt->error);
244     auto execute = [ctxt]() {
245         LOG_INFO("start");
246         CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper != nullptr, ctxt, "wrapper is null");
247         CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper->GetObject() != nullptr, ctxt, "object is null");
248         uint32_t status = ctxt->wrapper->GetObject()->Save(ctxt->deviceId);
249         INVALID_API_THROW_ERROR(status != ERR_PROCESSING);
250         INVALID_STATUS_THROW_ERROR(status == SUCCESS, "operation failed");
251         ctxt->status = napi_ok;
252         LOG_INFO("end");
253     };
254     auto output = [env, ctxt](napi_value &result) {
255         if (ctxt->status == napi_ok) {
256             CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper != nullptr, ctxt, "wrapper is null");
257             CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper->GetObject() != nullptr, ctxt, "object is null");
258             std::string &sessionId = ctxt->wrapper->GetObject()->GetSessionId();
259             ctxt->status = napi_new_instance(
260                 env, GetSaveResultCons(env, sessionId, ctxt->version, ctxt->deviceId), 0, nullptr, &result);
261             INVALID_STATUS_RETURN_ERROR(ctxt, "output failed!");
262         }
263     };
264     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
265 }
266 
267 // revokeSave(callback?:AsyncCallback<RevokeSaveSuccessResponse>): void;
268 // revokeSave(): Promise<RevokeSaveSuccessResponse>;
JSRevokeSave(napi_env env,napi_callback_info info)269 napi_value JSDistributedObject::JSRevokeSave(napi_env env, napi_callback_info info)
270 {
271     LOG_DEBUG("JSRevokeSave()");
272     struct RevokeSaveContext : public ContextBase {
273         JSObjectWrapper *wrapper;
274     };
275     auto ctxt = std::make_shared<RevokeSaveContext>();
276     std::function<void(size_t argc, napi_value * argv)> getCbOpe = [env, ctxt](size_t argc, napi_value *argv) {
277         // required 1 arguments :: <key>
278         JSObjectWrapper *wrapper = nullptr;
279         napi_status status = napi_unwrap(env, ctxt->self, (void **)&wrapper);
280         NOT_MATCH_RETURN_VOID(status == napi_ok && wrapper != nullptr && wrapper->GetObject() != nullptr);
281         ctxt->wrapper = wrapper;
282     };
283     ctxt->GetCbInfo(env, info, getCbOpe);
284     if (ctxt->status != napi_ok) {
285         napi_throw_error((env), std::to_string(ctxt->error->GetCode()).c_str(), ctxt->error->GetMessage().c_str());
286         return nullptr;
287     }
288     auto execute = [ctxt]() {
289         CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper != nullptr, ctxt, "wrapper is null");
290         CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper->GetObject() != nullptr, ctxt, "object is null");
291         uint32_t status = ctxt->wrapper->GetObject()->RevokeSave();
292         INVALID_API_THROW_ERROR(status != ERR_PROCESSING);
293         INVALID_STATUS_THROW_ERROR(status == SUCCESS, "operation failed");
294         ctxt->status = napi_ok;
295         LOG_INFO("end");
296     };
297     auto output = [env, ctxt](napi_value &result) {
298         if (ctxt->status == napi_ok) {
299             CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper != nullptr, ctxt, "wrapper is null");
300             CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper->GetObject() != nullptr, ctxt, "object is null");
301             ctxt->status = napi_new_instance(env,
302                 JSDistributedObject::GetRevokeSaveResultCons(env, ctxt->wrapper->GetObject()->GetSessionId()), 0,
303                 nullptr, &result);
304             INVALID_STATUS_RETURN_ERROR(ctxt, "output failed!");
305         }
306     };
307     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
308 }
309 
GetSaveResultCons(napi_env env,std::string & sessionId,double version,std::string deviceId)310 napi_value JSDistributedObject::GetSaveResultCons(
311     napi_env env, std::string &sessionId, double version, std::string deviceId)
312 {
313     const char *objectName = "SaveResult";
314     napi_value napiSessionId, napiVersion, napiDeviceId;
315     napi_value result;
316 
317     napi_status status = JSUtil::SetValue(env, sessionId, napiSessionId);
318     NOT_MATCH_RETURN_NULL(status == napi_ok);
319     status = JSUtil::SetValue(env, version, napiVersion);
320     NOT_MATCH_RETURN_NULL(status == napi_ok);
321     status = JSUtil::SetValue(env, deviceId, napiDeviceId);
322     NOT_MATCH_RETURN_NULL(status == napi_ok);
323     napi_property_descriptor desc[] = {
324         DECLARE_NAPI_PROPERTY("sessionId", napiSessionId),
325         DECLARE_NAPI_PROPERTY("version", napiVersion),
326         DECLARE_NAPI_PROPERTY("deviceId", napiDeviceId)
327     };
328 
329     status = napi_define_class(env, objectName, strlen(objectName), JSDistributedObject::JSConstructor, nullptr,
330         sizeof(desc) / sizeof(desc[0]), desc, &result);
331     NOT_MATCH_RETURN_NULL(status == napi_ok);
332     return result;
333 }
334 
GetRevokeSaveResultCons(napi_env env,std::string & sessionId)335 napi_value JSDistributedObject::GetRevokeSaveResultCons(napi_env env, std::string &sessionId)
336 {
337     const char *objectName = "RevokeSaveResult";
338     napi_value napiSessionId;
339     napi_value result;
340 
341     napi_status status = JSUtil::SetValue(env, sessionId, napiSessionId);
342     NOT_MATCH_RETURN_NULL(status == napi_ok);
343     napi_property_descriptor desc[] = {
344         DECLARE_NAPI_PROPERTY("sessionId", napiSessionId)
345     };
346 
347     status = napi_define_class(env, objectName, strlen(objectName), JSDistributedObject::JSConstructor, nullptr,
348         sizeof(desc) / sizeof(desc[0]), desc, &result);
349     NOT_MATCH_RETURN_NULL(status == napi_ok);
350     return result;
351 }
352 
JSBindAssetStore(napi_env env,napi_callback_info info)353 napi_value JSDistributedObject::JSBindAssetStore(napi_env env, napi_callback_info info)
354 {
355     struct BindAssetStoreContext : public ContextBase {
356         std::string assetKey;
357         AssetBindInfo bindInfo;
358         JSObjectWrapper *wrapper;
359     };
360     auto ctxt = std::make_shared<BindAssetStoreContext>();
361     auto input = [env, ctxt](size_t argc, napi_value *argv) {
362         INVALID_ARGS_RETURN_ERROR(ctxt, argc >= 2, "arguments error", std::make_shared<ParametersNum>("2"));
363         ctxt->status = JSUtil::GetValue(env, argv[0], ctxt->assetKey);
364         INVALID_ARGS_RETURN_ERROR(ctxt, ctxt->status == napi_ok, "arguments error",
365             std::make_shared<ParametersType>("assetKey", "string"));
366 
367         ctxt->status = JSUtil::GetValue(env, argv[1], ctxt->bindInfo);
368         INVALID_ARGS_RETURN_ERROR(ctxt, ctxt->status == napi_ok, "arguments error",
369             std::make_shared<ParametersType>("bindInfo", "BindInfo"));
370         JSObjectWrapper *wrapper = nullptr;
371         napi_status status = napi_unwrap(env, ctxt->self, (void **)&wrapper);
372         NOT_MATCH_RETURN_VOID(status == napi_ok && wrapper != nullptr && wrapper->GetObject() != nullptr);
373         ctxt->wrapper = wrapper;
374     };
375     ctxt->GetCbInfo(env, info, input);
376     NAPI_ASSERT_ERRCODE(env, ctxt->status == napi_ok, ctxt->error);
377     auto execute = [ctxt]() {
378         CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper != nullptr, ctxt, "wrapper is null");
379         CHECH_STATUS_RETURN_VOID(env, ctxt->wrapper->GetObject() != nullptr, ctxt, "object is null");
380         uint32_t status = ctxt->wrapper->GetObject()->BindAssetStore(ctxt->assetKey, ctxt->bindInfo);
381         LOG_INFO("BindAssetStore return: %{public}d", status);
382         INVALID_API_THROW_ERROR(status != ERR_PROCESSING);
383         INVALID_STATUS_THROW_ERROR(status == SUCCESS, "operation failed");
384         ctxt->status = napi_ok;
385     };
386     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
387 }
388 } // namespace OHOS::ObjectStore