• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "NapiParser"
17 #include "napi_parser.h"
18 
19 #include <charconv>
20 #include <iostream>
21 #include <sstream>
22 
23 #include <nlohmann/json.hpp>
24 
25 #include "napi_errno.h"
26 #include "napi_error_utils.h"
27 #include "napi_node.h"
28 #include "napi_utils.h"
29 
30 namespace OHOS::CollaborationEdit {
31 
32 static constexpr const uint8_t NUMBER_OF_FIELDS_IN_ID = 2;
33 
34 static std::vector<std::string> g_cloudDbFields = {
35     "batchInsert",
36     "query",
37     "downloadAsset",
38     "uploadAsset",
39     "deleteAsset",
40     "deleteLocalAsset"
41 };
42 
Stringsplit(std::string str,const char split,std::vector<std::string> & res)43 void Parser::Stringsplit(std::string str, const char split, std::vector<std::string> &res)
44 {
45     std::istringstream iss(str);
46     std::string token;
47     while (std::getline(iss, token, split)) {
48         res.push_back(token);
49     }
50 }
51 
ConvertToUint64(std::string str,uint64_t & out)52 bool Parser::ConvertToUint64(std::string str, uint64_t &out)
53 {
54     auto [ptr, errCode] = std::from_chars(str.data(), str.data() + str.size(), out);
55     return errCode == std::errc{} && ptr == str.data() + str.size();
56 }
57 
ConvertStrToID(std::string idStr)58 std::optional<ID> Parser::ConvertStrToID(std::string idStr)
59 {
60     if (idStr.empty()) {
61         return std::nullopt;
62     }
63     std::vector<std::string> strList;
64     Stringsplit(idStr, '_', strList);
65     if (strList.size() != NUMBER_OF_FIELDS_IN_ID) {  // id returned from kernel is a string like 'id_clock'
66         return std::nullopt;
67     }
68     uint64_t clock = 0;
69     if (!ConvertToUint64(strList[1], clock)) {
70         return std::nullopt;
71     }
72     ID id(strList[0], clock);
73     return id;
74 }
75 
ParseJsonToJsNode(napi_env env,json jsonObj,AbstractType * parent,napi_value & out)76 int ParseJsonToJsNode(napi_env env, json jsonObj, AbstractType *parent, napi_value &out)
77 {
78     napi_value constructor = Node::Constructor(env);
79     ASSERT(constructor != nullptr, "node constructor is null", ERR);
80     std::string nodeName = jsonObj["nodeName"];
81     std::string idStr = jsonObj["elementId"];
82     std::optional<ID> id = Parser::ConvertStrToID(idStr);
83     ASSERT(id.has_value(), "incorrect id.", ERR); // ID should not be null
84     napi_value jsNodeName = nullptr;
85     napi_status status = NapiUtils::SetValue(env, nodeName, jsNodeName);
86     ASSERT(status == napi_ok, "wrap nodeName go wrong.", ERR);
87     size_t argc = 1;
88     napi_value argv[1] = {jsNodeName};
89     napi_value jsNode;
90     status = napi_new_instance(env, constructor, argc, argv, &jsNode);
91     ASSERT(status == napi_ok, "new node instance go wrong.", ERR);
92     Node *tempNode = nullptr;
93     status = napi_unwrap(env, jsNode, reinterpret_cast<void **>(&tempNode));
94     ASSERT(status == napi_ok, "unwrap jsNode go wrong.", ERR);
95     tempNode->SetDBStore(parent->GetDBStore());
96     tempNode->SetTableName(parent->GetTableName());
97     tempNode->SetID(id);
98     out = jsNode;
99     return OK;
100 }
101 
ParseJsonToJsText(napi_env env,json jsonObj,AbstractType * parent,napi_value & out)102 int ParseJsonToJsText(napi_env env, json jsonObj, AbstractType *parent, napi_value &out)
103 {
104     napi_value constructor = Text::Constructor(env);
105     ASSERT(constructor != nullptr, "text constructor is null", ERR);
106     std::string idStr = jsonObj["elementId"];
107     LOG_DEBUG("id is %{public}s", idStr.c_str());
108     std::optional<ID> id = Parser::ConvertStrToID(idStr);
109     ASSERT(id.has_value(), "incorrect id.", ERR); // ID should not be null
110     napi_value jsText;
111     napi_status status = napi_new_instance(env, constructor, 0, nullptr, &jsText);
112     ASSERT(status == napi_ok, "new text instance go wrong.", ERR);
113     Node *tempText = nullptr;
114     status = napi_unwrap(env, jsText, reinterpret_cast<void **>(&tempText));
115     ASSERT(status == napi_ok, "unwrap jsText go wrong.", ERR);
116     tempText->SetDBStore(parent->GetDBStore());
117     tempText->SetTableName(parent->GetTableName());
118     tempText->SetID(id);
119     out = jsText;
120     return OK;
121 }
122 
ParseJsonStrToJsChildren(napi_env env,const std::string & nodeJsonStr,AbstractType * parent,napi_value & out)123 int Parser::ParseJsonStrToJsChildren(
124     napi_env env, const std::string &nodeJsonStr, AbstractType *parent, napi_value &out)
125 {
126     ASSERT(!nodeJsonStr.empty() && json::accept(nodeJsonStr), "invalid json str", ERR);
127     napi_status status = napi_create_array(env, &out);
128     ASSERT(status == napi_ok, "create array go wrong!", ERR);
129     json jsonArray = json::parse(nodeJsonStr);
130     ASSERT(jsonArray.is_array(), "result is not json array.", ERR);
131     int i = 0;
132     for (const auto &jsonObj : jsonArray) {
133         if (!jsonObj.contains("type")) {
134             continue;
135         }
136         std::string type = jsonObj["type"];
137         napi_value jsNode;
138         if (type.compare("XML_ELEMENT") == 0) {
139             int ret = ParseJsonToJsNode(env, jsonObj, parent, jsNode);
140             ASSERT(ret == OK, "Parse json to node go wrong.", ERR);
141         } else if (type.compare("XML_TEXT") == 0) {
142             int ret = ParseJsonToJsText(env, jsonObj, parent, jsNode);
143             ASSERT(ret == OK, "Parse json to text go wrong.", ERR);
144         } else {
145             LOG_ERROR("Unsupported type. type = %{public}s", type.c_str());
146             continue;
147         }
148         status = napi_set_element(env, out, i, jsNode);
149         ASSERT(status == napi_ok, "set element go wrong.", ERR);
150         i++;
151     }
152     return OK;
153 }
154 
ParseJsonStrToJsUpdateNode(napi_env env,std::string nodeJsonStr,std::shared_ptr<DBStore> dbStore,napi_value & out)155 int Parser::ParseJsonStrToJsUpdateNode(
156     napi_env env, std::string nodeJsonStr, std::shared_ptr<DBStore> dbStore, napi_value &out)
157 {
158     ASSERT(!nodeJsonStr.empty() && json::accept(nodeJsonStr), "invalid json str", ERR);
159     napi_status status = napi_create_array(env, &out);
160     ASSERT(status == napi_ok, "create array go wrong!", ERR);
161     json jsonArray = json::parse(nodeJsonStr);
162     ASSERT(jsonArray.is_array(), "result is not json array.", ERR);
163     int i = 0;
164     for (const auto &jsonObj : jsonArray) {
165         if (!jsonObj.contains("type") || !jsonObj.contains("name")) {
166             continue;
167         }
168         std::string type = jsonObj["type"];
169         std::string tableName = NapiUtils::RemovePrefix(jsonObj["name"], std::to_string(LABEL_FRAGMENT) + "_");
170         AbstractType parent;
171         parent.SetDBStore(dbStore);
172         parent.SetTableName(tableName);
173 
174         napi_value jsNode;
175         if (type.compare("XML_ELEMENT") == 0) {
176             int ret = ParseJsonToJsNode(env, jsonObj, &parent, jsNode);
177             ASSERT(ret == OK, "Parse json to node go wrong.", ERR);
178         } else if (type.compare("XML_TEXT") == 0) {
179             int ret = ParseJsonToJsText(env, jsonObj, &parent, jsNode);
180             ASSERT(ret == OK, "Parse json to text go wrong.", ERR);
181         } else {
182             LOG_ERROR("Unsupported type. type = %{public}s", type.c_str());
183             continue;
184         }
185 
186         napi_value jsUpdateNode = nullptr;
187         status = napi_create_object(env, &jsUpdateNode);
188         ASSERT(status == napi_ok, "create object go wrong!", ERR);
189         napi_value jstableName;
190         NapiUtils::SetValue(env, tableName, jstableName);
191         status = napi_set_named_property(env, jsUpdateNode, "editUnitName", jstableName);
192         ASSERT(status == napi_ok, "set editUnitName go wrong.", ERR);
193         status = napi_set_named_property(env, jsUpdateNode, "node", jsNode);
194         ASSERT(status == napi_ok, "set node go wrong.", ERR);
195         status = napi_set_element(env, out, i, jsUpdateNode);
196         ASSERT(status == napi_ok, "set element go wrong.", ERR);
197         i++;
198     }
199     return OK;
200 }
201 
ParseFromAttrsJsonStr(napi_env env,const std::string & jsonStr,napi_value & out)202 int Parser::ParseFromAttrsJsonStr(napi_env env, const std::string &jsonStr, napi_value &out)
203 {
204     ASSERT(!jsonStr.empty() && json::accept(jsonStr), "invalid json str", ERR);
205     napi_status status = napi_create_object(env, &out);
206     ASSERT(status == napi_ok, "create object go wrong!", ERR);
207     json jsonObject = json::parse(jsonStr);
208     napi_value jsAttr = nullptr;
209     for (json::iterator iter = jsonObject.begin(); iter != jsonObject.end(); ++iter) {
210         std::string key = iter.key();
211         json value = iter.value();
212         if (value.is_string()) {
213             NapiUtils::SetValue(env, value.get<std::string>(), jsAttr);
214         } else if (value.is_number_integer()) {
215             NapiUtils::SetValue(env, value.get<int>(), jsAttr);
216         } else if (value.is_boolean()) {
217             NapiUtils::SetValue(env, value.get<bool>(), jsAttr);
218         } else {
219             LOG_ERROR("Unsupported value type");
220             continue;
221         }
222         status = napi_set_named_property(env, out, key.c_str(), jsAttr);
223     }
224     return OK;
225 }
226 
ParseJsFormatToStr(napi_env env,napi_value jsFormat,std::string & out)227 int Parser::ParseJsFormatToStr(napi_env env, napi_value jsFormat, std::string &out)
228 {
229     napi_value keys = nullptr;
230     napi_get_all_property_names(env, jsFormat, napi_key_own_only,
231         static_cast<napi_key_filter>(napi_key_enumerable | napi_key_skip_symbols), napi_key_numbers_to_strings, &keys);
232     uint32_t arrLen = 0;
233     napi_status status = napi_get_array_length(env, keys, &arrLen);
234     ASSERT(status == napi_ok, "get keys array length go wrong.", ERR);
235     json resJson;
236     for (size_t i = 0; i < arrLen; i++) {
237         napi_value key = nullptr;
238         status = napi_get_element(env, keys, i, &key);
239         ASSERT(status == napi_ok, "get key element go wrong.", ERR);
240         std::string keyStr;
241         NapiUtils::GetValue(env, key, keyStr);
242         napi_value jsValue = nullptr;
243         napi_get_property(env, jsFormat, key, &jsValue);
244         std::string value;
245         int ret = ParseVariantJsValueToStr(env, jsValue, value);
246         ASSERT(ret == OK, "parse value go wrong.", ERR);
247         resJson[keyStr] = value;
248     }
249     out = resJson.dump();
250     return OK;
251 }
252 
ParseVariantJsValueToStr(napi_env env,napi_value input,std::string & out)253 int Parser::ParseVariantJsValueToStr(napi_env env, napi_value input, std::string &out)
254 {
255     napi_valuetype valueType;
256     napi_status status = napi_typeof(env, input, &valueType);
257     ASSERT(status == napi_ok, "get type of input go wrong.", ERR);
258     ASSERT(valueType != napi_undefined, "cannot be undefined.", ERR);
259     switch (valueType) {
260         case napi_number: {
261             int64_t intValue;
262             status = NapiUtils::GetValue(env, input, intValue);
263             if (status == napi_ok) {
264                 out = std::to_string(intValue);
265                 break;
266             }
267             double doubleValue;
268             status = NapiUtils::GetValue(env, input, doubleValue);
269             ASSERT(status == napi_ok, "Neither int or double", ERR);
270             out = std::to_string(doubleValue);
271             break;
272         }
273         case napi_string: {
274             std::string strValue;
275             status = NapiUtils::GetValue(env, input, strValue);
276             ASSERT(status == napi_ok, "convert to str go wrong.", ERR);
277             out = strValue;
278             break;
279         }
280         case napi_boolean: {
281             bool bValue;
282             status = NapiUtils::GetValue(env, input, bValue);
283             ASSERT(status == napi_ok, "convert to bool go wrong.", ERR);
284             out = bValue ? "true" : "false";
285             break;
286         }
287         default: {
288             LOG_ERROR("unsupported format type: %{public}d", valueType);
289             return ERR;
290         }
291     }
292     return OK;
293 }
294 
CheckValueType(napi_env env,napi_value value)295 int Parser::CheckValueType(napi_env env, napi_value value)
296 {
297     napi_valuetype valueType;
298     napi_status status = napi_typeof(env, value, &valueType);
299     if (status != napi_ok) {
300         LOG_ERROR("type of args go wrong, status = %{public}d", status);
301         return ERR;
302     }
303     if (valueType != napi_function) {
304         LOG_ERROR("value type go wrong: %{public}d", valueType);
305         return ERR;
306     }
307     return OK;
308 }
309 
ParseCloudDbFields(napi_env env,napi_value input,std::vector<napi_value> & cloudDbFuncVector)310 int Parser::ParseCloudDbFields(napi_env env, napi_value input, std::vector<napi_value> &cloudDbFuncVector)
311 {
312     for (auto field : g_cloudDbFields) {
313         napi_value cloudDbFunc = nullptr;
314         napi_status status = NapiUtils::GetNamedProperty(env, input, field.c_str(), cloudDbFunc);
315         if (status != napi_ok) {
316             LOG_ERROR("get func go wrong, status = %{public}d", status);
317             return ERR;
318         }
319         int ret = CheckValueType(env, cloudDbFunc);
320         if (ret != OK) {
321             LOG_ERROR("check func type go wrong, status = %{public}d", ret);
322             return ret;
323         }
324         cloudDbFuncVector.push_back(cloudDbFunc);
325     }
326     return OK;
327 }
328 
GetUniqueIdFromJsonStr(napi_env env,json & root)329 napi_value Parser::GetUniqueIdFromJsonStr(napi_env env, json &root)
330 {
331     ASSERT(root.contains("client"), "parse client from json str go wrong", nullptr);
332     ASSERT(root.contains("clock"), "parse clock from json str go wrong", nullptr);
333 
334     napi_value uniqueId = nullptr;
335     NAPI_CALL(env, napi_create_object(env, &uniqueId));
336     napi_value jsDeviceId = nullptr;
337     std::string client = root["client"];
338     NapiUtils::SetValue(env, client, jsDeviceId);
339     NAPI_CALL(env, napi_set_named_property(env, uniqueId, "id", jsDeviceId));
340     napi_value jsClock = nullptr;
341     int64_t clock = root["clock"];
342     NapiUtils::SetValue(env, clock, jsClock);
343     NAPI_CALL(env, napi_set_named_property(env, uniqueId, "clock", jsClock));
344 
345     return uniqueId;
346 }
347 
SetRelativePosType(napi_env env,json & root,napi_value & relativePos)348 int Parser::SetRelativePosType(napi_env env, json &root, napi_value &relativePos)
349 {
350     if (!root.contains("type")) {
351         return OK;
352     }
353     napi_value jsType = Parser::GetUniqueIdFromJsonStr(env, root["type"]);
354     if (jsType == nullptr) {
355         return ERR;
356     }
357     napi_status status = napi_set_named_property(env, relativePos, "parentId", jsType);
358     return status == napi_ok ? OK : ERR;
359 }
360 
SetRelativePosItem(napi_env env,json & root,napi_value & relativePos)361 int Parser::SetRelativePosItem(napi_env env, json &root, napi_value &relativePos)
362 {
363     if (!root.contains("item")) {
364         return OK;
365     }
366     napi_value jsItem = Parser::GetUniqueIdFromJsonStr(env, root["item"]);
367     if (jsItem == nullptr) {
368         return ERR;
369     }
370     napi_status status = napi_set_named_property(env, relativePos, "id", jsItem);
371     return status == napi_ok ? OK : ERR;
372 }
373 
SetRelativePosTname(napi_env env,json & root,napi_value & relativePos)374 int Parser::SetRelativePosTname(napi_env env, json &root, napi_value &relativePos)
375 {
376     if (!root.contains("tname")) {
377         return OK;
378     }
379     std::string tnameStrTmp = root["tname"];
380     auto tnameStr = NapiUtils::RemovePrefix(tnameStrTmp, std::to_string(LABEL_FRAGMENT) + "_");
381     napi_value tname = nullptr;
382     NapiUtils::SetValue(env, tnameStr, tname);
383     napi_status status = napi_set_named_property(env, relativePos, "parentName", tname);
384     return status == napi_ok ? OK : ERR;
385 }
386 
SetRelativePosAssoc(napi_env env,json & root,napi_value & relativePos)387 int Parser::SetRelativePosAssoc(napi_env env, json &root, napi_value &relativePos)
388 {
389     if (!root.contains("assoc")) {
390         return OK;
391     }
392     napi_value assoc;
393     int64_t assoc_num = root["assoc"];
394     NapiUtils::SetValue(env, assoc_num, assoc);
395     napi_status status = napi_set_named_property(env, relativePos, "pos", assoc);
396     return status == napi_ok ? OK : ERR;
397 }
398 
GetRelativePosFromJsonStr(napi_env env,std::string & relPos)399 napi_value Parser::GetRelativePosFromJsonStr(napi_env env, std::string &relPos)
400 {
401     ASSERT_THROW(env, json::accept(relPos), Status::INTERNAL_ERROR, "parse relpos str go wrong");
402     json root = json::parse(relPos);
403 
404     napi_value relativePos;
405     NAPI_CALL(env, napi_create_object(env, &relativePos));
406     Parser::SetRelativePosType(env, root, relativePos);
407     Parser::SetRelativePosItem(env, root, relativePos);
408     Parser::SetRelativePosTname(env, root, relativePos);
409     Parser::SetRelativePosAssoc(env, root, relativePos);
410     return relativePos;
411 }
412 
GetUniqueIdFromNapiValueToJsonStr(napi_env env,napi_value type,json & typeJson)413 void Parser::GetUniqueIdFromNapiValueToJsonStr(napi_env env, napi_value type, json &typeJson)
414 {
415     std::string id;
416     int64_t clock;
417     napi_status status = NapiUtils::GetNamedProperty(env, type, "id", id);
418     ASSERT_THROW_VOID(env, status == napi_ok, Status::INVALID_ARGUMENT, "read id param from type go wrong");
419     status = NapiUtils::GetNamedProperty(env, type, "clock", clock);
420     ASSERT_THROW_VOID(env, status == napi_ok, Status::INVALID_ARGUMENT, "read clock param from type go wrong");
421     typeJson["client"] = id;
422     typeJson["clock"] = clock;
423 }
424 
ParseFromAssetOpConfig(napi_env env,const AssetOpConfig & config)425 napi_value Parser::ParseFromAssetOpConfig(napi_env env, const AssetOpConfig &config)
426 {
427     napi_value jsConfig = nullptr;
428     NAPI_CALL(env, napi_create_object(env, &jsConfig));
429     napi_value jsPath = nullptr;
430     NapiUtils::SetValue(env, config.inputPath, jsPath);
431     NAPI_CALL(env, napi_set_named_property(env, jsConfig, "path", jsPath));
432     return jsConfig;
433 }
434 
435 } // namespace OHOS::CollaborationEdit
436