1 /*
2  * Copyright (c) 2023 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 #define LOG_TAG "UnifiedDataChannelNapi"
16 #include "unified_data_channel_napi.h"
17 
18 #include "napi_data_utils.h"
19 #include "unified_data_napi.h"
20 
21 namespace OHOS {
22 namespace UDMF {
23 using namespace OHOS::AppExecFwk;
UnifiedDataChannelInit(napi_env env,napi_value exports)24 napi_value UnifiedDataChannelNapi::UnifiedDataChannelInit(napi_env env, napi_value exports)
25 {
26     LOG_DEBUG(UDMF_KITS_NAPI, "UnifiedDataChannelNapi");
27     napi_value intention = CreateIntention(env);
28     napi_property_descriptor desc[] = {
29         DECLARE_NAPI_PROPERTY("Intention", intention),
30         DECLARE_NAPI_FUNCTION("insertData", InsertData),
31         DECLARE_NAPI_FUNCTION("updateData", UpdateData),
32         DECLARE_NAPI_FUNCTION("queryData", QueryData),
33         DECLARE_NAPI_FUNCTION("deleteData", DeleteData),
34         DECLARE_NAPI_GETTER("ShareOptions", CreateShareOptions),
35         DECLARE_NAPI_FUNCTION("setAppShareOptions", SetAppShareOptions),
36         DECLARE_NAPI_FUNCTION("removeAppShareOptions", RemoveAppShareOptions),
37     };
38 
39     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
40     return exports;
41 }
42 
CreateIntention(napi_env env)43 napi_value UnifiedDataChannelNapi::CreateIntention(napi_env env)
44 {
45     napi_value intention = nullptr;
46     napi_create_object(env, &intention);
47     SetNamedProperty(env, intention, JS_UD_INTENTION_NAME_MAP.at(UD_INTENTION_DATA_HUB),
48         UD_INTENTION_MAP.at(UD_INTENTION_DATA_HUB));
49     SetNamedProperty(env, intention, JS_UD_INTENTION_NAME_MAP.at(UD_INTENTION_DRAG),
50         UD_SYSTEM_INTENTION_MAP.at(UD_INTENTION_DRAG));
51     napi_object_freeze(env, intention);
52     return intention;
53 }
54 
SetNamedProperty(napi_env env,napi_value & obj,const std::string & name,const std::string & value)55 napi_status UnifiedDataChannelNapi::SetNamedProperty(napi_env env, napi_value &obj, const std::string &name,
56     const std::string &value)
57 {
58     napi_value property = nullptr;
59     napi_status status = NapiDataUtils::SetValue(env, value, property);
60     ASSERT(status == napi_ok, "string to napi_value failed!", status);
61     status = napi_set_named_property(env, obj, name.c_str(), property);
62     ASSERT(status == napi_ok, "napi_set_named_property failed!", status);
63     return status;
64 }
65 
InsertData(napi_env env,napi_callback_info info)66 napi_value UnifiedDataChannelNapi::InsertData(napi_env env, napi_callback_info info)
67 {
68     LOG_DEBUG(UDMF_KITS_NAPI, "InsertData is called!");
69     struct InsertContext : public ContextBase {
70         std::string key;
71         Intention intention;
72         std::shared_ptr<UnifiedData> unifiedData;
73     };
74     std::string intention;
75     UnifiedDataNapi *unifiedDataNapi = nullptr;
76     auto ctxt = std::make_shared<InsertContext>();
77     auto input = [env, ctxt, &intention, &unifiedDataNapi](size_t argc, napi_value *argv) {
78         // require 2 arguments <options, unifiedData>
79         ASSERT_BUSINESS_ERR(ctxt, argc >= 2,
80             E_INVALID_PARAMETERS, "Parameter error: Mandatory parameters are left unspecified");
81         ctxt->status = GetNamedProperty(env, argv[0], "intention", intention);
82         ASSERT_BUSINESS_ERR(ctxt, UnifiedDataUtils::GetIntentionByString(intention) != UD_INTENTION_DRAG,
83             E_INVALID_PARAMETERS, "Parameter error: The intention parameter is invalid");
84         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok && UnifiedDataUtils::IsPersist(intention),
85             E_INVALID_PARAMETERS, "Parameter error: parameter options intention type must correspond to Intention");
86         ctxt->status = napi_unwrap(env, argv[1], reinterpret_cast<void **>(&unifiedDataNapi));
87         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, E_INVALID_PARAMETERS,
88             "Parameter error: parameter data type must be UnifiedData");
89     };
90     ctxt->GetCbInfo(env, info, input);
91     ASSERT_NULL(!ctxt->isThrowError, "Insert Exit");
92     ctxt->unifiedData = unifiedDataNapi->value_;
93     ctxt->intention = UnifiedDataUtils::GetIntentionByString(intention);
94     auto execute = [ctxt]() {
95         CustomOption option = { .intention = ctxt->intention };
96         auto status = UdmfClient::GetInstance().SetData(option, *(ctxt->unifiedData), ctxt->key);
97         ASSERT_WITH_ERRCODE(ctxt, status == E_OK, status, "InsertData failed!");
98     };
99 
100     auto output = [env, ctxt](napi_value &result) {
101         ctxt->status = NapiDataUtils::SetValue(env, ctxt->key, result);
102         ASSERT_WITH_ERRCODE(ctxt, ctxt->status == napi_ok, E_ERROR, "output failed!");
103     };
104     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
105 }
106 
UpdateData(napi_env env,napi_callback_info info)107 napi_value UnifiedDataChannelNapi::UpdateData(napi_env env, napi_callback_info info)
108 {
109     LOG_DEBUG(UDMF_KITS_NAPI, "UpdateData is called!");
110     struct UpdateContext : public ContextBase {
111         std::string key;
112         std::shared_ptr<UnifiedData> unifiedData;
113     };
114     std::string key;
115     UnifiedDataNapi *unifiedDataNapi = nullptr;
116     auto ctxt = std::make_shared<UpdateContext>();
117     auto input = [env, ctxt, &unifiedDataNapi](size_t argc, napi_value *argv) {
118         // require 2 arguments <options, unifiedData>
119         ASSERT_BUSINESS_ERR(ctxt, argc >= 2,
120             E_INVALID_PARAMETERS, "Parameter error: Mandatory parameters are left unspecified");
121         ctxt->status = GetNamedProperty(env, argv[0], "key", ctxt->key);
122         UnifiedKey key(ctxt->key);
123         ASSERT_BUSINESS_ERR(ctxt,
124             ctxt->status == napi_ok && key.IsValid() && UnifiedDataUtils::IsPersist(key.intention),
125             E_INVALID_PARAMETERS, "Parameter error: parameter options intention type must correspond to Intention");
126         ctxt->status = napi_unwrap(env, argv[1], reinterpret_cast<void **>(&unifiedDataNapi));
127         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, E_INVALID_PARAMETERS,
128             "Parameter error: parameter data type must be UnifiedData");
129     };
130     ctxt->GetCbInfo(env, info, input);
131     ASSERT_NULL(!ctxt->isThrowError, "Update Exit");
132     ctxt->unifiedData = unifiedDataNapi->value_;
133     auto execute = [ctxt]() {
134         QueryOption option = { .key = ctxt->key };
135         auto status = UdmfClient::GetInstance().UpdateData(option, *(ctxt->unifiedData));
136         ASSERT_WITH_ERRCODE(ctxt, status == E_OK, status, "UpdateData failed!");
137     };
138     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute);
139 }
140 
QueryData(napi_env env,napi_callback_info info)141 napi_value UnifiedDataChannelNapi::QueryData(napi_env env, napi_callback_info info)
142 {
143     LOG_DEBUG(UDMF_KITS_NAPI, "QueryData is called!");
144     struct QueryContext : public ContextBase {
145         std::string key;
146         Intention intention;
147         std::vector<UnifiedData> unifiedDataSet;
148     };
149     std::string intention;
150     auto ctxt = std::make_shared<QueryContext>();
151     auto input = [env, ctxt, &intention](size_t argc, napi_value *argv) {
152         // require 1 arguments <options>
153         ASSERT_BUSINESS_ERR(ctxt, argc >= 1,
154             E_INVALID_PARAMETERS, "Parameter error: Mandatory parameters are left unspecified");
155         napi_status keyStatus;
156         napi_status intentionStatus;
157         auto options = argv[0];
158         keyStatus = GetNamedProperty(env, options, "key", ctxt->key);
159         intentionStatus = GetNamedProperty(env, options, "intention", intention);
160         ASSERT_BUSINESS_ERR(ctxt, UnifiedDataUtils::GetIntentionByString(intention) != UD_INTENTION_DRAG,
161             E_INVALID_PARAMETERS, "Parameter error: The intention parameter is invalid");
162         ASSERT_BUSINESS_ERR(ctxt, (keyStatus == napi_ok || intentionStatus == napi_ok) &&
163             UnifiedDataUtils::IsValidOptions(ctxt->key, intention),
164             E_INVALID_PARAMETERS, "Parameter error: parameter options intention type must correspond to Intention");
165     };
166     ctxt->GetCbInfo(env, info, input);
167     ASSERT_NULL(!ctxt->isThrowError, "Query Exit");
168     ctxt->intention = UnifiedDataUtils::GetIntentionByString(intention);
169     auto execute = [env, ctxt]() {
170         QueryOption option = {
171             .key = ctxt->key,
172             .intention = ctxt->intention,
173         };
174         auto status = UdmfClient::GetInstance().GetBatchData(option, ctxt->unifiedDataSet);
175         LOG_DEBUG(UDMF_SERVICE, "GetBatchData : status =  %{public}d!", status);
176         ASSERT_WITH_ERRCODE(ctxt, status == E_OK, status, "QueryData failed!");
177     };
178     auto output = [env, ctxt](napi_value &result) {
179         ASSERT_WITH_ERRCODE(ctxt, !ctxt->unifiedDataSet.empty(), E_ERROR, "unifiedDataSet is empty!");
180         ctxt->status = ConvertUnifiedDataSetToNapi(env, ctxt->unifiedDataSet, result);
181         ASSERT_WITH_ERRCODE(ctxt, ctxt->status == napi_ok, E_ERROR, "ConvertUnifiedDataSetToNapi failed!");
182     };
183     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
184 }
185 
DeleteData(napi_env env,napi_callback_info info)186 napi_value UnifiedDataChannelNapi::DeleteData(napi_env env, napi_callback_info info)
187 {
188     LOG_DEBUG(UDMF_KITS_NAPI, "DeleteData is called!");
189     struct DeleteContext : public ContextBase {
190         std::string key;
191         Intention intention;
192         std::vector<UnifiedData> unifiedDataSet;
193     };
194     std::string intention;
195     auto ctxt = std::make_shared<DeleteContext>();
196     auto input = [env, ctxt, &intention](size_t argc, napi_value *argv) {
197         // require 1 arguments <options>
198         ASSERT_BUSINESS_ERR(ctxt, argc >= 1,
199             E_INVALID_PARAMETERS, "Parameter error: Mandatory parameters are left unspecified");
200         napi_status keyStatus;
201         napi_status intentionStatus;
202         napi_value options = argv[0];
203         keyStatus = GetNamedProperty(env, options, "key", ctxt->key);
204         intentionStatus = GetNamedProperty(env, options, "intention", intention);
205         ASSERT_BUSINESS_ERR(ctxt, intention.empty() ||
206             UnifiedDataUtils::GetIntentionByString(intention) == UD_INTENTION_DATA_HUB,
207             E_INVALID_PARAMETERS, "Parameter error: The intention parameter is invalid");
208         ASSERT_BUSINESS_ERR(ctxt,
209             (keyStatus == napi_ok || intentionStatus == napi_ok) &&
210                 UnifiedDataUtils::IsValidOptions(ctxt->key, intention),
211             E_INVALID_PARAMETERS, "Parameter error: parameter options intention type must correspond to Intention");
212     };
213     ctxt->GetCbInfo(env, info, input);
214     ASSERT_NULL(!ctxt->isThrowError, "Delete Exit");
215     ctxt->intention = UnifiedDataUtils::GetIntentionByString(intention);
216     auto execute = [env, ctxt]() {
217         QueryOption option = {
218             .key = ctxt->key,
219             .intention = ctxt->intention,
220         };
221         auto status = UdmfClient::GetInstance().DeleteData(option, ctxt->unifiedDataSet);
222         ASSERT_WITH_ERRCODE(ctxt, status == E_OK, status, "DeleteData failed!");
223     };
224 
225     auto output = [env, ctxt](napi_value &result) {
226         ASSERT_WITH_ERRCODE(ctxt, !ctxt->unifiedDataSet.empty(), E_ERROR, "unifiedDataSet is empty!");
227         ctxt->status = ConvertUnifiedDataSetToNapi(env, ctxt->unifiedDataSet, result);
228         ASSERT_WITH_ERRCODE(ctxt, ctxt->status == napi_ok, E_ERROR, "ConvertUnifiedDataSetToNapi failed!");
229     };
230     return NapiQueue::AsyncWork(env, ctxt, std::string(__FUNCTION__), execute, output);
231 }
232 
GetNamedProperty(napi_env env,napi_value & obj,const std::string & key,std::string & value)233 napi_status UnifiedDataChannelNapi::GetNamedProperty(napi_env env, napi_value &obj, const std::string &key,
234     std::string &value)
235 {
236     bool hasKey = false;
237     napi_status status = napi_has_named_property(env, obj, key.c_str(), &hasKey);
238     if (status != napi_ok) {
239         LOG_ERROR(UDMF_KITS_NAPI, "napi_has_named_property failed, name = %{public}s", key.c_str());
240         return napi_generic_failure;
241     }
242     if (!hasKey) {
243         LOG_ERROR(UDMF_KITS_NAPI, "The property name is non-existent, name: %{public}s", key.c_str());
244         return napi_generic_failure;
245     }
246     napi_value napiValue = nullptr;
247     status = napi_get_named_property(env, obj, key.c_str(), &napiValue);
248     if (status != napi_ok || napiValue == nullptr) {
249         LOG_ERROR(UDMF_KITS_NAPI, "napi_get_named_property failed, name = %{public}s", key.c_str());
250         return napi_generic_failure;
251     }
252     if (NapiDataUtils::IsNull(env, napiValue)) {
253         LOG_ERROR(UDMF_KITS_NAPI, "The property value is null, name = %{public}s", key.c_str());
254         return napi_generic_failure;
255     }
256     status = NapiDataUtils::GetValue(env, napiValue, value);
257     if (status != napi_ok) {
258         LOG_ERROR(UDMF_KITS_NAPI, "NapiDataUtils::GetValue failed, name = %{public}s", key.c_str());
259         return status;
260     }
261     LOG_DEBUG(UDMF_KITS_NAPI, "Param parse successful, Options.%{public}s = %{public}s", key.c_str(), value.c_str());
262     return status;
263 }
264 
CreateShareOptions(napi_env env,napi_callback_info info)265 napi_value UnifiedDataChannelNapi::CreateShareOptions(napi_env env, napi_callback_info info)
266 {
267     napi_value jsShareOptions = nullptr;
268     napi_create_object(env, &jsShareOptions);
269 
270     napi_value jsInApp;
271     NapiDataUtils::SetValue(env, static_cast<int32_t>(ShareOptions::IN_APP), jsInApp);
272     NAPI_CALL(env, napi_set_named_property(env, jsShareOptions, "IN_APP", jsInApp));
273 
274     napi_value jsCrossDevice;
275     NapiDataUtils::SetValue(env, static_cast<int32_t>(ShareOptions::CROSS_APP), jsCrossDevice);
276     NAPI_CALL(env, napi_set_named_property(env, jsShareOptions, "CROSS_APP", jsCrossDevice));
277     return jsShareOptions;
278 }
279 
SetAppShareOptions(napi_env env,napi_callback_info info)280 napi_value UnifiedDataChannelNapi::SetAppShareOptions(napi_env env, napi_callback_info info)
281 {
282     LOG_DEBUG(UDMF_KITS_NAPI, "SetAppShareOption is called!");
283     std::string intention;
284     int32_t shareOptionValue = ShareOptions::CROSS_APP;
285     auto ctxt = std::make_shared<ContextBase>();
286     auto input = [env, ctxt, &intention, &shareOptionValue](size_t argc, napi_value* argv) {
287         LOG_DEBUG(UDMF_KITS_NAPI, "set appShareOption, argc = %{public}zu !", argc);
288         // required 2 arguments : intention, shareOption
289         ASSERT_BUSINESS_ERR(ctxt, argc > 1,
290             Status::E_INVALID_PARAMETERS, "Parameter error: Mandatory parameters are left unspecified");
291         ctxt->status = NapiDataUtils::GetValue(env, argv[0], intention);
292         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, E_INVALID_PARAMETERS,
293             "Parameter error:The parameter intention must be within the scope of the Intention enumeration.");
294         ctxt->status = NapiDataUtils::GetValue(env, argv[1], shareOptionValue);
295         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, E_INVALID_PARAMETERS,
296             "Parameter error:The parameter shareOption must be within the scope of the ShareOptions enumeration.");
297     };
298     ctxt->GetCbInfoSync(env, info, input);
299     ASSERT_NULL(!ctxt->isThrowError, "SetAppShareOption Exit");
300     auto status = E_OK;
301     ASSERT_ERR(ctxt->env, intention == "Drag",
302                E_INVALID_PARAMETERS, "Parameter error: The intention parameter is invalid!");
303     ASSERT_ERR(ctxt->env, (shareOptionValue >= IN_APP && shareOptionValue < SHARE_OPTIONS_BUTT),
304                E_INVALID_PARAMETERS, "Parameter error: The shareOptions parameter is invalid!");
305     std::transform(intention.begin(), intention.end(), intention.begin(), ::tolower); // js : Drag --> drag
306     status = UdmfClient::GetInstance().SetAppShareOption(intention, static_cast<ShareOptions>(shareOptionValue));
307     ASSERT_BUSINESS_ERR_VOID(ctxt, !(status == E_SETTINGS_EXISTED), E_SETTINGS_EXISTED, "Settings already exist!");
308     ASSERT_BUSINESS_ERR_VOID(ctxt, status != E_NO_PERMISSION, E_NO_PERMISSION, "Permission denied!");
309     ASSERT_ERR(ctxt->env, status == E_OK, status, "invalid arguments!");
310     return nullptr;
311 }
312 
RemoveAppShareOptions(napi_env env,napi_callback_info info)313 napi_value UnifiedDataChannelNapi::RemoveAppShareOptions(napi_env env, napi_callback_info info)
314 {
315     LOG_DEBUG(UDMF_KITS_NAPI, "RemoveAppShareOption is called!");
316     std::string intention;
317     auto ctxt = std::make_shared<ContextBase>();
318     auto input = [env, ctxt, &intention](size_t argc, napi_value* argv) {
319         LOG_DEBUG(UDMF_KITS_NAPI, "RemoveAppShareOption, argc = %{public}zu !", argc);
320         // required 2 arguments : typeId
321         ASSERT_BUSINESS_ERR(ctxt, argc > 0,
322             Status::E_INVALID_PARAMETERS, "Parameter error: Mandatory parameters are left unspecified");
323         ctxt->status = NapiDataUtils::GetValue(env, argv[0], intention);
324         ASSERT_BUSINESS_ERR(ctxt, ctxt->status == napi_ok, E_INVALID_PARAMETERS,
325             "Parameter error:The parameter intention must be within the scope of the Intention enumeration.");
326     };
327     ctxt->GetCbInfoSync(env, info, input);
328     ASSERT_NULL(!ctxt->isThrowError, "RemoveAppShareOption Exit");
329     ASSERT_ERR(ctxt->env, intention == "Drag",
330                E_INVALID_PARAMETERS, "Parameter error: The intention parameter is invalid!");
331     std::transform(intention.begin(), intention.end(), intention.begin(), ::tolower); // js : Drag --> drag
332     auto status = E_OK;
333     status = UdmfClient::GetInstance().RemoveAppShareOption(intention);
334     ASSERT_BUSINESS_ERR_VOID(ctxt, status != E_NO_PERMISSION, E_NO_PERMISSION, "Permission denied!");
335     ASSERT_ERR(ctxt->env, status == E_OK, status, "invalid arguments!");
336     return nullptr;
337 }
338 
ConvertUnifiedDataSetToNapi(napi_env env,const std::vector<UnifiedData> & dataSet,napi_value & output)339 napi_status UnifiedDataChannelNapi::ConvertUnifiedDataSetToNapi(
340     napi_env env, const std::vector<UnifiedData> &dataSet, napi_value &output)
341 {
342     auto status = napi_create_array_with_length(env, dataSet.size(), &output);
343     if (status != napi_ok) {
344         LOG_ERROR(UDMF_KITS_NAPI, "napi_create_array_with_length failed!");
345         return napi_generic_failure;
346     }
347     int index = 0;
348     for (const UnifiedData &data : dataSet) {
349         std::shared_ptr<UnifiedData> unifiedData = std::make_shared<UnifiedData>();
350         unifiedData->SetRecords(data.GetRecords());
351         napi_value dataNapi = nullptr;
352         UnifiedDataNapi::NewInstance(env, unifiedData, dataNapi);
353         status = napi_set_element(env, output, index++, dataNapi);
354         if (status != napi_ok) {
355             LOG_ERROR(UDMF_KITS_NAPI, "napi_set_element failed!");
356             return napi_generic_failure;
357         }
358     }
359     return napi_ok;
360 }
361 } // namespace UDMF
362 } // namespace OHOS