1 /*
2 * Copyright (c) 2024 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 #define LOG_TAG "Node"
17
18 #include "napi_node.h"
19
20 #include "napi_error_utils.h"
21 #include "napi_parser.h"
22
23 namespace OHOS::CollaborationEdit {
24 static __thread napi_ref g_node_cons_ref = nullptr;
25
Node(std::string name)26 Node::Node(std::string name) : AbstractType(), name_(name)
27 {}
28
~Node()29 Node::~Node()
30 {}
31
Init(napi_env env,napi_value exports)32 void Node::Init(napi_env env, napi_value exports)
33 {
34 napi_value cons = Node::Constructor(env);
35 NAPI_CALL_RETURN_VOID(env, napi_create_reference(env, cons, 1, &g_node_cons_ref));
36 NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, exports, "Node", cons));
37 LOG_DEBUG("Node::Init end.");
38 }
39
Constructor(napi_env env)40 napi_value Node::Constructor(napi_env env)
41 {
42 napi_property_descriptor descriptors[] = {
43 DECLARE_NAPI_FUNCTION("getId", GetUniqueId),
44 DECLARE_NAPI_FUNCTION("getName", GetName),
45 DECLARE_NAPI_FUNCTION("insertNodes", InsertNodes),
46 DECLARE_NAPI_FUNCTION("delete", Delete),
47 DECLARE_NAPI_FUNCTION("getChildren", GetChildren),
48 DECLARE_NAPI_FUNCTION("getJsonResult", GetJsonResult),
49 DECLARE_NAPI_FUNCTION("insertTexts", InsertTexts),
50 DECLARE_NAPI_FUNCTION("setAttributes", SetAttributes),
51 DECLARE_NAPI_FUNCTION("removeAttributes", RemoveAttributes),
52 DECLARE_NAPI_FUNCTION("getAttributes", GetAttributes),
53 DECLARE_NAPI_FUNCTION("setAsset", SetAsset),
54 };
55 napi_value cons = nullptr;
56 NAPI_CALL(env, napi_define_class(env, "Node", NAPI_AUTO_LENGTH, New, nullptr,
57 sizeof(descriptors) / sizeof(napi_property_descriptor), descriptors, &cons));
58 return cons;
59 }
60
New(napi_env env,napi_callback_info info)61 napi_value Node::New(napi_env env, napi_callback_info info)
62 {
63 napi_value newTarget = nullptr;
64 NAPI_CALL(env, napi_get_new_target(env, info, &newTarget));
65
66 size_t argc = 1;
67 napi_value argv[1] = {nullptr};
68 napi_value self = nullptr;
69 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
70
71 // create instance by 'new Node(name)'
72 if (newTarget != nullptr) {
73 napi_valuetype valueType = napi_undefined;
74 NAPI_CALL(env, napi_typeof(env, argv[0], &valueType));
75 if (valueType != napi_string) {
76 ThrowNapiError(env, Status::INVALID_ARGUMENT, "Param error. The name must be a string");
77 return nullptr;
78 }
79 std::string name = "";
80 NapiUtils::GetValue(env, argv[0], name);
81 Node *node = new (std::nothrow) Node(name);
82 ASSERT_THROW_BASE(env, node != nullptr, Status::INTERNAL_ERROR, "new: new node go wrong", self);
83 auto finalize = [](napi_env env, void *data, void *hint) {
84 Node *node = reinterpret_cast<Node *>(data);
85 delete node;
86 };
87 napi_status status = napi_wrap(env, self, node, finalize, nullptr, nullptr);
88 if (status != napi_ok) {
89 LOG_ERROR("napi_wrap failed. code:%{public}d", status);
90 delete node;
91 return nullptr;
92 }
93 return self;
94 }
95
96 // create instance by 'Node(name)'
97 napi_value cons = nullptr;
98 NAPI_CALL(env, napi_get_reference_value(env, g_node_cons_ref, &cons));
99 napi_value output = nullptr;
100 NAPI_CALL(env, napi_new_instance(env, cons, argc, argv, &output));
101 return output;
102 }
103
InnerGetName()104 std::string Node::InnerGetName()
105 {
106 return this->name_;
107 }
108
GetName(napi_env env,napi_callback_info info)109 napi_value Node::GetName(napi_env env, napi_callback_info info)
110 {
111 napi_value self = nullptr;
112 NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr));
113 Node *thisNode = nullptr;
114 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
115 ASSERT_THROW(env, thisNode != nullptr, Status::INTERNAL_ERROR, "unwrap self is null");
116 napi_value result;
117 NapiUtils::SetValue(env, thisNode->name_, result);
118 return result;
119 }
120
GetUniqueId(napi_env env,napi_callback_info info)121 napi_value Node::GetUniqueId(napi_env env, napi_callback_info info)
122 {
123 napi_value self = nullptr;
124 NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr));
125 Node *thisNode = nullptr;
126 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
127 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
128 ASSERT_THROW(env, thisNode->GetID().has_value(), Status::UNSUPPORTED_OPERATION, "empty id");
129 ID id = thisNode->GetID().value();
130 napi_value result;
131 NAPI_CALL(env, napi_create_object(env, &result));
132 napi_value jsDeviceId;
133 NapiUtils::SetValue(env, id.deviceId, jsDeviceId);
134 NAPI_CALL(env, napi_set_named_property(env, result, "id", jsDeviceId));
135 napi_value jsClock;
136 NapiUtils::SetValue(env, static_cast<int64_t>(id.clock), jsClock);
137 NAPI_CALL(env, napi_set_named_property(env, result, "clock", jsClock));
138 return result;
139 }
140
InsertNodes(napi_env env,napi_callback_info info)141 napi_value Node::InsertNodes(napi_env env, napi_callback_info info)
142 {
143 size_t argc = 2;
144 napi_value argv[2] = {nullptr};
145 napi_value self = nullptr;
146 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
147 Node *thisNode = nullptr;
148 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
149 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
150 ASSERT_THROW(env, thisNode->GetID().has_value(), Status::UNSUPPORTED_OPERATION, "empty id");
151 int64_t index = 0;
152 napi_status status = NapiUtils::GetValue(env, argv[0], index);
153 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: Read index go wrong.");
154 ASSERT_THROW(env, index >= 0, Status::INVALID_ARGUMENT, "Param Error: Invalid index.");
155 bool isArray = false;
156 NAPI_CALL(env, napi_is_array(env, argv[1], &isArray));
157 ASSERT_THROW(env, isArray, Status::INVALID_ARGUMENT, "Param Error: The nodes must be an array.");
158 uint32_t length = 0;
159 NAPI_CALL(env, napi_get_array_length(env, argv[1], &length));
160 LOG_INFO("length = %{public}u", length);
161 for (uint32_t i = 0; i < length; i++) {
162 napi_value jsNode = nullptr;
163 NAPI_CALL(env, napi_get_element(env, argv[1], i, &jsNode));
164 Node *tempNode = nullptr;
165 NAPI_CALL(env, napi_unwrap(env, jsNode, reinterpret_cast<void **>(&tempNode)));
166 auto [retCode, id] = thisNode->GetAdapter()->InsertNode(index + i, tempNode->InnerGetName());
167 if (retCode != SUCCESS || !id.has_value()) {
168 ThrowNapiError(env, retCode, "InsertNodes go wrong.");
169 return nullptr;
170 }
171 tempNode->SetID(id);
172 tempNode->SetTableName(thisNode->GetTableName());
173 tempNode->SetDBStore(thisNode->GetDBStore());
174 }
175 return nullptr;
176 }
177
Delete(napi_env env,napi_callback_info info)178 napi_value Node::Delete(napi_env env, napi_callback_info info)
179 {
180 size_t argc = 2;
181 napi_value argv[2] = {nullptr};
182 napi_value self = nullptr;
183 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
184 Node *thisNode = nullptr;
185 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
186 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
187 ASSERT_THROW(env, thisNode->GetID().has_value(), Status::UNSUPPORTED_OPERATION, "empty id");
188 int64_t index = 0;
189 napi_status status = NapiUtils::GetValue(env, argv[0], index);
190 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: Read index go wrong.");
191 ASSERT_THROW(env, index >= 0, Status::INVALID_ARGUMENT, "Param Error: Invalid index.");
192 int64_t length = 0;
193 status = NapiUtils::GetValue(env, argv[1], length);
194 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: Read length go wrong.");
195 ASSERT_THROW(env, length > 0, Status::INVALID_ARGUMENT, "Param Error: Invalid length.");
196
197 int32_t retCode = thisNode->GetAdapter()->DeleteChildren(index, length);
198 if (retCode != SUCCESS) {
199 ThrowNapiError(env, retCode, "Delete Nodes go wrong.");
200 }
201 return nullptr;
202 }
203
GetChildren(napi_env env,napi_callback_info info)204 napi_value Node::GetChildren(napi_env env, napi_callback_info info)
205 {
206 size_t argc = 2;
207 napi_value argv[2] = {nullptr};
208 napi_value self = nullptr;
209 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
210 Node *thisNode = nullptr;
211 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
212 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
213 ASSERT_THROW(env, thisNode->GetID().has_value(), Status::UNSUPPORTED_OPERATION, "empty id");
214 int64_t start = 0;
215 napi_status status = NapiUtils::GetValue(env, argv[0], start);
216 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: Read start index go wrong.");
217 ASSERT_THROW(env, start >= 0, Status::INVALID_ARGUMENT, "Param Error: Invalid start.");
218 int64_t end = 0;
219 status = NapiUtils::GetValue(env, argv[1], end);
220 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: Read end index go wrong.");
221 ASSERT_THROW(env, end >= 0, Status::INVALID_ARGUMENT, "Param Error: Invalid end.");
222 ASSERT_THROW(env, end > start, Status::INVALID_ARGUMENT, "Param Error: end should be greater than start.");
223
224 auto [retCode, result] = thisNode->GetAdapter()->GetChildren(start, end - start);
225 if (retCode != SUCCESS) {
226 ThrowNapiError(env, retCode, "Get Children go wrong.");
227 return nullptr;
228 }
229 // transfer string to node array
230 napi_value output = nullptr;
231 int ret = Parser::ParseJsonStrToJsChildren(env, result, thisNode, output);
232 if (ret != OK) {
233 ThrowNapiError(env, Status::INTERNAL_ERROR, "convert result go wrong.");
234 return nullptr;
235 }
236 return output;
237 }
238
GetJsonResult(napi_env env,napi_callback_info info)239 napi_value Node::GetJsonResult(napi_env env, napi_callback_info info)
240 {
241 napi_value self = nullptr;
242 NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr));
243 Node *thisNode = nullptr;
244 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
245 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
246 ASSERT_THROW(env, thisNode->GetID().has_value(), Status::UNSUPPORTED_OPERATION, "empty id");
247 auto [retCode, result] = thisNode->GetAdapter()->GetJsonString();
248 if (retCode != SUCCESS) {
249 ThrowNapiError(env, retCode, "toString go wrong.");
250 return nullptr;
251 }
252 napi_value output = nullptr;
253 napi_status status = NapiUtils::SetValue(env, result, output);
254 if (status != napi_ok || output == nullptr) {
255 ThrowNapiError(env, Status::INTERNAL_ERROR, "convert result go wrong.");
256 return nullptr;
257 }
258 return output;
259 }
260
InsertTexts(napi_env env,napi_callback_info info)261 napi_value Node::InsertTexts(napi_env env, napi_callback_info info)
262 {
263 size_t argc = 2;
264 napi_value argv[2] = {nullptr};
265 napi_value self = nullptr;
266 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
267 Node *thisNode = nullptr;
268 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
269 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
270 ASSERT_THROW(env, thisNode->GetID().has_value(), Status::UNSUPPORTED_OPERATION, "empty id");
271 int64_t index = 0;
272 napi_status status = NapiUtils::GetValue(env, argv[0], index);
273 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: Read index go wrong.");
274 ASSERT_THROW(env, index >= 0, Status::INVALID_ARGUMENT, "Param Error: Invalid index.");
275 bool isArray = false;
276 NAPI_CALL(env, napi_is_array(env, argv[1], &isArray));
277 ASSERT_THROW(env, isArray, Status::INVALID_ARGUMENT, "Param Error: The nodes must be an array.");
278 uint32_t length = 0;
279 NAPI_CALL(env, napi_get_array_length(env, argv[1], &length));
280 for (uint32_t i = 0; i < length; i++) {
281 napi_value jsText = nullptr;
282 NAPI_CALL(env, napi_get_element(env, argv[1], i, &jsText));
283 Text *tempText = nullptr;
284 NAPI_CALL(env, napi_unwrap(env, jsText, reinterpret_cast<void **>(&tempText)));
285 auto [retCode, id] = thisNode->GetAdapter()->InsertText(index + i);
286 if (retCode != SUCCESS || !id.has_value()) {
287 ThrowNapiError(env, retCode, "InsertText go wrong.");
288 return nullptr;
289 }
290 tempText->SetID(id);
291 tempText->SetTableName(thisNode->GetTableName());
292 tempText->SetDBStore(thisNode->GetDBStore());
293 }
294 return nullptr;
295 }
296
SetAttributes(napi_env env,napi_callback_info info)297 napi_value Node::SetAttributes(napi_env env, napi_callback_info info)
298 {
299 size_t argc = 1;
300 napi_value argv[1] = {nullptr};
301 napi_value self = nullptr;
302 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
303 Node *thisNode = nullptr;
304 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
305 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
306 ASSERT_THROW(env, thisNode->GetID().has_value(), Status::UNSUPPORTED_OPERATION, "empty id");
307
308 // convert input argument
309 napi_value keys = nullptr;
310 napi_get_all_property_names(env, argv[0], napi_key_own_only,
311 static_cast<napi_key_filter>(napi_key_enumerable | napi_key_skip_symbols), napi_key_numbers_to_strings, &keys);
312 uint32_t arrLen = 0;
313 napi_status status = napi_get_array_length(env, keys, &arrLen);
314 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: Parse argument go wrong.");
315 for (size_t i = 0; i < arrLen; i++) {
316 napi_value key = nullptr;
317 status = napi_get_element(env, keys, i, &key);
318 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: get element go wrong.");
319 std::string keyStr;
320 NapiUtils::GetValue(env, key, keyStr);
321 napi_value value = nullptr;
322 napi_get_property(env, argv[0], key, &value);
323 std::string valueStr;
324 int retCode = Parser::ParseVariantJsValueToStr(env, value, valueStr);
325 ASSERT(retCode == OK, "parse value go wrong.", nullptr);
326 retCode = thisNode->GetAdapter()->SetAttribute(keyStr, valueStr, 0);
327 ASSERT_THROW(env, retCode == SUCCESS, retCode, "Set attribute go wrong.");
328 }
329 return nullptr;
330 }
331
RemoveAttributes(napi_env env,napi_callback_info info)332 napi_value Node::RemoveAttributes(napi_env env, napi_callback_info info)
333 {
334 size_t argc = 1;
335 napi_value argv[1] = {nullptr};
336 napi_value self = nullptr;
337 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
338 Node *thisNode = nullptr;
339 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
340 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
341 bool isArray;
342 NAPI_CALL(env, napi_is_array(env, argv[0], &isArray));
343 if (!isArray) {
344 ThrowNapiError(env, Status::INVALID_ARGUMENT, "Param Error: The argument must be an array.");
345 return nullptr;
346 }
347 uint32_t length = 0;
348 NAPI_CALL(env, napi_get_array_length(env, argv[0], &length));
349 for (uint32_t i = 0; i < length; i++) {
350 napi_value jsAttributeName = nullptr;
351 NAPI_CALL(env, napi_get_element(env, argv[0], i, &jsAttributeName));
352 std::string attributeName;
353 NapiUtils::GetValue(env, jsAttributeName, attributeName);
354 int32_t retCode = thisNode->GetAdapter()->RemoveAttrribute(attributeName);
355 if (retCode != SUCCESS) {
356 ThrowNapiError(env, retCode, "RemoveAttrribute go wrong.");
357 return nullptr;
358 }
359 }
360 return nullptr;
361 }
362
GetAttributes(napi_env env,napi_callback_info info)363 napi_value Node::GetAttributes(napi_env env, napi_callback_info info)
364 {
365 napi_value self = nullptr;
366 NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &self, nullptr));
367 Node *thisNode = nullptr;
368 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
369 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
370 ASSERT_THROW(env, thisNode->GetID().has_value(), Status::UNSUPPORTED_OPERATION, "empty id");
371
372 auto [retCode, result] = thisNode->GetAdapter()->GetAttributes();
373 if (retCode != SUCCESS) {
374 ThrowNapiError(env, retCode, "GetAttributes go wrong.");
375 return nullptr;
376 }
377 // convert result string to record
378 napi_value output = nullptr;
379 int ret = Parser::ParseFromAttrsJsonStr(env, result, output);
380 if (ret != OK) {
381 ThrowNapiError(env, Status::INTERNAL_ERROR, "parse result go wrong.");
382 return nullptr;
383 }
384 return output;
385 }
386
SetAsset(napi_env env,napi_callback_info info)387 napi_value Node::SetAsset(napi_env env, napi_callback_info info)
388 {
389 size_t argc = 2;
390 napi_value argv[2] = {nullptr};
391 napi_value self = nullptr;
392 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &self, nullptr));
393 Node *thisNode = nullptr;
394 NAPI_CALL(env, napi_unwrap(env, self, reinterpret_cast<void **>(&thisNode)));
395 ASSERT(thisNode != nullptr, "unwrap self go wrong.", nullptr);
396 ASSERT_THROW(env, thisNode->GetID().has_value(), Status::UNSUPPORTED_OPERATION, "empty id");
397
398 // convert input argument
399 std::string assetKey;
400 napi_status status = NapiUtils::GetValue(env, argv[0], assetKey);
401 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: get asset key go wrong.");
402
403 std::string assetValue;
404 status = NapiUtils::GetValue(env, argv[1], assetValue);
405 ASSERT_THROW(env, status == napi_ok, Status::INVALID_ARGUMENT, "Param Error: get asset value go wrong.");
406
407 int32_t retCode = thisNode->GetAdapter()->SetAttribute(assetKey, assetValue, 1);
408 if (retCode != SUCCESS) {
409 ThrowNapiError(env, retCode, "Set asset go wrong.");
410 }
411 return nullptr;
412 }
413 } // namespace OHOS::CollaborationEdit
414