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