ea 0.1.0 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CLAUDE.md +125 -0
- data/Rakefile +12 -4
- data/TODO.next/00-publish-blocking-bugs.md +74 -0
- data/TODO.next/01-standalone-ea-gem-identity.md +76 -0
- data/TODO.next/02-optional-lutaml-uml-dependency.md +47 -0
- data/TODO.next/03-slim-lutaml-uml.md +79 -0
- data/TODO.next/04-loader-registry-for-uml-repository.md +49 -0
- data/TODO.next/05-extract-shared-transformer-methods.md +14 -0
- data/TODO.next/06-deduplicate-stereotype-loading.md +17 -0
- data/TODO.next/07-transformer-registry-in-factory.md +20 -0
- data/TODO.next/08-connector-type-registry.md +27 -0
- data/TODO.next/09-element-renderer-registry.md +29 -0
- data/TODO.next/10-connector-renderer-lsp.md +18 -0
- data/TODO.next/11-consolidate-style-knowledge.md +33 -0
- data/TODO.next/12-data-driven-from-db-row.md +24 -0
- data/TODO.next/13-extract-duplicated-methods.md +17 -0
- data/TODO.next/14-remove-dead-code.md +10 -0
- data/TODO.next/15-narrow-exception-handling.md +39 -0
- data/TODO.next/16-repository-indexes.md +28 -0
- data/TODO.next/17-fix-spec-quality-and-coverage.md +32 -0
- data/TODO.next/18-xmi-tool-specific-parser-architecture.md +172 -0
- data/TODO.next/19-fix-ea-gemspec-dependency-declarations.md +56 -0
- data/TODO.next/20-ci-requires-unreleased-lutaml-uml.md +63 -0
- data/TODO.next/21-qeatoxmi-via-xmi-gem.md +340 -0
- data/TODO.next/22-strip-respond-to-from-qeatoxmi-specs.md +32 -0
- data/TODO.next/23-cleanup-idallocator.md +41 -0
- data/TODO.next/24-tighten-parity-specs.md +42 -0
- data/TODO.next/25-sparx-eaid-format-for-synthesized-ids.md +62 -0
- data/TODO.next/26-fix-uppervalue-lowervalue-count-gap.md +51 -0
- data/TODO.next/27-extract-cardinality-module.md +68 -0
- data/TODO.next/28-extract-xml-sanitizer.md +51 -0
- data/TODO.next/29-ocp-registry-for-classifier-builders.md +58 -0
- data/TODO.next/30-struct-return-for-association-end.md +37 -0
- data/TODO.next/31-idallocator-specs.md +27 -0
- data/TODO.next/32-phase2-gap-sentinel-specs.md +53 -0
- data/TODO.next/33-normalize-lower-cleanup.md +30 -0
- data/TODO.next/34-document-member-end-order-rt-prefix.md +29 -0
- data/TODO.next/35-walk-runstate-for-instance-slots.md +76 -0
- data/TODO.next/36-wire-interface-realization.md +50 -0
- data/TODO.next/37-visibility-returns-real-booleans.md +36 -0
- data/config/diagram_styles.yml +200 -0
- data/config/model_transformations.yml +266 -0
- data/config/qea_schema.yml +1024 -0
- data/docs/ea_to_uml_type_mapping.md +89 -0
- data/docs/xmi_qea_conversion_capabilities.md +99 -0
- data/examples/lur/20251010_current_plateau_v5.1.lur +0 -0
- data/examples/lur/basic.lur +0 -0
- data/examples/lur/test-output.lur +0 -0
- data/examples/lur/test.lur +0 -0
- data/examples/lur_basic_usage.rb +221 -0
- data/examples/lur_cli_workflow.rb +263 -0
- data/examples/lur_statistics.rb +326 -0
- data/examples/qea/20251010_current_plateau_v5.1.qea +0 -0
- data/examples/qea/ArcGISWorkspace_template.qea +0 -0
- data/examples/qea/README_qea_parser.adoc +230 -0
- data/examples/qea/UmlModel_template.qea +0 -0
- data/examples/qea/basic.qea +0 -0
- data/examples/qea/simple.qea +0 -0
- data/examples/qea/simple_example.qea +0 -0
- data/examples/qea/test.qea +0 -0
- data/examples/qea_standalone_query.rb +73 -0
- data/examples/qea_to_repository.rb +51 -0
- data/examples/smoke_test_real_qea.rb +81 -0
- data/exe/ea +7 -0
- data/lib/ea/cli/app.rb +72 -0
- data/lib/ea/cli/command/base.rb +80 -0
- data/lib/ea/cli/command/convert.rb +62 -0
- data/lib/ea/cli/command/diagrams.rb +81 -0
- data/lib/ea/cli/command/list.rb +61 -0
- data/lib/ea/cli/command/parse.rb +29 -0
- data/lib/ea/cli/command/stats.rb +20 -0
- data/lib/ea/cli/command/validate.rb +41 -0
- data/lib/ea/cli/command.rb +15 -0
- data/lib/ea/cli/error.rb +34 -0
- data/lib/ea/cli/output/formatter.rb +34 -0
- data/lib/ea/cli/output/json_formatter.rb +20 -0
- data/lib/ea/cli/output/table_formatter.rb +42 -0
- data/lib/ea/cli/output/yaml_formatter.rb +20 -0
- data/lib/ea/cli/output.rb +56 -0
- data/lib/ea/cli.rb +17 -0
- data/lib/ea/diagram/configuration.rb +379 -0
- data/lib/ea/diagram/element_renderers/base_renderer.rb +77 -0
- data/lib/ea/diagram/element_renderers/class_renderer.rb +323 -0
- data/lib/ea/diagram/element_renderers/connector_renderer.rb +41 -0
- data/lib/ea/diagram/element_renderers/package_renderer.rb +61 -0
- data/lib/ea/diagram/element_renderers.rb +43 -0
- data/lib/ea/diagram/extractor.rb +560 -0
- data/lib/ea/diagram/layout_engine.rb +170 -0
- data/lib/ea/diagram/path_builder.rb +202 -0
- data/lib/ea/diagram/style_parser.rb +42 -0
- data/lib/ea/diagram/style_resolver.rb +276 -0
- data/lib/ea/diagram/svg_renderer.rb +274 -0
- data/lib/ea/diagram/util.rb +73 -0
- data/lib/ea/diagram.rb +47 -0
- data/lib/ea/qea/benchmark.rb +210 -0
- data/lib/ea/qea/database.rb +308 -0
- data/lib/ea/qea/factory/association_builder.rb +203 -0
- data/lib/ea/qea/factory/association_transformer.rb +91 -0
- data/lib/ea/qea/factory/attribute_tag_transformer.rb +57 -0
- data/lib/ea/qea/factory/attribute_transformer.rb +93 -0
- data/lib/ea/qea/factory/base_transformer.rb +177 -0
- data/lib/ea/qea/factory/class_transformer.rb +116 -0
- data/lib/ea/qea/factory/constraint_transformer.rb +75 -0
- data/lib/ea/qea/factory/data_type_transformer.rb +77 -0
- data/lib/ea/qea/factory/diagram_transformer.rb +157 -0
- data/lib/ea/qea/factory/document_builder.rb +283 -0
- data/lib/ea/qea/factory/ea_to_uml_factory.rb +229 -0
- data/lib/ea/qea/factory/enum_transformer.rb +74 -0
- data/lib/ea/qea/factory/generalization_builder.rb +227 -0
- data/lib/ea/qea/factory/generalization_transformer.rb +98 -0
- data/lib/ea/qea/factory/instance_transformer.rb +68 -0
- data/lib/ea/qea/factory/object_property_transformer.rb +58 -0
- data/lib/ea/qea/factory/operation_transformer.rb +66 -0
- data/lib/ea/qea/factory/package_transformer.rb +145 -0
- data/lib/ea/qea/factory/reference_resolver.rb +99 -0
- data/lib/ea/qea/factory/stereotype_loader.rb +39 -0
- data/lib/ea/qea/factory/tagged_value_transformer.rb +38 -0
- data/lib/ea/qea/factory/transformer_registry.rb +80 -0
- data/lib/ea/qea/factory.rb +37 -0
- data/lib/ea/qea/file_detector.rb +178 -0
- data/lib/ea/qea/infrastructure/database_connection.rb +100 -0
- data/lib/ea/qea/infrastructure/schema_reader.rb +136 -0
- data/lib/ea/qea/infrastructure/table_reader.rb +224 -0
- data/lib/ea/qea/infrastructure.rb +12 -0
- data/lib/ea/qea/models/base_model.rb +59 -0
- data/lib/ea/qea/models/ea_attribute.rb +109 -0
- data/lib/ea/qea/models/ea_attribute_tag.rb +100 -0
- data/lib/ea/qea/models/ea_complexity_type.rb +79 -0
- data/lib/ea/qea/models/ea_connector.rb +160 -0
- data/lib/ea/qea/models/ea_connector_type.rb +60 -0
- data/lib/ea/qea/models/ea_constraint_type.rb +63 -0
- data/lib/ea/qea/models/ea_datatype.rb +104 -0
- data/lib/ea/qea/models/ea_diagram.rb +115 -0
- data/lib/ea/qea/models/ea_diagram_link.rb +78 -0
- data/lib/ea/qea/models/ea_diagram_object.rb +73 -0
- data/lib/ea/qea/models/ea_diagram_type.rb +56 -0
- data/lib/ea/qea/models/ea_document.rb +63 -0
- data/lib/ea/qea/models/ea_object.rb +223 -0
- data/lib/ea/qea/models/ea_object_constraint.rb +53 -0
- data/lib/ea/qea/models/ea_object_property.rb +87 -0
- data/lib/ea/qea/models/ea_object_type.rb +73 -0
- data/lib/ea/qea/models/ea_operation.rb +127 -0
- data/lib/ea/qea/models/ea_operation_param.rb +76 -0
- data/lib/ea/qea/models/ea_package.rb +78 -0
- data/lib/ea/qea/models/ea_script.rb +62 -0
- data/lib/ea/qea/models/ea_status_type.rb +66 -0
- data/lib/ea/qea/models/ea_stereotype.rb +57 -0
- data/lib/ea/qea/models/ea_tagged_value.rb +99 -0
- data/lib/ea/qea/models/ea_xref.rb +165 -0
- data/lib/ea/qea/models.rb +35 -0
- data/lib/ea/qea/repositories/base_repository.rb +225 -0
- data/lib/ea/qea/repositories/object_repository.rb +219 -0
- data/lib/ea/qea/repositories.rb +10 -0
- data/lib/ea/qea/services/configuration.rb +211 -0
- data/lib/ea/qea/services/database_loader.rb +191 -0
- data/lib/ea/qea/services.rb +10 -0
- data/lib/ea/qea/validation/association_validator.rb +73 -0
- data/lib/ea/qea/validation/attribute_validator.rb +91 -0
- data/lib/ea/qea/validation/base_validator.rb +331 -0
- data/lib/ea/qea/validation/class_validator.rb +121 -0
- data/lib/ea/qea/validation/database/circular_reference_validator.rb +109 -0
- data/lib/ea/qea/validation/database/orphan_validator.rb +153 -0
- data/lib/ea/qea/validation/database/referential_integrity_validator.rb +128 -0
- data/lib/ea/qea/validation/database.rb +16 -0
- data/lib/ea/qea/validation/diagram_validator.rb +112 -0
- data/lib/ea/qea/validation/formatters/json_formatter.rb +137 -0
- data/lib/ea/qea/validation/formatters/text_formatter.rb +235 -0
- data/lib/ea/qea/validation/formatters.rb +12 -0
- data/lib/ea/qea/validation/operation_validator.rb +71 -0
- data/lib/ea/qea/validation/package_validator.rb +111 -0
- data/lib/ea/qea/validation/validation_engine.rb +387 -0
- data/lib/ea/qea/validation/validation_message.rb +144 -0
- data/lib/ea/qea/validation/validation_result.rb +210 -0
- data/lib/ea/qea/validation/validator_registry.rb +134 -0
- data/lib/ea/qea/validation.rb +28 -0
- data/lib/ea/qea/verification/comparison_result.rb +264 -0
- data/lib/ea/qea/verification/document_normalizer.rb +169 -0
- data/lib/ea/qea/verification/document_verifier.rb +322 -0
- data/lib/ea/qea/verification/element_comparator.rb +277 -0
- data/lib/ea/qea/verification/structure_matcher.rb +287 -0
- data/lib/ea/qea/verification.rb +14 -0
- data/lib/ea/qea.rb +185 -0
- data/lib/ea/transformations/configuration.rb +333 -0
- data/lib/ea/transformations/format_registry.rb +366 -0
- data/lib/ea/transformations/parsers/base_parser.rb +482 -0
- data/lib/ea/transformations/parsers/qea_parser.rb +401 -0
- data/lib/ea/transformations/parsers/xmi_parser.rb +243 -0
- data/lib/ea/transformations/transformation_engine.rb +390 -0
- data/lib/ea/transformations.rb +85 -0
- data/lib/ea/transformers/qea_to_xmi/association_end.rb +19 -0
- data/lib/ea/transformers/qea_to_xmi/cardinality.rb +96 -0
- data/lib/ea/transformers/qea_to_xmi/context.rb +106 -0
- data/lib/ea/transformers/qea_to_xmi/guid_format.rb +56 -0
- data/lib/ea/transformers/qea_to_xmi/id_allocator.rb +92 -0
- data/lib/ea/transformers/qea_to_xmi/run_state.rb +107 -0
- data/lib/ea/transformers/qea_to_xmi/transformer.rb +607 -0
- data/lib/ea/transformers/qea_to_xmi/visibility.rb +73 -0
- data/lib/ea/transformers/qea_to_xmi.rb +29 -0
- data/lib/ea/transformers/uml_to_xmi/id_generator.rb +54 -0
- data/lib/ea/transformers/uml_to_xmi/transformer.rb +152 -0
- data/lib/ea/transformers/uml_to_xmi/writer.rb +96 -0
- data/lib/ea/transformers/uml_to_xmi.rb +16 -0
- data/lib/ea/transformers.rb +34 -0
- data/lib/ea/version.rb +1 -1
- data/lib/ea/xmi/liquid_drops/association_drop.rb +56 -0
- data/lib/ea/xmi/liquid_drops/attribute_drop.rb +72 -0
- data/lib/ea/xmi/liquid_drops/cardinality_drop.rb +35 -0
- data/lib/ea/xmi/liquid_drops/connector_drop.rb +54 -0
- data/lib/ea/xmi/liquid_drops/constraint_drop.rb +29 -0
- data/lib/ea/xmi/liquid_drops/data_type_drop.rb +63 -0
- data/lib/ea/xmi/liquid_drops/dependency_drop.rb +36 -0
- data/lib/ea/xmi/liquid_drops/diagram_drop.rb +34 -0
- data/lib/ea/xmi/liquid_drops/enum_drop.rb +49 -0
- data/lib/ea/xmi/liquid_drops/enum_owned_literal_drop.rb +25 -0
- data/lib/ea/xmi/liquid_drops/generalization_attribute_drop.rb +87 -0
- data/lib/ea/xmi/liquid_drops/generalization_drop.rb +127 -0
- data/lib/ea/xmi/liquid_drops/klass_drop.rb +191 -0
- data/lib/ea/xmi/liquid_drops/operation_drop.rb +29 -0
- data/lib/ea/xmi/liquid_drops/package_drop.rb +108 -0
- data/lib/ea/xmi/liquid_drops/root_drop.rb +34 -0
- data/lib/ea/xmi/liquid_drops/source_target_drop.rb +43 -0
- data/lib/ea/xmi/lookup_service.rb +89 -0
- data/lib/ea/xmi/parser.rb +919 -0
- data/lib/ea/xmi.rb +35 -0
- data/lib/ea.rb +10 -1
- metadata +382 -9
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Qea
|
|
5
|
+
module Validation
|
|
6
|
+
# Detects orphaned entities (invalid foreign keys)
|
|
7
|
+
class OrphanValidator < BaseValidator
|
|
8
|
+
def validate
|
|
9
|
+
find_orphaned_objects
|
|
10
|
+
find_orphaned_attributes
|
|
11
|
+
find_orphaned_operations
|
|
12
|
+
find_unreferenced_objects
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def find_orphaned_objects # rubocop:disable Metrics/MethodLength
|
|
18
|
+
objects.each do |obj|
|
|
19
|
+
next unless obj.package_id
|
|
20
|
+
next if package_exists?(obj.package_id)
|
|
21
|
+
|
|
22
|
+
result.add_error(
|
|
23
|
+
category: :orphaned,
|
|
24
|
+
entity_type: :class,
|
|
25
|
+
entity_id: obj.ea_object_id.to_s,
|
|
26
|
+
entity_name: obj.name,
|
|
27
|
+
field: "package_id",
|
|
28
|
+
reference: obj.package_id.to_s,
|
|
29
|
+
message: "Orphaned object: package #{obj.package_id} " \
|
|
30
|
+
"does not exist",
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def find_orphaned_attributes # rubocop:disable Metrics/MethodLength
|
|
36
|
+
attributes.each do |attr|
|
|
37
|
+
next if object_exists?(attr.ea_object_id)
|
|
38
|
+
|
|
39
|
+
result.add_error(
|
|
40
|
+
category: :orphaned,
|
|
41
|
+
entity_type: :attribute,
|
|
42
|
+
entity_id: attr.id.to_s,
|
|
43
|
+
entity_name: attr.name,
|
|
44
|
+
field: "object_id",
|
|
45
|
+
reference: attr.ea_object_id.to_s,
|
|
46
|
+
message: "Orphaned attribute: parent object " \
|
|
47
|
+
"#{attr.ea_object_id} does not exist",
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def find_orphaned_operations # rubocop:disable Metrics/MethodLength
|
|
53
|
+
operations.each do |op|
|
|
54
|
+
next if object_exists?(op.ea_object_id)
|
|
55
|
+
|
|
56
|
+
result.add_error(
|
|
57
|
+
category: :orphaned,
|
|
58
|
+
entity_type: :operation,
|
|
59
|
+
entity_id: op.operationid.to_s,
|
|
60
|
+
entity_name: op.name,
|
|
61
|
+
field: "object_id",
|
|
62
|
+
reference: op.ea_object_id.to_s,
|
|
63
|
+
message: "Orphaned operation: parent object " \
|
|
64
|
+
"#{op.ea_object_id} does not exist",
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def find_unreferenced_objects # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
|
|
70
|
+
# Find objects that are not referenced by any connector,
|
|
71
|
+
# attribute, or diagram
|
|
72
|
+
object_ids = objects.to_set(&:ea_object_id)
|
|
73
|
+
|
|
74
|
+
referenced_ids = Set.new
|
|
75
|
+
|
|
76
|
+
# Objects referenced by connectors
|
|
77
|
+
connectors.each do |conn|
|
|
78
|
+
referenced_ids << conn.start_object_id
|
|
79
|
+
referenced_ids << conn.end_object_id
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Objects referenced by attributes (as classifiers)
|
|
83
|
+
attributes.each do |attr|
|
|
84
|
+
referenced_ids << attr.ea_object_id
|
|
85
|
+
if attr.classifier&.to_i&.positive?
|
|
86
|
+
referenced_ids << attr.classifier.to_i
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Objects referenced by operations
|
|
91
|
+
operations.each do |op|
|
|
92
|
+
referenced_ids << op.ea_object_id
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Objects referenced by diagrams
|
|
96
|
+
diagram_objects.each do |diag_obj|
|
|
97
|
+
referenced_ids << diag_obj.ea_object_id
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Unreferenced objects (except root packages which
|
|
101
|
+
# may be unreferenced)
|
|
102
|
+
unreferenced = object_ids - referenced_ids
|
|
103
|
+
unreferenced.each do |obj_id|
|
|
104
|
+
obj = objects.find { |o| o.ea_object_id == obj_id }
|
|
105
|
+
next unless obj
|
|
106
|
+
# Skip DataType and Enumeration as they may not be referenced
|
|
107
|
+
next if obj.data_type? || obj.enumeration?
|
|
108
|
+
|
|
109
|
+
result.add_info(
|
|
110
|
+
category: :unreferenced,
|
|
111
|
+
entity_type: :class,
|
|
112
|
+
entity_id: obj_id.to_s,
|
|
113
|
+
entity_name: obj.name,
|
|
114
|
+
message: "Unreferenced object (not used in any relationship)",
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def packages
|
|
120
|
+
@packages ||= context[:db_packages] || []
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def objects
|
|
124
|
+
@objects ||= context[:db_objects] || []
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def attributes
|
|
128
|
+
@attributes ||= context[:attributes] || []
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def operations
|
|
132
|
+
@operations ||= context[:operations] || []
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def connectors
|
|
136
|
+
@connectors ||= context[:connectors] || []
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def diagram_objects
|
|
140
|
+
@diagram_objects ||= context[:diagram_objects] || []
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def package_exists?(package_id)
|
|
144
|
+
packages.any? { |p| p.package_id == package_id }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def object_exists?(object_id)
|
|
148
|
+
objects.any? { |o| o.ea_object_id == object_id }
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Qea
|
|
5
|
+
module Validation
|
|
6
|
+
# Validates referential integrity across all entities
|
|
7
|
+
class ReferentialIntegrityValidator < BaseValidator
|
|
8
|
+
def validate
|
|
9
|
+
validate_all_package_references
|
|
10
|
+
validate_all_object_references
|
|
11
|
+
validate_all_connector_references
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def validate_all_package_references # rubocop:disable Metrics/MethodLength
|
|
17
|
+
# Check all parent_id references in packages
|
|
18
|
+
packages.each do |pkg|
|
|
19
|
+
next if pkg.root?
|
|
20
|
+
|
|
21
|
+
unless package_exists?(pkg.parent_id)
|
|
22
|
+
result.add_error(
|
|
23
|
+
category: :referential_integrity,
|
|
24
|
+
entity_type: :package,
|
|
25
|
+
entity_id: pkg.package_id.to_s,
|
|
26
|
+
entity_name: pkg.name,
|
|
27
|
+
field: "parent_id",
|
|
28
|
+
reference: pkg.parent_id.to_s,
|
|
29
|
+
message: "Foreign key violation: parent_id #{pkg.parent_id} " \
|
|
30
|
+
"not found in t_package",
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def validate_all_object_references # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength
|
|
37
|
+
# Check all package_id references in objects
|
|
38
|
+
objects.each do |obj| # rubocop:disable Metrics/BlockLength
|
|
39
|
+
next unless obj.package_id
|
|
40
|
+
|
|
41
|
+
unless package_exists?(obj.package_id)
|
|
42
|
+
result.add_error(
|
|
43
|
+
category: :referential_integrity,
|
|
44
|
+
entity_type: :class,
|
|
45
|
+
entity_id: obj.ea_object_id.to_s,
|
|
46
|
+
entity_name: obj.name,
|
|
47
|
+
field: "package_id",
|
|
48
|
+
reference: obj.package_id.to_s,
|
|
49
|
+
message: "Foreign key violation: package_id " \
|
|
50
|
+
"#{obj.package_id} not found in t_package",
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Check classifier references
|
|
55
|
+
next unless obj.classifier&.positive?
|
|
56
|
+
|
|
57
|
+
unless object_exists?(obj.classifier)
|
|
58
|
+
result.add_warning(
|
|
59
|
+
category: :referential_integrity,
|
|
60
|
+
entity_type: :class,
|
|
61
|
+
entity_id: obj.ea_object_id.to_s,
|
|
62
|
+
entity_name: obj.name,
|
|
63
|
+
field: "classifier",
|
|
64
|
+
reference: obj.classifier.to_s,
|
|
65
|
+
message: "Classifier object #{obj.classifier} not found",
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def validate_all_connector_references # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
72
|
+
# Check all object references in connectors
|
|
73
|
+
connectors.each do |conn|
|
|
74
|
+
unless object_exists?(conn.start_object_id)
|
|
75
|
+
result.add_error(
|
|
76
|
+
category: :referential_integrity,
|
|
77
|
+
entity_type: :connector,
|
|
78
|
+
entity_id: conn.connector_id.to_s,
|
|
79
|
+
entity_name: conn.name || "Unnamed",
|
|
80
|
+
field: "start_object_id",
|
|
81
|
+
reference: conn.start_object_id.to_s,
|
|
82
|
+
message: "Foreign key violation: start_object_id " \
|
|
83
|
+
"#{conn.start_object_id} not found in t_object",
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
unless object_exists?(conn.end_object_id)
|
|
88
|
+
result.add_error(
|
|
89
|
+
category: :referential_integrity,
|
|
90
|
+
entity_type: :connector,
|
|
91
|
+
entity_id: conn.connector_id.to_s,
|
|
92
|
+
entity_name: conn.name || "Unnamed",
|
|
93
|
+
field: "end_object_id",
|
|
94
|
+
reference: conn.end_object_id.to_s,
|
|
95
|
+
message: "Foreign key violation: end_object_id " \
|
|
96
|
+
"#{conn.end_object_id} not found in t_object",
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def packages
|
|
103
|
+
@packages ||= context[:db_packages] || []
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def objects
|
|
107
|
+
@objects ||= context[:db_objects] || []
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def connectors
|
|
111
|
+
@connectors ||= context[:connectors] || []
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def package_exists?(package_id)
|
|
115
|
+
return false unless package_id
|
|
116
|
+
|
|
117
|
+
packages.any? { |p| p.package_id == package_id }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def object_exists?(object_id)
|
|
121
|
+
return false unless object_id
|
|
122
|
+
|
|
123
|
+
objects.any? { |o| o.ea_object_id == object_id }
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Qea
|
|
5
|
+
module Validation
|
|
6
|
+
module Database
|
|
7
|
+
autoload :CircularReferenceValidator,
|
|
8
|
+
"ea/qea/validation/database/circular_reference_validator"
|
|
9
|
+
autoload :OrphanValidator,
|
|
10
|
+
"ea/qea/validation/database/orphan_validator"
|
|
11
|
+
autoload :ReferentialIntegrityValidator,
|
|
12
|
+
"ea/qea/validation/database/referential_integrity_validator"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ea
|
|
4
|
+
module Qea
|
|
5
|
+
module Validation
|
|
6
|
+
# Validates diagram references and structure
|
|
7
|
+
class DiagramValidator < BaseValidator
|
|
8
|
+
def validate
|
|
9
|
+
validate_package_references
|
|
10
|
+
validate_diagram_objects
|
|
11
|
+
validate_diagram_links
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def validate_package_references # rubocop:disable Metrics/MethodLength
|
|
17
|
+
diagrams.each do |diagram|
|
|
18
|
+
next unless diagram.package_id
|
|
19
|
+
|
|
20
|
+
unless package_exists?(diagram.package_id)
|
|
21
|
+
result.add_error(
|
|
22
|
+
category: :missing_reference,
|
|
23
|
+
entity_type: :diagram,
|
|
24
|
+
entity_id: diagram.diagram_id.to_s,
|
|
25
|
+
entity_name: diagram.name,
|
|
26
|
+
field: "package_id",
|
|
27
|
+
reference: diagram.package_id.to_s,
|
|
28
|
+
message: "Package #{diagram.package_id} does not exist",
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validate_diagram_objects # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
35
|
+
diagram_objects.each do |diag_obj|
|
|
36
|
+
unless object_exists?(diag_obj.ea_object_id)
|
|
37
|
+
diagram = diagrams.find do |d|
|
|
38
|
+
d.diagram_id == diag_obj.diagram_id
|
|
39
|
+
end
|
|
40
|
+
result.add_warning(
|
|
41
|
+
category: :missing_reference,
|
|
42
|
+
entity_type: :diagram_object,
|
|
43
|
+
entity_id: diag_obj.instance_id.to_s,
|
|
44
|
+
entity_name: diagram&.name || "Unknown diagram",
|
|
45
|
+
field: "object_id",
|
|
46
|
+
reference: diag_obj.ea_object_id.to_s,
|
|
47
|
+
message: "Diagram object references non-existent " \
|
|
48
|
+
"object #{diag_obj.ea_object_id}",
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def validate_diagram_links # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
|
55
|
+
diagram_links.each do |diag_link|
|
|
56
|
+
unless connector_exists?(diag_link.connectorid)
|
|
57
|
+
diagram = diagrams.find do |d|
|
|
58
|
+
d.diagram_id == diag_link.diagramid
|
|
59
|
+
end
|
|
60
|
+
result.add_warning(
|
|
61
|
+
category: :missing_reference,
|
|
62
|
+
entity_type: :diagram_link,
|
|
63
|
+
entity_id: diag_link.instance_id.to_s,
|
|
64
|
+
entity_name: diagram&.name || "Unknown diagram",
|
|
65
|
+
field: "connectorid",
|
|
66
|
+
reference: diag_link.connectorid.to_s,
|
|
67
|
+
message: "Diagram link references non-existent " \
|
|
68
|
+
"connector #{diag_link.connectorid}",
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def diagrams
|
|
75
|
+
@diagrams ||= context[:diagrams] || []
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def diagram_objects
|
|
79
|
+
@diagram_objects ||= context[:diagram_objects] || []
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def diagram_links
|
|
83
|
+
@diagram_links ||= context[:diagram_links] || []
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def packages
|
|
87
|
+
@packages ||= context[:db_packages] || []
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def objects
|
|
91
|
+
@objects ||= context[:db_objects] || []
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def connectors
|
|
95
|
+
@connectors ||= context[:connectors] || []
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def package_exists?(package_id)
|
|
99
|
+
packages.any? { |p| p.package_id == package_id }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def object_exists?(object_id)
|
|
103
|
+
objects.any? { |o| o.ea_object_id == object_id }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def connector_exists?(connector_id)
|
|
107
|
+
connectors.any? { |c| c.connector_id == connector_id }
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Ea
|
|
6
|
+
module Qea
|
|
7
|
+
module Validation
|
|
8
|
+
module Formatters
|
|
9
|
+
# Formats validation results as JSON for machine consumption
|
|
10
|
+
#
|
|
11
|
+
# @example Basic usage
|
|
12
|
+
# formatter = JsonFormatter.new(result)
|
|
13
|
+
# puts formatter.format
|
|
14
|
+
#
|
|
15
|
+
# @example Pretty printed
|
|
16
|
+
# formatter = JsonFormatter.new(result, pretty: true)
|
|
17
|
+
# puts formatter.format
|
|
18
|
+
class JsonFormatter
|
|
19
|
+
attr_reader :result, :options
|
|
20
|
+
|
|
21
|
+
# Creates a new JSON formatter
|
|
22
|
+
#
|
|
23
|
+
# @param result [ValidationResult] The validation result to format
|
|
24
|
+
# @param options [Hash] Formatting options
|
|
25
|
+
# @option options [Boolean] :pretty Pretty print JSON (default: false)
|
|
26
|
+
def initialize(result: nil, **options)
|
|
27
|
+
@result = result
|
|
28
|
+
@options = {
|
|
29
|
+
pretty: false,
|
|
30
|
+
}.merge(options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Formats the validation result as JSON
|
|
34
|
+
#
|
|
35
|
+
# @return [String] JSON output
|
|
36
|
+
def format
|
|
37
|
+
data = build_data
|
|
38
|
+
|
|
39
|
+
if options[:pretty]
|
|
40
|
+
JSON.pretty_generate(data)
|
|
41
|
+
else
|
|
42
|
+
JSON.generate(data)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# Builds the data structure for JSON output
|
|
49
|
+
#
|
|
50
|
+
# @return [Hash]
|
|
51
|
+
def build_data
|
|
52
|
+
{
|
|
53
|
+
summary: build_summary,
|
|
54
|
+
messages: build_messages,
|
|
55
|
+
by_category: build_by_category,
|
|
56
|
+
by_severity: build_by_severity,
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Builds the summary section
|
|
61
|
+
#
|
|
62
|
+
# @return [Hash]
|
|
63
|
+
def build_summary
|
|
64
|
+
{
|
|
65
|
+
valid: result.valid?,
|
|
66
|
+
total_messages: result.messages.size,
|
|
67
|
+
error_count: result.errors.size,
|
|
68
|
+
warning_count: result.warnings.size,
|
|
69
|
+
info_count: result.info.size,
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Builds the messages array
|
|
74
|
+
#
|
|
75
|
+
# @return [Array<Hash>]
|
|
76
|
+
def build_messages # rubocop:disable Metrics/MethodLength
|
|
77
|
+
result.messages.map do |message|
|
|
78
|
+
{
|
|
79
|
+
severity: message.severity,
|
|
80
|
+
category: message.category,
|
|
81
|
+
entity_type: message.entity_type,
|
|
82
|
+
entity_id: message.entity_id,
|
|
83
|
+
entity_name: message.entity_name,
|
|
84
|
+
message: message.message,
|
|
85
|
+
context: message.context,
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Builds messages grouped by category
|
|
91
|
+
#
|
|
92
|
+
# @return [Hash]
|
|
93
|
+
def build_by_category
|
|
94
|
+
result.messages.group_by(&:category).transform_values do |msgs|
|
|
95
|
+
{
|
|
96
|
+
count: msgs.size,
|
|
97
|
+
messages: msgs.map(&:message),
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Builds messages grouped by severity
|
|
103
|
+
#
|
|
104
|
+
# @return [Hash]
|
|
105
|
+
def build_by_severity
|
|
106
|
+
{
|
|
107
|
+
errors: format_severity_group(result.errors),
|
|
108
|
+
warnings: format_severity_group(result.warnings),
|
|
109
|
+
info: format_severity_group(result.info),
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Formats a group of messages by severity
|
|
114
|
+
#
|
|
115
|
+
# @param messages [Array<ValidationMessage>]
|
|
116
|
+
# @return [Hash]
|
|
117
|
+
def format_severity_group(messages) # rubocop:disable Metrics/MethodLength
|
|
118
|
+
{
|
|
119
|
+
count: messages.size,
|
|
120
|
+
by_category: messages
|
|
121
|
+
.group_by(&:category).transform_values do |msgs|
|
|
122
|
+
msgs.map do |msg|
|
|
123
|
+
{
|
|
124
|
+
entity_type: msg.entity_type,
|
|
125
|
+
entity_id: msg.entity_id,
|
|
126
|
+
entity_name: msg.entity_name,
|
|
127
|
+
message: msg.message,
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
end,
|
|
131
|
+
}
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|