1use wasm_bindgen::prelude::*;
2use serde::{Serialize, Deserialize};
3use std::collections::HashMap;
4use gloo_utils::format::JsValueSerdeExt;
5
6#[wasm_bindgen]
8extern "C" {
9 #[wasm_bindgen(js_namespace = entityOps, js_name = createEntity)]
11 fn js_create_entity(entity_name: &str, entity_data: JsValue) -> JsValue;
12
13 #[wasm_bindgen(js_namespace = entityOps, js_name = updateEntity)]
15 fn js_update_entity(entity_name: &str, entity_data: JsValue) -> JsValue;
16
17 #[wasm_bindgen(js_namespace = entityOps, js_name = deleteEntity)]
19 fn js_delete_entity(entity_name: &str, entity_data: JsValue) -> JsValue;
20
21 #[wasm_bindgen(js_namespace = entityOps, js_name = findOne)]
23 fn js_find_one(entity_name: &str, query: JsValue) -> JsValue;
24
25 #[wasm_bindgen(js_namespace = entityOps, js_name = findMany)]
27 fn js_find_many(entity_name: &str, query: JsValue) -> JsValue;
28}
29
30#[derive(Serialize, Deserialize)]
32pub struct ServiceParameters {
33 #[serde(flatten)]
35 params: HashMap<String, serde_json::Value>,
36}
37
38#[wasm_bindgen(js_name = "callService")]
40pub fn call_service(name: &str, parameters: JsValue) -> Result<JsValue, JsValue> {
41 if name.trim().is_empty() {
43 return Err(JsValue::from_str("Service name cannot be empty"));
44 }
45
46 let params: ServiceParameters = parameters.into_serde()
48 .map_err(|e| JsValue::from_str(&format!("Failed to parse parameters: {}", e)))?;
49
50 let mut result = HashMap::new();
54 result.insert("service".to_string(), name.to_string());
55 result.insert("status".to_string(), "success".to_string());
56 result.insert("parameters".to_string(), format!("{:?}", params.params));
57
58 JsValue::from_serde(&result)
60 .map_err(|e| JsValue::from_str(&format!("Failed to serialize result: {}", e)))
61}
62
63#[wasm_bindgen(js_name = "createEntity")]
67pub fn create_entity(entity_name: &str, entity_data: JsValue) -> Result<JsValue, JsValue> {
68 let result = js_create_entity(entity_name, entity_data);
70
71 Ok(result)
73}
74
75#[wasm_bindgen(js_name = "updateEntity")]
77pub fn update_entity(entity_name: &str, entity_data: JsValue) -> Result<JsValue, JsValue> {
78 let result = js_update_entity(entity_name, entity_data);
80
81 Ok(result)
83}
84
85#[wasm_bindgen(js_name = "deleteEntity")]
87pub fn delete_entity(entity_name: &str, entity_data: JsValue) -> Result<JsValue, JsValue> {
88 let result = js_delete_entity(entity_name, entity_data);
90
91 Ok(result)
93}
94
95#[wasm_bindgen(js_name = "findOne")]
97pub fn find_one(entity_name: &str, query: JsValue) -> Result<JsValue, JsValue> {
98 let result = js_find_one(entity_name, query);
100
101 Ok(result)
103}
104
105#[wasm_bindgen(js_name = "findMany")]
107pub fn find_many(entity_name: &str, query: JsValue) -> Result<JsValue, JsValue> {
108 let result = js_find_many(entity_name, query);
110
111 Ok(result)
113}
114
115#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
119pub struct ColumnInfo {
120 pub name: String,
122 pub data_type: String,
124 pub nullable: bool,
126 pub primary_key: bool,
128 pub default_value: Option<String>,
130 pub description: Option<String>,
132}
133
134#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
136pub struct TableInfo {
137 pub name: String,
139 pub description: Option<String>,
141 pub columns: Vec<ColumnInfo>,
143 pub primary_key: Vec<String>,
145 pub foreign_keys: Vec<ForeignKeyInfo>,
147 pub indexes: Vec<IndexInfo>,
149}
150
151#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
153pub struct ForeignKeyInfo {
154 pub name: Option<String>,
156 pub column_names: Vec<String>,
158 pub referenced_table: String,
160 pub referenced_columns: Vec<String>,
162 pub on_update: Option<String>,
164 pub on_delete: Option<String>,
166}
167
168#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
170pub struct IndexInfo {
171 pub name: String,
173 pub column_names: Vec<String>,
175 pub unique: bool,
177}
178
179#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
181pub struct DatabaseSchema {
182 pub tables: Vec<TableInfo>,
184}
185
186#[wasm_bindgen(js_name = "exportDatabaseSchema")]
205pub fn export_database_schema() -> Result<JsValue, JsValue> {
206 let schema = create_mock_schema();
209
210 JsValue::from_serde(&schema)
212 .map_err(|e| JsValue::from_str(&format!("Failed to serialize schema: {}", e)))
213}
214
215fn create_mock_schema() -> DatabaseSchema {
217 let user_account_columns = vec![
219 ColumnInfo {
220 name: "userId".to_string(),
221 data_type: "TEXT".to_string(),
222 nullable: false,
223 primary_key: true,
224 default_value: None,
225 description: Some("Unique identifier for the user".to_string()),
226 },
227 ColumnInfo {
228 name: "username".to_string(),
229 data_type: "TEXT".to_string(),
230 nullable: false,
231 primary_key: false,
232 default_value: None,
233 description: Some("The username used along with the password to login".to_string()),
234 },
235 ColumnInfo {
236 name: "userFullName".to_string(),
237 data_type: "TEXT".to_string(),
238 nullable: true,
239 primary_key: false,
240 default_value: None,
241 description: Some("User's first, middle, last, etc name".to_string()),
242 },
243 ColumnInfo {
244 name: "currentPassword".to_string(),
245 data_type: "TEXT".to_string(),
246 nullable: true,
247 primary_key: false,
248 default_value: None,
249 description: Some("NOTE: not an encrypted field because one way hash encryption used for it".to_string()),
250 },
251 ColumnInfo {
252 name: "resetPassword".to_string(),
253 data_type: "TEXT".to_string(),
254 nullable: true,
255 primary_key: false,
256 default_value: None,
257 description: Some("Set to random password for password reset, can be used only to update password".to_string()),
258 },
259 ColumnInfo {
260 name: "passwordSalt".to_string(),
261 data_type: "TEXT".to_string(),
262 nullable: true,
263 primary_key: false,
264 default_value: None,
265 description: None,
266 },
267 ColumnInfo {
268 name: "passwordHashType".to_string(),
269 data_type: "TEXT".to_string(),
270 nullable: true,
271 primary_key: false,
272 default_value: None,
273 description: None,
274 },
275 ColumnInfo {
276 name: "passwordBase64".to_string(),
277 data_type: "TEXT".to_string(),
278 nullable: true,
279 primary_key: false,
280 default_value: None,
281 description: Some("Set to Y is currentPassword Base64 encoded, defaults to Hex encoded".to_string()),
282 },
283 ColumnInfo {
284 name: "passwordSetDate".to_string(),
285 data_type: "DATETIME".to_string(),
286 nullable: true,
287 primary_key: false,
288 default_value: None,
289 description: None,
290 },
291 ColumnInfo {
292 name: "passwordHint".to_string(),
293 data_type: "TEXT".to_string(),
294 nullable: true,
295 primary_key: false,
296 default_value: None,
297 description: None,
298 },
299 ColumnInfo {
300 name: "publicKey".to_string(),
301 data_type: "TEXT".to_string(),
302 nullable: true,
303 primary_key: false,
304 default_value: None,
305 description: Some("RSA public key for key based authentication".to_string()),
306 },
307 ColumnInfo {
308 name: "hasLoggedOut".to_string(),
309 data_type: "TEXT".to_string(),
310 nullable: true,
311 primary_key: false,
312 default_value: None,
313 description: Some("Set to Y when user logs out and to N when user logs in. If user is session authenticated on request and this is Y then treat as if user not authenticated.".to_string()),
314 },
315 ColumnInfo {
316 name: "disabled".to_string(),
317 data_type: "TEXT".to_string(),
318 nullable: false,
319 primary_key: false,
320 default_value: Some("'N'".to_string()),
321 description: None,
322 },
323 ColumnInfo {
324 name: "disabledDateTime".to_string(),
325 data_type: "DATETIME".to_string(),
326 nullable: true,
327 primary_key: false,
328 default_value: None,
329 description: None,
330 },
331 ColumnInfo {
332 name: "terminateDate".to_string(),
333 data_type: "DATETIME".to_string(),
334 nullable: true,
335 primary_key: false,
336 default_value: None,
337 description: Some("If set then user may not login after this date, and no notifications will be sent after this date.".to_string()),
338 },
339 ColumnInfo {
340 name: "successiveFailedLogins".to_string(),
341 data_type: "INTEGER".to_string(),
342 nullable: true,
343 primary_key: false,
344 default_value: None,
345 description: None,
346 },
347 ColumnInfo {
348 name: "requirePasswordChange".to_string(),
349 data_type: "TEXT".to_string(),
350 nullable: true,
351 primary_key: false,
352 default_value: None,
353 description: None,
354 },
355 ColumnInfo {
356 name: "currencyUomId".to_string(),
357 data_type: "TEXT".to_string(),
358 nullable: true,
359 primary_key: false,
360 default_value: None,
361 description: None,
362 },
363 ColumnInfo {
364 name: "locale".to_string(),
365 data_type: "TEXT".to_string(),
366 nullable: true,
367 primary_key: false,
368 default_value: None,
369 description: None,
370 },
371 ColumnInfo {
372 name: "timeZone".to_string(),
373 data_type: "TEXT".to_string(),
374 nullable: true,
375 primary_key: false,
376 default_value: None,
377 description: None,
378 },
379 ColumnInfo {
380 name: "externalUserId".to_string(),
381 data_type: "TEXT".to_string(),
382 nullable: true,
383 primary_key: false,
384 default_value: None,
385 description: None,
386 },
387 ColumnInfo {
388 name: "emailAddress".to_string(),
389 data_type: "TEXT".to_string(),
390 nullable: true,
391 primary_key: false,
392 default_value: None,
393 description: Some("The email address to use for forgot password emails and other system messages.".to_string()),
394 },
395 ColumnInfo {
396 name: "ipAllowed".to_string(),
397 data_type: "TEXT".to_string(),
398 nullable: true,
399 primary_key: false,
400 default_value: None,
401 description: Some("If specified only allow login from matching IP4 address. Comma separated patterns where each pattern has 4 dot separated segments each segment may be number, '*' for wildcard, or '-' separate number range (like '0-31').".to_string()),
402 },
403 ];
404
405 let user_account_table = TableInfo {
406 name: "UserAccount".to_string(),
407 description: Some("User account information from moqui.security package".to_string()),
408 columns: user_account_columns,
409 primary_key: vec!["userId".to_string()],
410 foreign_keys: Vec::new(),
411 indexes: vec![
412 IndexInfo {
413 name: "USERACCT_USERNAME".to_string(),
414 column_names: vec!["username".to_string()],
415 unique: true,
416 },
417 IndexInfo {
418 name: "USERACCT_EMAILADDR".to_string(),
419 column_names: vec!["emailAddress".to_string()],
420 unique: true,
421 },
422 ],
423 };
424
425 DatabaseSchema {
426 tables: vec![user_account_table],
427 }
428}