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 #define LOG_TAG "JsSchema"
16 #include "js_schema.h"
17 #include <nlohmann/json.hpp>
18 
19 #include "js_util.h"
20 #include "log_print.h"
21 #include "napi_queue.h"
22 #include "uv_queue.h"
23 
24 using namespace OHOS::DistributedKv;
25 using json = nlohmann::json;
26 
27 namespace OHOS::DistributedData {
28 static std::string LABEL = "Schema";
29 static std::string SCHEMA_VERSION = "SCHEMA_VERSION";
30 static std::string SCHEMA_MODE = "SCHEMA_MODE";
31 static std::string SCHEMA_DEFINE = "SCHEMA_DEFINE";
32 static std::string SCHEMA_INDEXES = "SCHEMA_INDEXES";
33 static std::string SCHEMA_SKIPSIZE = "SCHEMA_SKIPSIZE";
34 static std::string DEFAULT_SCHEMA_VERSION = "1.0";
35 
JsSchema(napi_env env)36 JsSchema::JsSchema(napi_env env)
37     : env_(env)
38 {
39 }
40 
~JsSchema()41 JsSchema::~JsSchema()
42 {
43     ZLOGD("no memory leak for JsSchema");
44     if (ref_ != nullptr) {
45         napi_delete_reference(env_, ref_);
46     }
47 }
48 
Constructor(napi_env env)49 napi_value JsSchema::Constructor(napi_env env)
50 {
51     ZLOGD("Init JsSchema");
52     const napi_property_descriptor properties[] = {
53         DECLARE_NAPI_GETTER_SETTER("root", JsSchema::GetRootNode, JsSchema::SetRootNode),
54         DECLARE_NAPI_GETTER_SETTER("indexes", JsSchema::GetIndexes, JsSchema::SetIndexes),
55         DECLARE_NAPI_GETTER_SETTER("mode", JsSchema::GetMode, JsSchema::SetMode),
56         DECLARE_NAPI_GETTER_SETTER("skip", JsSchema::GetSkip, JsSchema::SetSkip)
57     };
58     size_t count = sizeof(properties) / sizeof(properties[0]);
59     return JSUtil::DefineClass(env, "Schema", properties, count, JsSchema::New);
60 }
61 
New(napi_env env,napi_callback_info info)62 napi_value JsSchema::New(napi_env env, napi_callback_info info)
63 {
64     ZLOGD("Schema::New");
65     auto ctxt = std::make_shared<ContextBase>();
66     ctxt->GetCbInfoSync(env, info);
67     NAPI_ASSERT(env, ctxt->status == napi_ok, "invalid arguments!");
68 
69     JsSchema* schema = new (std::nothrow) JsSchema(env);
70     NAPI_ASSERT(env, schema !=nullptr, "no memory for schema");
71 
72     auto finalize = [](napi_env env, void* data, void* hint) {
73         ZLOGD("Schema finalize.");
74         auto* schema = reinterpret_cast<JsSchema*>(data);
75         CHECK_RETURN_VOID(schema != nullptr, "schema is null!");
76         delete schema;
77     };
78     ASSERT_CALL(env, napi_wrap(env, ctxt->self, schema, finalize, nullptr, nullptr), schema);
79     return ctxt->self;
80 }
81 
ToJson(napi_env env,napi_value inner,JsSchema * & out)82 napi_status JsSchema::ToJson(napi_env env, napi_value inner, JsSchema*& out)
83 {
84     ZLOGD("Schema::ToJson");
85     return JSUtil::Unwrap(env, inner, reinterpret_cast<void**>(&out), JsSchema::Constructor(env));
86 }
87 
GetSchema(napi_env env,napi_callback_info info,std::shared_ptr<ContextBase> & ctxt)88 JsSchema* JsSchema::GetSchema(napi_env env, napi_callback_info info, std::shared_ptr<ContextBase>& ctxt)
89 {
90     ctxt->GetCbInfoSync(env, info);
91     NAPI_ASSERT(env, ctxt->status == napi_ok, "invalid arguments!");
92     return reinterpret_cast<JsSchema*>(ctxt->native);
93 }
94 
95 template <typename T>
GetContextValue(napi_env env,std::shared_ptr<ContextBase> & ctxt,T & value)96 napi_value JsSchema::GetContextValue(napi_env env, std::shared_ptr<ContextBase>& ctxt, T& value)
97 {
98     JSUtil::SetValue(env, value, ctxt->output);
99     return ctxt->output;
100 }
101 
GetRootNode(napi_env env,napi_callback_info info)102 napi_value JsSchema::GetRootNode(napi_env env, napi_callback_info info)
103 {
104     ZLOGD("Schema::GetRootNode");
105     auto ctxt = std::make_shared<ContextBase>();
106     auto schema = GetSchema(env, info, ctxt);
107     CHECK_RETURN(schema != nullptr, "schema is nullptr!", nullptr);
108     if (schema->rootNode_ == nullptr) {
109         int argc = 1;
110         napi_value argv[1] = { nullptr };
111         std::string root(SCHEMA_DEFINE);
112         JSUtil::SetValue(env, root, argv[0]);
113         schema->ref_ = JSUtil::NewWithRef(env, argc, argv,
114             reinterpret_cast<void**>(&schema->rootNode_), JsFieldNode::Constructor(env));
115     }
116     NAPI_ASSERT(env, schema->ref_ != nullptr, "no root, please set first!");
117     NAPI_CALL(env, napi_get_reference_value(env, schema->ref_, &ctxt->output));
118     return ctxt->output;
119 }
120 
SetRootNode(napi_env env,napi_callback_info info)121 napi_value JsSchema::SetRootNode(napi_env env, napi_callback_info info)
122 {
123     ZLOGD("Schema::SetRootNode");
124     auto ctxt = std::make_shared<ContextBase>();
125     auto input = [env, ctxt](size_t argc, napi_value* argv) {
126         // required 2 arguments :: <root-node>
127         CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
128         JsFieldNode* node = nullptr;
129         ctxt->status = JSUtil::Unwrap(env, argv[0], reinterpret_cast<void**>(&node), JsFieldNode::Constructor(env));
130         CHECK_STATUS_RETURN_VOID(ctxt, "napi_unwrap to FieldNode failed");
131         CHECK_ARGS_RETURN_VOID(ctxt, node != nullptr, "invalid arg[0], i.e. invalid node!");
132 
133         auto schema = reinterpret_cast<JsSchema*>(ctxt->native);
134         if (schema->ref_ != nullptr) {
135             napi_delete_reference(env, schema->ref_);
136         }
137         ctxt->status = napi_create_reference(env, argv[0], 1, &schema->ref_);
138         CHECK_STATUS_RETURN_VOID(ctxt, "napi_create_reference to FieldNode failed");
139         schema->rootNode_ = node;
140     };
141     ctxt->GetCbInfoSync(env, info, input);
142     NAPI_ASSERT(env, ctxt->status == napi_ok, "invalid arguments!");
143     return ctxt->self;
144 }
145 
GetMode(napi_env env,napi_callback_info info)146 napi_value JsSchema::GetMode(napi_env env, napi_callback_info info)
147 {
148     ZLOGD("Schema::GetMode");
149     auto ctxt = std::make_shared<ContextBase>();
150     auto schema = GetSchema(env, info, ctxt);
151     CHECK_RETURN(schema != nullptr, "schema is nullptr!", nullptr);
152     return GetContextValue(env, ctxt, schema->mode_);
153 }
154 
SetMode(napi_env env,napi_callback_info info)155 napi_value JsSchema::SetMode(napi_env env, napi_callback_info info)
156 {
157     auto ctxt = std::make_shared<ContextBase>();
158     uint32_t mode = false;
159     auto input = [env, ctxt, &mode](size_t argc, napi_value* argv) {
160         // required 1 arguments :: <mode>
161         CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
162         ctxt->status = JSUtil::GetValue(env, argv[0], mode);
163         CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid mode!");
164     };
165     ctxt->GetCbInfoSync(env, info, input);
166     NAPI_ASSERT(env, ctxt->status == napi_ok, "invalid arguments!");
167 
168     auto schema = reinterpret_cast<JsSchema*>(ctxt->native);
169     schema->mode_ = mode;
170     return nullptr;
171 }
172 
GetSkip(napi_env env,napi_callback_info info)173 napi_value JsSchema::GetSkip(napi_env env, napi_callback_info info)
174 {
175     ZLOGD("Schema::GetSkip");
176     auto ctxt = std::make_shared<ContextBase>();
177     auto schema = GetSchema(env, info, ctxt);
178     CHECK_RETURN(schema != nullptr, "schema is nullptr!", nullptr);
179     return GetContextValue(env, ctxt, schema->skip_);
180 }
181 
SetSkip(napi_env env,napi_callback_info info)182 napi_value JsSchema::SetSkip(napi_env env, napi_callback_info info)
183 {
184     auto ctxt = std::make_shared<ContextBase>();
185     uint32_t skip = false;
186     auto input = [env, ctxt, &skip](size_t argc, napi_value* argv) {
187         // required 1 arguments :: <skip size>
188         CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
189         ctxt->status = JSUtil::GetValue(env, argv[0], skip);
190         CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid skip size!");
191     };
192     ctxt->GetCbInfoSync(env, info, input);
193     NAPI_ASSERT(env, ctxt->status == napi_ok, "invalid arguments!");
194 
195     auto schema = reinterpret_cast<JsSchema*>(ctxt->native);
196     schema->skip_ = skip;
197     return nullptr;
198 }
199 
GetIndexes(napi_env env,napi_callback_info info)200 napi_value JsSchema::GetIndexes(napi_env env, napi_callback_info info)
201 {
202     ZLOGD("Schema::GetIndexes");
203     auto ctxt = std::make_shared<ContextBase>();
204     auto schema = GetSchema(env, info, ctxt);
205     CHECK_RETURN(schema != nullptr, "schema is nullptr!", nullptr);
206     return GetContextValue(env, ctxt, schema->indexes_);
207 }
208 
SetIndexes(napi_env env,napi_callback_info info)209 napi_value JsSchema::SetIndexes(napi_env env, napi_callback_info info)
210 {
211     auto ctxt = std::make_shared<ContextBase>();
212     std::vector<std::string> indexes;
213     auto input = [env, ctxt, &indexes](size_t argc, napi_value* argv) {
214         // required 1 arguments :: <indexes>
215         CHECK_ARGS_RETURN_VOID(ctxt, argc == 1, "invalid arguments!");
216         ctxt->status = JSUtil::GetValue(env, argv[0], indexes, false);
217         CHECK_STATUS_RETURN_VOID(ctxt, "invalid arg[0], i.e. invalid indexes!");
218     };
219     ctxt->GetCbInfoSync(env, info, input);
220     NAPI_ASSERT(env, ctxt->status == napi_ok, "invalid arguments!");
221 
222     auto schema = reinterpret_cast<JsSchema*>(ctxt->native);
223     schema->indexes_ = indexes;
224     return nullptr;
225 }
226 
Dump()227 std::string JsSchema::Dump()
228 {
229     json jsIndexes = nlohmann::json::array();
230     for (auto idx : indexes_) {
231         jsIndexes.push_back(idx);
232     }
233     json js = {
234         { SCHEMA_VERSION, DEFAULT_SCHEMA_VERSION },
235         { SCHEMA_MODE, (mode_ == SCHEMA_MODE_STRICT) ? "STRICT" : "COMPATIBLE" },
236         { SCHEMA_DEFINE, rootNode_->GetValueForJson() },
237         { SCHEMA_INDEXES, jsIndexes },
238         { SCHEMA_SKIPSIZE, skip_ },
239     };
240     return js.dump();
241 }
242 } // namespace OHOS::DistributedData
243