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