1pub mod auth;
12#[cfg(feature = "cli")]
13pub mod cli;
14pub mod convert;
15pub mod export;
16#[cfg(feature = "git")]
17pub mod git;
18pub mod import;
19pub mod model;
20pub mod models;
21pub mod storage;
22pub mod validation;
23pub mod workspace;
24
25#[cfg(feature = "api-backend")]
27pub use storage::api::ApiStorageBackend;
28#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
29pub use storage::browser::BrowserStorageBackend;
30#[cfg(feature = "native-fs")]
31pub use storage::filesystem::FileSystemStorageBackend;
32pub use storage::{StorageBackend, StorageError};
33
34pub use convert::{ConversionError, convert_to_odcs};
35#[cfg(feature = "png-export")]
36pub use export::PNGExporter;
37pub use export::{
38 AvroExporter, ExportError, ExportResult, JSONSchemaExporter, ODCSExporter, ProtobufExporter,
39 SQLExporter,
40};
41pub use import::{
42 AvroImporter, ImportError, ImportResult, JSONSchemaImporter, ODCSImporter, ProtobufImporter,
43 SQLImporter,
44};
45#[cfg(feature = "api-backend")]
46pub use model::ApiModelLoader;
47pub use model::{ModelLoader, ModelSaver};
48pub use validation::{
49 RelationshipValidationError, RelationshipValidationResult, TableValidationError,
50 TableValidationResult,
51};
52
53pub use models::enums::*;
55pub use models::{Column, ContactDetails, DataModel, ForeignKey, Relationship, SlaProperty, Table};
56
57pub use auth::{
59 AuthMode, AuthState, GitHubEmail, InitiateOAuthRequest, InitiateOAuthResponse,
60 SelectEmailRequest,
61};
62
63pub use workspace::{
65 CreateWorkspaceRequest, CreateWorkspaceResponse, ListProfilesResponse, LoadProfileRequest,
66 ProfileInfo, WorkspaceInfo,
67};
68
69#[cfg(feature = "git")]
71pub use git::{GitError, GitService, GitStatus};
72
73#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
75mod wasm {
76 use crate::export::ExportError;
77 use crate::import::{ImportError, ImportResult};
78 use crate::models::DataModel;
79 use js_sys;
80 use serde_json;
81 use serde_yaml;
82 use uuid;
83 use wasm_bindgen::prelude::*;
84 use wasm_bindgen_futures;
85
86 fn import_error_to_js(err: ImportError) -> JsValue {
88 JsValue::from_str(&err.to_string())
89 }
90
91 fn export_error_to_js(err: ExportError) -> JsValue {
93 JsValue::from_str(&err.to_string())
94 }
95
96 fn serialize_import_result(result: &ImportResult) -> Result<String, JsValue> {
98 serde_json::to_string(result)
99 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
100 }
101
102 fn deserialize_workspace(json: &str) -> Result<DataModel, JsValue> {
104 serde_json::from_str(json)
105 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))
106 }
107
108 #[wasm_bindgen]
118 pub fn parse_odcs_yaml(yaml_content: &str) -> Result<String, JsValue> {
119 let mut importer = crate::import::ODCSImporter::new();
120 match importer.import(yaml_content) {
121 Ok(result) => serialize_import_result(&result),
122 Err(err) => Err(import_error_to_js(err)),
123 }
124 }
125
126 #[wasm_bindgen]
136 pub fn export_to_odcs_yaml(workspace_json: &str) -> Result<String, JsValue> {
137 let model = deserialize_workspace(workspace_json)?;
138
139 let exports = crate::export::ODCSExporter::export_model(&model, None, "odcs_v3_1_0");
141
142 let yaml_docs: Vec<String> = exports.values().cloned().collect();
144 Ok(yaml_docs.join("\n---\n"))
145 }
146
147 #[wasm_bindgen]
158 pub fn import_from_sql(sql_content: &str, dialect: &str) -> Result<String, JsValue> {
159 let importer = crate::import::SQLImporter::new(dialect);
160 match importer.parse(sql_content) {
161 Ok(result) => serialize_import_result(&result),
162 Err(err) => Err(JsValue::from_str(&format!("Parse error: {}", err))),
163 }
164 }
165
166 #[wasm_bindgen]
176 pub fn import_from_avro(avro_content: &str) -> Result<String, JsValue> {
177 let importer = crate::import::AvroImporter::new();
178 match importer.import(avro_content) {
179 Ok(result) => serialize_import_result(&result),
180 Err(err) => Err(import_error_to_js(err)),
181 }
182 }
183
184 #[wasm_bindgen]
194 pub fn import_from_json_schema(json_schema_content: &str) -> Result<String, JsValue> {
195 let importer = crate::import::JSONSchemaImporter::new();
196 match importer.import(json_schema_content) {
197 Ok(result) => serialize_import_result(&result),
198 Err(err) => Err(import_error_to_js(err)),
199 }
200 }
201
202 #[wasm_bindgen]
212 pub fn import_from_protobuf(protobuf_content: &str) -> Result<String, JsValue> {
213 let importer = crate::import::ProtobufImporter::new();
214 match importer.import(protobuf_content) {
215 Ok(result) => serialize_import_result(&result),
216 Err(err) => Err(import_error_to_js(err)),
217 }
218 }
219
220 #[wasm_bindgen]
231 pub fn export_to_sql(workspace_json: &str, dialect: &str) -> Result<String, JsValue> {
232 let model = deserialize_workspace(workspace_json)?;
233 let exporter = crate::export::SQLExporter;
234 match exporter.export(&model.tables, Some(dialect)) {
235 Ok(result) => Ok(result.content),
236 Err(err) => Err(export_error_to_js(err)),
237 }
238 }
239
240 #[wasm_bindgen]
250 pub fn export_to_avro(workspace_json: &str) -> Result<String, JsValue> {
251 let model = deserialize_workspace(workspace_json)?;
252 let exporter = crate::export::AvroExporter;
253 match exporter.export(&model.tables) {
254 Ok(result) => Ok(result.content),
255 Err(err) => Err(export_error_to_js(err)),
256 }
257 }
258
259 #[wasm_bindgen]
269 pub fn export_to_json_schema(workspace_json: &str) -> Result<String, JsValue> {
270 let model = deserialize_workspace(workspace_json)?;
271 let exporter = crate::export::JSONSchemaExporter;
272 match exporter.export(&model.tables) {
273 Ok(result) => Ok(result.content),
274 Err(err) => Err(export_error_to_js(err)),
275 }
276 }
277
278 #[wasm_bindgen]
288 pub fn export_to_protobuf(workspace_json: &str) -> Result<String, JsValue> {
289 let model = deserialize_workspace(workspace_json)?;
290 let exporter = crate::export::ProtobufExporter;
291 match exporter.export(&model.tables) {
292 Ok(result) => Ok(result.content),
293 Err(err) => Err(export_error_to_js(err)),
294 }
295 }
296
297 #[wasm_bindgen]
307 pub fn import_from_cads(yaml_content: &str) -> Result<String, JsValue> {
308 let importer = crate::import::CADSImporter::new();
309 match importer.import(yaml_content) {
310 Ok(asset) => serde_json::to_string(&asset)
311 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
312 Err(err) => Err(import_error_to_js(err)),
313 }
314 }
315
316 #[wasm_bindgen]
326 pub fn export_to_cads(asset_json: &str) -> Result<String, JsValue> {
327 let asset: crate::models::cads::CADSAsset = serde_json::from_str(asset_json)
328 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
329 let exporter = crate::export::CADSExporter;
330 match exporter.export(&asset) {
331 Ok(yaml) => Ok(yaml),
332 Err(err) => Err(export_error_to_js(err)),
333 }
334 }
335
336 #[wasm_bindgen]
346 pub fn import_from_odps(yaml_content: &str) -> Result<String, JsValue> {
347 let importer = crate::import::ODPSImporter::new();
348 match importer.import(yaml_content) {
349 Ok(product) => serde_json::to_string(&product)
350 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
351 Err(err) => Err(import_error_to_js(err)),
352 }
353 }
354
355 #[wasm_bindgen]
365 pub fn export_to_odps(product_json: &str) -> Result<String, JsValue> {
366 let product: crate::models::odps::ODPSDataProduct = serde_json::from_str(product_json)
367 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
368 let exporter = crate::export::ODPSExporter;
369 match exporter.export(&product) {
370 Ok(yaml) => Ok(yaml),
371 Err(err) => Err(export_error_to_js(err)),
372 }
373 }
374
375 #[cfg(feature = "odps-validation")]
385 #[wasm_bindgen]
386 pub fn validate_odps(yaml_content: &str) -> Result<(), JsValue> {
387 #[cfg(feature = "cli")]
388 {
389 use crate::cli::validation::validate_odps_internal;
390 validate_odps_internal(yaml_content).map_err(|e| JsValue::from_str(&e.to_string()))
391 }
392 #[cfg(not(feature = "cli"))]
393 {
394 use jsonschema::Validator;
396 use serde_json::Value;
397
398 let schema_content = include_str!("../schemas/odps-json-schema-latest.json");
399 let schema: Value = serde_json::from_str(schema_content)
400 .map_err(|e| JsValue::from_str(&format!("Failed to load ODPS schema: {}", e)))?;
401
402 let validator = Validator::new(&schema)
403 .map_err(|e| JsValue::from_str(&format!("Failed to compile ODPS schema: {}", e)))?;
404
405 let data: Value = serde_yaml::from_str(yaml_content)
406 .map_err(|e| JsValue::from_str(&format!("Failed to parse YAML: {}", e)))?;
407
408 if let Err(error) = validator.validate(&data) {
409 return Err(JsValue::from_str(&format!(
410 "ODPS validation failed: {}",
411 error
412 )));
413 }
414
415 Ok(())
416 }
417 }
418
419 #[cfg(not(feature = "odps-validation"))]
420 #[wasm_bindgen]
421 pub fn validate_odps(_yaml_content: &str) -> Result<(), JsValue> {
422 Ok(())
425 }
426
427 #[wasm_bindgen]
437 pub fn create_domain(name: &str) -> Result<String, JsValue> {
438 let domain = crate::models::domain::Domain::new(name.to_string());
439 serde_json::to_string(&domain)
440 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
441 }
442
443 #[wasm_bindgen]
453 pub fn import_from_domain(yaml_content: &str) -> Result<String, JsValue> {
454 match crate::models::domain::Domain::from_yaml(yaml_content) {
455 Ok(domain) => serde_json::to_string(&domain)
456 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
457 Err(e) => Err(JsValue::from_str(&format!("Parse error: {}", e))),
458 }
459 }
460
461 #[wasm_bindgen]
471 pub fn export_to_domain(domain_json: &str) -> Result<String, JsValue> {
472 let domain: crate::models::domain::Domain = serde_json::from_str(domain_json)
473 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
474 domain
475 .to_yaml()
476 .map_err(|e| JsValue::from_str(&format!("YAML serialization error: {}", e)))
477 }
478
479 #[wasm_bindgen]
490 pub fn migrate_dataflow_to_domain(
491 dataflow_yaml: &str,
492 domain_name: Option<String>,
493 ) -> Result<String, JsValue> {
494 match crate::convert::migrate_dataflow::migrate_dataflow_to_domain(
495 dataflow_yaml,
496 domain_name.as_deref(),
497 ) {
498 Ok(domain) => serde_json::to_string(&domain)
499 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
500 Err(e) => Err(JsValue::from_str(&format!("Migration error: {}", e))),
501 }
502 }
503
504 #[wasm_bindgen]
514 pub fn parse_tag(tag_str: &str) -> Result<String, JsValue> {
515 use crate::models::Tag;
516 use std::str::FromStr;
517 match Tag::from_str(tag_str) {
518 Ok(tag) => serde_json::to_string(&tag)
519 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
520 Err(_) => Err(JsValue::from_str("Invalid tag format")),
521 }
522 }
523
524 #[wasm_bindgen]
534 pub fn serialize_tag(tag_json: &str) -> Result<String, JsValue> {
535 use crate::models::Tag;
536 let tag: Tag = serde_json::from_str(tag_json)
537 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
538 Ok(tag.to_string())
539 }
540
541 #[wasm_bindgen]
553 pub fn convert_to_odcs(input: &str, format: Option<String>) -> Result<String, JsValue> {
554 match crate::convert::convert_to_odcs(input, format.as_deref()) {
555 Ok(yaml) => Ok(yaml),
556 Err(e) => Err(JsValue::from_str(&format!("Conversion error: {}", e))),
557 }
558 }
559
560 #[wasm_bindgen]
571 pub fn filter_nodes_by_owner(workspace_json: &str, owner: &str) -> Result<String, JsValue> {
572 let model = deserialize_workspace(workspace_json)?;
573 let filtered = model.filter_nodes_by_owner(owner);
574 serde_json::to_string(&filtered)
575 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
576 }
577
578 #[wasm_bindgen]
589 pub fn filter_relationships_by_owner(
590 workspace_json: &str,
591 owner: &str,
592 ) -> Result<String, JsValue> {
593 let model = deserialize_workspace(workspace_json)?;
594 let filtered = model.filter_relationships_by_owner(owner);
595 serde_json::to_string(&filtered)
596 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
597 }
598
599 #[wasm_bindgen]
610 pub fn filter_nodes_by_infrastructure_type(
611 workspace_json: &str,
612 infrastructure_type: &str,
613 ) -> Result<String, JsValue> {
614 let model = deserialize_workspace(workspace_json)?;
615 let infra_type: crate::models::enums::InfrastructureType =
616 serde_json::from_str(&format!("\"{}\"", infrastructure_type)).map_err(|e| {
617 JsValue::from_str(&format!(
618 "Invalid infrastructure type '{}': {}",
619 infrastructure_type, e
620 ))
621 })?;
622 let filtered = model.filter_nodes_by_infrastructure_type(infra_type);
623 serde_json::to_string(&filtered)
624 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
625 }
626
627 #[wasm_bindgen]
638 pub fn filter_relationships_by_infrastructure_type(
639 workspace_json: &str,
640 infrastructure_type: &str,
641 ) -> Result<String, JsValue> {
642 let model = deserialize_workspace(workspace_json)?;
643 let infra_type: crate::models::enums::InfrastructureType =
644 serde_json::from_str(&format!("\"{}\"", infrastructure_type)).map_err(|e| {
645 JsValue::from_str(&format!(
646 "Invalid infrastructure type '{}': {}",
647 infrastructure_type, e
648 ))
649 })?;
650 let filtered = model.filter_relationships_by_infrastructure_type(infra_type);
651 serde_json::to_string(&filtered)
652 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
653 }
654
655 #[wasm_bindgen]
666 pub fn filter_by_tags(workspace_json: &str, tag: &str) -> Result<String, JsValue> {
667 let model = deserialize_workspace(workspace_json)?;
668 let (nodes, relationships) = model.filter_by_tags(tag);
669 let result = serde_json::json!({
670 "nodes": nodes,
671 "relationships": relationships
672 });
673 serde_json::to_string(&result)
674 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
675 }
676
677 #[wasm_bindgen]
693 pub fn add_system_to_domain(
694 workspace_json: &str,
695 domain_id: &str,
696 system_json: &str,
697 ) -> Result<String, JsValue> {
698 let mut model = deserialize_workspace(workspace_json)?;
699 let domain_uuid = uuid::Uuid::parse_str(domain_id)
700 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
701 let system: crate::models::domain::System = serde_json::from_str(system_json)
702 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
703 model
704 .add_system_to_domain(domain_uuid, system)
705 .map_err(|e| JsValue::from_str(&e))?;
706 serde_json::to_string(&model)
707 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
708 }
709
710 #[wasm_bindgen]
722 pub fn add_cads_node_to_domain(
723 workspace_json: &str,
724 domain_id: &str,
725 node_json: &str,
726 ) -> Result<String, JsValue> {
727 let mut model = deserialize_workspace(workspace_json)?;
728 let domain_uuid = uuid::Uuid::parse_str(domain_id)
729 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
730 let node: crate::models::domain::CADSNode = serde_json::from_str(node_json)
731 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
732 model
733 .add_cads_node_to_domain(domain_uuid, node)
734 .map_err(|e| JsValue::from_str(&e))?;
735 serde_json::to_string(&model)
736 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
737 }
738
739 #[wasm_bindgen]
751 pub fn add_odcs_node_to_domain(
752 workspace_json: &str,
753 domain_id: &str,
754 node_json: &str,
755 ) -> Result<String, JsValue> {
756 let mut model = deserialize_workspace(workspace_json)?;
757 let domain_uuid = uuid::Uuid::parse_str(domain_id)
758 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
759 let node: crate::models::domain::ODCSNode = serde_json::from_str(node_json)
760 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
761 model
762 .add_odcs_node_to_domain(domain_uuid, node)
763 .map_err(|e| JsValue::from_str(&e))?;
764 serde_json::to_string(&model)
765 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
766 }
767
768 #[wasm_bindgen]
782 pub fn validate_table_name(name: &str) -> Result<String, JsValue> {
783 match crate::validation::input::validate_table_name(name) {
784 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
785 Err(err) => {
786 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
787 }
788 }
789 }
790
791 #[wasm_bindgen]
801 pub fn validate_column_name(name: &str) -> Result<String, JsValue> {
802 match crate::validation::input::validate_column_name(name) {
803 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
804 Err(err) => {
805 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
806 }
807 }
808 }
809
810 #[wasm_bindgen]
820 pub fn validate_uuid(id: &str) -> Result<String, JsValue> {
821 match crate::validation::input::validate_uuid(id) {
822 Ok(uuid) => {
823 Ok(serde_json::json!({"valid": true, "uuid": uuid.to_string()}).to_string())
824 }
825 Err(err) => {
826 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
827 }
828 }
829 }
830
831 #[wasm_bindgen]
841 pub fn validate_data_type(data_type: &str) -> Result<String, JsValue> {
842 match crate::validation::input::validate_data_type(data_type) {
843 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
844 Err(err) => {
845 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
846 }
847 }
848 }
849
850 #[wasm_bindgen]
860 pub fn validate_description(desc: &str) -> Result<String, JsValue> {
861 match crate::validation::input::validate_description(desc) {
862 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
863 Err(err) => {
864 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
865 }
866 }
867 }
868
869 #[wasm_bindgen]
880 pub fn sanitize_sql_identifier(name: &str, dialect: &str) -> String {
881 crate::validation::input::sanitize_sql_identifier(name, dialect)
882 }
883
884 #[wasm_bindgen]
894 pub fn sanitize_description(desc: &str) -> String {
895 crate::validation::input::sanitize_description(desc)
896 }
897
898 #[wasm_bindgen]
909 pub fn detect_naming_conflicts(
910 existing_tables_json: &str,
911 new_tables_json: &str,
912 ) -> Result<String, JsValue> {
913 let existing_tables: Vec<crate::models::Table> = serde_json::from_str(existing_tables_json)
914 .map_err(|e| JsValue::from_str(&format!("Failed to parse existing tables: {}", e)))?;
915 let new_tables: Vec<crate::models::Table> = serde_json::from_str(new_tables_json)
916 .map_err(|e| JsValue::from_str(&format!("Failed to parse new tables: {}", e)))?;
917
918 let validator = crate::validation::tables::TableValidator::new();
919 let conflicts = validator.detect_naming_conflicts(&existing_tables, &new_tables);
920
921 serde_json::to_string(&conflicts)
922 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
923 }
924
925 #[wasm_bindgen]
935 pub fn validate_pattern_exclusivity(table_json: &str) -> Result<String, JsValue> {
936 let table: crate::models::Table = serde_json::from_str(table_json)
937 .map_err(|e| JsValue::from_str(&format!("Failed to parse table: {}", e)))?;
938
939 let validator = crate::validation::tables::TableValidator::new();
940 match validator.validate_pattern_exclusivity(&table) {
941 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
942 Err(violation) => {
943 Ok(serde_json::json!({"valid": false, "violation": violation}).to_string())
944 }
945 }
946 }
947
948 #[wasm_bindgen]
960 pub fn check_circular_dependency(
961 relationships_json: &str,
962 source_table_id: &str,
963 target_table_id: &str,
964 ) -> Result<String, JsValue> {
965 let relationships: Vec<crate::models::Relationship> =
966 serde_json::from_str(relationships_json)
967 .map_err(|e| JsValue::from_str(&format!("Failed to parse relationships: {}", e)))?;
968
969 let source_id = uuid::Uuid::parse_str(source_table_id)
970 .map_err(|e| JsValue::from_str(&format!("Invalid source_table_id: {}", e)))?;
971 let target_id = uuid::Uuid::parse_str(target_table_id)
972 .map_err(|e| JsValue::from_str(&format!("Invalid target_table_id: {}", e)))?;
973
974 let validator = crate::validation::relationships::RelationshipValidator::new();
975 match validator.check_circular_dependency(&relationships, source_id, target_id) {
976 Ok((has_cycle, cycle_path)) => {
977 let cycle_path_strs: Vec<String> = cycle_path
978 .map(|path| path.iter().map(|id| id.to_string()).collect())
979 .unwrap_or_default();
980 Ok(serde_json::json!({
981 "has_cycle": has_cycle,
982 "cycle_path": cycle_path_strs
983 })
984 .to_string())
985 }
986 Err(err) => Err(JsValue::from_str(&format!("Validation error: {}", err))),
987 }
988 }
989
990 #[wasm_bindgen]
1001 pub fn validate_no_self_reference(
1002 source_table_id: &str,
1003 target_table_id: &str,
1004 ) -> Result<String, JsValue> {
1005 let source_id = uuid::Uuid::parse_str(source_table_id)
1006 .map_err(|e| JsValue::from_str(&format!("Invalid source_table_id: {}", e)))?;
1007 let target_id = uuid::Uuid::parse_str(target_table_id)
1008 .map_err(|e| JsValue::from_str(&format!("Invalid target_table_id: {}", e)))?;
1009
1010 let validator = crate::validation::relationships::RelationshipValidator::new();
1011 match validator.validate_no_self_reference(source_id, target_id) {
1012 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
1013 Err(self_ref) => {
1014 Ok(serde_json::json!({"valid": false, "self_reference": self_ref}).to_string())
1015 }
1016 }
1017 }
1018
1019 #[cfg(feature = "png-export")]
1035 #[wasm_bindgen]
1036 pub fn export_to_png(workspace_json: &str, width: u32, height: u32) -> Result<String, JsValue> {
1037 let model = deserialize_workspace(workspace_json)?;
1038 let exporter = crate::export::PNGExporter::new();
1039 match exporter.export(&model.tables, width, height) {
1040 Ok(result) => Ok(result.content), Err(err) => Err(export_error_to_js(err)),
1042 }
1043 }
1044
1045 #[wasm_bindgen]
1061 pub fn load_model(db_name: &str, store_name: &str, workspace_path: &str) -> js_sys::Promise {
1062 let db_name = db_name.to_string();
1063 let store_name = store_name.to_string();
1064 let workspace_path = workspace_path.to_string();
1065
1066 wasm_bindgen_futures::future_to_promise(async move {
1067 let storage = crate::storage::browser::BrowserStorageBackend::new(db_name, store_name);
1068 let loader = crate::model::ModelLoader::new(storage);
1069 match loader.load_model(&workspace_path).await {
1070 Ok(result) => serde_json::to_string(&result)
1071 .map(|s| JsValue::from_str(&s))
1072 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1073 Err(err) => Err(JsValue::from_str(&format!("Storage error: {}", err))),
1074 }
1075 })
1076 }
1077
1078 #[wasm_bindgen]
1091 pub fn save_model(
1092 db_name: &str,
1093 store_name: &str,
1094 workspace_path: &str,
1095 model_json: &str,
1096 ) -> js_sys::Promise {
1097 let db_name = db_name.to_string();
1098 let store_name = store_name.to_string();
1099 let workspace_path = workspace_path.to_string();
1100 let model_json = model_json.to_string();
1101
1102 wasm_bindgen_futures::future_to_promise(async move {
1103 let model: crate::models::DataModel = serde_json::from_str(&model_json)
1104 .map_err(|e| JsValue::from_str(&format!("Failed to parse model: {}", e)))?;
1105
1106 let storage = crate::storage::browser::BrowserStorageBackend::new(db_name, store_name);
1107 let saver = crate::model::ModelSaver::new(storage);
1108
1109 for table in &model.tables {
1112 let yaml = crate::export::ODCSExporter::export_table(table, "odcs_v3_1_0");
1114 let table_data = crate::model::saver::TableData {
1115 id: table.id,
1116 name: table.name.clone(),
1117 yaml_file_path: Some(format!("tables/{}.yaml", table.name)),
1118 yaml_value: serde_yaml::from_str(&yaml)
1119 .map_err(|e| JsValue::from_str(&format!("Failed to parse YAML: {}", e)))?,
1120 };
1121 saver
1122 .save_table(&workspace_path, &table_data)
1123 .await
1124 .map_err(|e| JsValue::from_str(&format!("Failed to save table: {}", e)))?;
1125 }
1126
1127 if !model.relationships.is_empty() {
1129 let rel_data: Vec<crate::model::saver::RelationshipData> = model
1130 .relationships
1131 .iter()
1132 .map(|rel| {
1133 let yaml_value = serde_json::json!({
1134 "id": rel.id.to_string(),
1135 "source_table_id": rel.source_table_id.to_string(),
1136 "target_table_id": rel.target_table_id.to_string(),
1137 });
1138 let yaml_str = serde_json::to_string(&yaml_value)
1140 .map_err(|e| format!("Failed to serialize relationship: {}", e))?;
1141 let yaml_value = serde_yaml::from_str(&yaml_str)
1142 .map_err(|e| format!("Failed to convert to YAML: {}", e))?;
1143 Ok(crate::model::saver::RelationshipData {
1144 id: rel.id,
1145 source_table_id: rel.source_table_id,
1146 target_table_id: rel.target_table_id,
1147 yaml_value,
1148 })
1149 })
1150 .collect::<Result<Vec<_>, String>>()
1151 .map_err(|e| JsValue::from_str(&e))?;
1152
1153 saver
1154 .save_relationships(&workspace_path, &rel_data)
1155 .await
1156 .map_err(|e| {
1157 JsValue::from_str(&format!("Failed to save relationships: {}", e))
1158 })?;
1159 }
1160
1161 Ok(JsValue::from_str("Model saved successfully"))
1162 })
1163 }
1164
1165 #[cfg(feature = "bpmn")]
1178 #[wasm_bindgen]
1179 pub fn import_bpmn_model(
1180 domain_id: &str,
1181 xml_content: &str,
1182 model_name: Option<String>,
1183 ) -> Result<String, JsValue> {
1184 use crate::import::bpmn::BPMNImporter;
1185 use uuid::Uuid;
1186
1187 let domain_uuid = Uuid::parse_str(domain_id)
1188 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
1189
1190 let mut importer = BPMNImporter::new();
1191 match importer.import(xml_content, domain_uuid, model_name.as_deref()) {
1192 Ok(model) => serde_json::to_string(&model)
1193 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1194 Err(e) => Err(JsValue::from_str(&format!("Import error: {}", e))),
1195 }
1196 }
1197
1198 #[cfg(feature = "bpmn")]
1208 #[wasm_bindgen]
1209 pub fn export_bpmn_model(xml_content: &str) -> Result<String, JsValue> {
1210 use crate::export::bpmn::BPMNExporter;
1211 let exporter = BPMNExporter::new();
1212 exporter
1213 .export(xml_content)
1214 .map_err(|e| JsValue::from_str(&format!("Export error: {}", e)))
1215 }
1216
1217 #[cfg(feature = "dmn")]
1230 #[wasm_bindgen]
1231 pub fn import_dmn_model(
1232 domain_id: &str,
1233 xml_content: &str,
1234 model_name: Option<String>,
1235 ) -> Result<String, JsValue> {
1236 use crate::import::dmn::DMNImporter;
1237 use uuid::Uuid;
1238
1239 let domain_uuid = Uuid::parse_str(domain_id)
1240 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
1241
1242 let mut importer = DMNImporter::new();
1243 match importer.import(xml_content, domain_uuid, model_name.as_deref()) {
1244 Ok(model) => serde_json::to_string(&model)
1245 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1246 Err(e) => Err(JsValue::from_str(&format!("Import error: {}", e))),
1247 }
1248 }
1249
1250 #[cfg(feature = "dmn")]
1260 #[wasm_bindgen]
1261 pub fn export_dmn_model(xml_content: &str) -> Result<String, JsValue> {
1262 use crate::export::dmn::DMNExporter;
1263 let exporter = DMNExporter::new();
1264 exporter
1265 .export(xml_content)
1266 .map_err(|e| JsValue::from_str(&format!("Export error: {}", e)))
1267 }
1268
1269 #[cfg(feature = "openapi")]
1282 #[wasm_bindgen]
1283 pub fn import_openapi_spec(
1284 domain_id: &str,
1285 content: &str,
1286 api_name: Option<String>,
1287 ) -> Result<String, JsValue> {
1288 use crate::import::openapi::OpenAPIImporter;
1289 use uuid::Uuid;
1290
1291 let domain_uuid = Uuid::parse_str(domain_id)
1292 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
1293
1294 let mut importer = OpenAPIImporter::new();
1295 match importer.import(content, domain_uuid, api_name.as_deref()) {
1296 Ok(model) => serde_json::to_string(&model)
1297 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1298 Err(e) => Err(JsValue::from_str(&format!("Import error: {}", e))),
1299 }
1300 }
1301
1302 #[cfg(feature = "openapi")]
1314 #[wasm_bindgen]
1315 pub fn export_openapi_spec(
1316 content: &str,
1317 source_format: &str,
1318 target_format: Option<String>,
1319 ) -> Result<String, JsValue> {
1320 use crate::export::openapi::OpenAPIExporter;
1321 use crate::models::openapi::OpenAPIFormat;
1322
1323 let source_fmt = match source_format {
1324 "yaml" | "yml" => OpenAPIFormat::Yaml,
1325 "json" => OpenAPIFormat::Json,
1326 _ => {
1327 return Err(JsValue::from_str(
1328 "Invalid source format. Use 'yaml' or 'json'",
1329 ));
1330 }
1331 };
1332
1333 let target_fmt = if let Some(tf) = target_format {
1334 match tf.as_str() {
1335 "yaml" | "yml" => Some(OpenAPIFormat::Yaml),
1336 "json" => Some(OpenAPIFormat::Json),
1337 _ => {
1338 return Err(JsValue::from_str(
1339 "Invalid target format. Use 'yaml' or 'json'",
1340 ));
1341 }
1342 }
1343 } else {
1344 None
1345 };
1346
1347 let exporter = OpenAPIExporter::new();
1348 exporter
1349 .export(content, source_fmt, target_fmt)
1350 .map_err(|e| JsValue::from_str(&format!("Export error: {}", e)))
1351 }
1352
1353 #[cfg(feature = "openapi")]
1365 #[wasm_bindgen]
1366 pub fn convert_openapi_to_odcs(
1367 openapi_content: &str,
1368 component_name: &str,
1369 table_name: Option<String>,
1370 ) -> Result<String, JsValue> {
1371 use crate::convert::openapi_to_odcs::OpenAPIToODCSConverter;
1372
1373 let converter = OpenAPIToODCSConverter::new();
1374 match converter.convert_component(openapi_content, component_name, table_name.as_deref()) {
1375 Ok(table) => serde_json::to_string(&table)
1376 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1377 Err(e) => Err(JsValue::from_str(&format!("Conversion error: {}", e))),
1378 }
1379 }
1380
1381 #[cfg(feature = "openapi")]
1392 #[wasm_bindgen]
1393 pub fn analyze_openapi_conversion(
1394 openapi_content: &str,
1395 component_name: &str,
1396 ) -> Result<String, JsValue> {
1397 use crate::convert::openapi_to_odcs::OpenAPIToODCSConverter;
1398
1399 let converter = OpenAPIToODCSConverter::new();
1400 match converter.analyze_conversion(openapi_content, component_name) {
1401 Ok(report) => serde_json::to_string(&report)
1402 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1403 Err(e) => Err(JsValue::from_str(&format!("Analysis error: {}", e))),
1404 }
1405 }
1406}