mxrb 0.1.2 → 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/bin/mxrb +458 -10
- data/docs/de-DE/README.md +1 -0
- data/docs/de-DE/oql-sql.md +42 -0
- data/docs/de-DE/project-structure.md +201 -2
- data/docs/de-DE/runtime-ruby.md +104 -0
- data/docs/de-DE/runtime-schema-migrations.md +44 -0
- data/docs/de-DE/validation-matrix.md +42 -4
- data/docs/en-US/README.md +1 -0
- data/docs/en-US/oql-sql.md +41 -0
- data/docs/en-US/project-structure.md +192 -2
- data/docs/en-US/runtime-ruby.md +100 -0
- data/docs/en-US/runtime-schema-migrations.md +41 -0
- data/docs/en-US/validation-matrix.md +39 -4
- data/docs/pt-BR/README.md +1 -0
- data/docs/pt-BR/entity-dsl.md +8 -0
- data/docs/pt-BR/oql-sql.md +41 -0
- data/docs/pt-BR/project-structure.md +197 -2
- data/docs/pt-BR/runtime-ruby.md +108 -0
- data/docs/pt-BR/runtime-schema-migrations.md +40 -0
- data/docs/pt-BR/validation-matrix.md +42 -5
- data/docs/pt-BR/writing.md +28 -0
- data/lib/mxrb/cli/help.rb +426 -0
- data/lib/mxrb/cli/release.rb +137 -0
- data/lib/mxrb/compiler/compatibility_analyzer.rb +1 -1
- data/lib/mxrb/compiler/data_grid_bundle_compiler.rb +8 -1
- data/lib/mxrb/compiler/domain_document_compiler.rb +3 -0
- data/lib/mxrb/compiler/gallery_bundle_compiler.rb +11 -1
- data/lib/mxrb/compiler/generic_widget_bundle_compiler.rb +76 -19
- data/lib/mxrb/compiler/legacy_custom_widget_compiler.rb +159 -0
- data/lib/mxrb/compiler/legacy_data_grid_compiler.rb +43 -7
- data/lib/mxrb/compiler/legacy_page_builder.rb +1248 -23
- data/lib/mxrb/compiler/packager.rb +27 -13
- data/lib/mxrb/compiler/page_bundle_compiler.rb +13 -4
- data/lib/mxrb/compiler/portable_packager.rb +16 -11
- data/lib/mxrb/compiler/web_bundle_builder.rb +5 -1
- data/lib/mxrb/compiler/web_shell_materializer.rb +42 -2
- data/lib/mxrb/compiler/widget_package_extractor.rb +3 -0
- data/lib/mxrb/domain_diagram/lifecycle.rb +272 -0
- data/lib/mxrb/domain_diagram.rb +409 -0
- data/lib/mxrb/dsl/builder.rb +62 -11
- data/lib/mxrb/environment.rb +226 -0
- data/lib/mxrb/exporter.rb +265 -45
- data/lib/mxrb/http/server.rb +107 -0
- data/lib/mxrb/initializer.rb +80 -9
- data/lib/mxrb/integrity/validator.rb +32 -7
- data/lib/mxrb/io/bson_codec.rb +42 -1
- data/lib/mxrb/io/mpr_file.rb +146 -3
- data/lib/mxrb/model/attribute.rb +3 -0
- data/lib/mxrb/model/entity.rb +44 -2
- data/lib/mxrb/model/microflow.rb +3 -1
- data/lib/mxrb/model/module.rb +71 -0
- data/lib/mxrb/model/page.rb +126 -15
- data/lib/mxrb/model/project.rb +3 -0
- data/lib/mxrb/modeler/catalog.rb +178 -0
- data/lib/mxrb/official_marketplace/module_package_importer.rb +23 -12
- data/lib/mxrb/official_marketplace/widget_package_installer.rb +15 -11
- data/lib/mxrb/official_marketplace.rb +9 -3
- data/lib/mxrb/oql/reverse_translator.rb +394 -0
- data/lib/mxrb/oql/server.rb +4 -8
- data/lib/mxrb/oql.rb +32 -4
- data/lib/mxrb/progress.rb +242 -0
- data/lib/mxrb/project_lifecycle.rb +5 -2
- data/lib/mxrb/ruby_app/exporter.rb +1749 -0
- data/lib/mxrb/ruby_app/preset.rb +506 -0
- data/lib/mxrb/ruby_app/session_manager.rb +122 -0
- data/lib/mxrb/ruby_app.rb +1361 -0
- data/lib/mxrb/runtime/access_control.rb +408 -0
- data/lib/mxrb/runtime/native.rb +1015 -93
- data/lib/mxrb/runtime/scheduler.rb +396 -0
- data/lib/mxrb/runtime/schema_migrator.rb +598 -0
- data/lib/mxrb/runtime/shared_store.rb +335 -0
- data/lib/mxrb/runtime/sqlite_store.rb +729 -0
- data/lib/mxrb/scaffold/generator.rb +11 -3
- data/lib/mxrb/team_server.rb +10 -7
- data/lib/mxrb/uml/activity_diagram.rb +118 -0
- data/lib/mxrb/uml/class_diagram.rb +118 -0
- data/lib/mxrb/uml/sequence_diagram.rb +112 -0
- data/lib/mxrb/uml/server.rb +146 -0
- data/lib/mxrb/uml/support.rb +35 -0
- data/lib/mxrb/version.rb +1 -1
- data/lib/mxrb/web_ui/assets/abnfDiagram-N423BO3Z-C4JD3cEC.js +1 -0
- data/lib/mxrb/web_ui/assets/arc-3Z53kgFp.js +1 -0
- data/lib/mxrb/web_ui/assets/architecture-TIHT7OUA-BStBTLea.js +1 -0
- data/lib/mxrb/web_ui/assets/architectureDiagram-T3A2C74G-DBwqnM4J.js +36 -0
- data/lib/mxrb/web_ui/assets/array-BifhSqXX.js +1 -0
- data/lib/mxrb/web_ui/assets/blockDiagram-VBNYF7ZC-DLtuHt-j.js +132 -0
- data/lib/mxrb/web_ui/assets/c4Diagram-5PPSVZJV-ConSk_pe.js +10 -0
- data/lib/mxrb/web_ui/assets/channel-BhPvD7Dt.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-2GRJ4B5K-D2HpWsf_.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-4I5QYGJK-CiIdNwaF.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-5RXB4S5H-Bu_AaEkE.js +231 -0
- data/lib/mxrb/web_ui/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- data/lib/mxrb/web_ui/assets/chunk-6Q2QTUOP-wQkNmt_X.js +88 -0
- data/lib/mxrb/web_ui/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-GF5L2VYU-G8MmiBzU.js +206 -0
- data/lib/mxrb/web_ui/assets/chunk-I66GZJ75-CulcrLPh.js +127 -0
- data/lib/mxrb/web_ui/assets/chunk-JQJVKLGR-ukjzcdlL.js +156 -0
- data/lib/mxrb/web_ui/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-KBJHAD2P-Wm35lYov.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-KEIR6QF5-BICK3FdT.js +161 -0
- data/lib/mxrb/web_ui/assets/chunk-NSK5VX7P-D2nYrCY4.js +2 -0
- data/lib/mxrb/web_ui/assets/chunk-QR6OTTB3-BrdIzg4Q.js +62 -0
- data/lib/mxrb/web_ui/assets/chunk-RYQCIY6F-CCfZMcW6.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-UBXNYLIW-BPEVYsFK.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-W5SLKNZC-2E6OPhA4.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-WRU74C26-CBIvOyWp.js +70 -0
- data/lib/mxrb/web_ui/assets/chunk-XXDRQBXY-pH58XAyl.js +1 -0
- data/lib/mxrb/web_ui/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- data/lib/mxrb/web_ui/assets/classDiagram-JCYQIIEL-xiqo4D89.js +1 -0
- data/lib/mxrb/web_ui/assets/classDiagram-v2-OCEON4UE-xiqo4D89.js +1 -0
- data/lib/mxrb/web_ui/assets/cose-bilkent-JH36ORCC-t_flPpii.js +1 -0
- data/lib/mxrb/web_ui/assets/cynefin-VYW2F7L2-BIqktTEv.js +1 -0
- data/lib/mxrb/web_ui/assets/cynefinDiagram-MW4NZA55-C2d--lut.js +62 -0
- data/lib/mxrb/web_ui/assets/cytoscape.esm-B-NFISlW.js +321 -0
- data/lib/mxrb/web_ui/assets/dagre-Buvkdvvj.js +1 -0
- data/lib/mxrb/web_ui/assets/dagre-VZM6K2ZE-CSz2_tb5.js +4 -0
- data/lib/mxrb/web_ui/assets/defaultLocale-BFoDCU3G.js +1 -0
- data/lib/mxrb/web_ui/assets/diagram-7IWD3JNH-DCt7u7V_.js +30 -0
- data/lib/mxrb/web_ui/assets/diagram-B4RE2ZJO-DqZBqkMu.js +3 -0
- data/lib/mxrb/web_ui/assets/diagram-LBJQPF4R-ClIYbIsI.js +24 -0
- data/lib/mxrb/web_ui/assets/diagram-Q27KOJAE-DFxhvthJ.js +24 -0
- data/lib/mxrb/web_ui/assets/diagram-UB23O5K3-BSUTXptv.js +41 -0
- data/lib/mxrb/web_ui/assets/dist-D2qOEbeJ.js +1 -0
- data/lib/mxrb/web_ui/assets/domain-mGOZ1KCG.css +1 -0
- data/lib/mxrb/web_ui/assets/domain-qsjXhy4v.js +1 -0
- data/lib/mxrb/web_ui/assets/ebnfDiagram-BXEA7PRR-B5reICEi.js +1 -0
- data/lib/mxrb/web_ui/assets/erDiagram-JOGREHBK-Co5Mxn2p.js +85 -0
- data/lib/mxrb/web_ui/assets/eventmodeling-45OFAUF4-C1ylhSks.js +1 -0
- data/lib/mxrb/web_ui/assets/flowDiagram-UKHOOZJN-CHXaJIaS.js +1 -0
- data/lib/mxrb/web_ui/assets/ganttDiagram-PKOTCBZU-DPPPaWaz.js +292 -0
- data/lib/mxrb/web_ui/assets/gitGraph-TEB2WS4Q-qS77ywA9.js +1 -0
- data/lib/mxrb/web_ui/assets/gitGraphDiagram-DS77QQ5N-DpAv6-BT.js +106 -0
- data/lib/mxrb/web_ui/assets/graphlib-DS17s2tU.js +1 -0
- data/lib/mxrb/web_ui/assets/info-DKCQHKI2-C9VD1Y4e.js +1 -0
- data/lib/mxrb/web_ui/assets/infoDiagram-6WML65LV-Bk1kbcCy.js +2 -0
- data/lib/mxrb/web_ui/assets/init-C-OQMol4.js +1 -0
- data/lib/mxrb/web_ui/assets/ishikawaDiagram-WSZJBQD7-0gcISBFR.js +70 -0
- data/lib/mxrb/web_ui/assets/journeyDiagram-NVQOT4AX-Dt0RQnlh.js +139 -0
- data/lib/mxrb/web_ui/assets/jsx-runtime-DV0a5kSb.js +9 -0
- data/lib/mxrb/web_ui/assets/kanban-definition-27J2QSJJ-DIPQM7w9.js +89 -0
- data/lib/mxrb/web_ui/assets/katex-CXMH3UgJ.js +257 -0
- data/lib/mxrb/web_ui/assets/line-C0rYD-tL.js +1 -0
- data/lib/mxrb/web_ui/assets/linear-BPPxWorj.js +1 -0
- data/lib/mxrb/web_ui/assets/map-BaFkSB1l.js +1 -0
- data/lib/mxrb/web_ui/assets/mermaid-parser.core-B_UPzTxa.js +7 -0
- data/lib/mxrb/web_ui/assets/mindmap-definition-FAOFIHXS-CdJRSMuk.js +96 -0
- data/lib/mxrb/web_ui/assets/modeler-BNqHthwk.css +1 -0
- data/lib/mxrb/web_ui/assets/modeler-CQmqflbg.js +1 -0
- data/lib/mxrb/web_ui/assets/ordinal-BDEzSJ7C.js +1 -0
- data/lib/mxrb/web_ui/assets/packet-7NZHBO7P-CwQ4NSBs.js +1 -0
- data/lib/mxrb/web_ui/assets/path-fybaL0A-.js +1 -0
- data/lib/mxrb/web_ui/assets/pegDiagram-VL7TDLO6-D40S7ppx.js +1 -0
- data/lib/mxrb/web_ui/assets/pie-RZYD4A2V-CJroHMKh.js +1 -0
- data/lib/mxrb/web_ui/assets/pieDiagram-7S7Q4E2Y-QB54__BV.js +39 -0
- data/lib/mxrb/web_ui/assets/quadrantDiagram-CIZ2JOQS-DBhDS6ag.js +7 -0
- data/lib/mxrb/web_ui/assets/radar-I7S5WNFK-DdIUwd2d.js +1 -0
- data/lib/mxrb/web_ui/assets/railroad-3IZDKUUU-B1Lw6Xyq.js +1 -0
- data/lib/mxrb/web_ui/assets/railroad-abnf-AHOZXSZD-CQgQoeak.js +1 -0
- data/lib/mxrb/web_ui/assets/railroad-ebnf-EBAXGLYW-D7qEQ2x8.js +1 -0
- data/lib/mxrb/web_ui/assets/railroad-peg-LSFZ7HO6-C005Vxbb.js +1 -0
- data/lib/mxrb/web_ui/assets/railroadDiagram-AXF67PYL-BBP2qk7M.js +1 -0
- data/lib/mxrb/web_ui/assets/requirementDiagram-LRYGKXZP-D2cLsAoI.js +84 -0
- data/lib/mxrb/web_ui/assets/rough.esm-Dy-Kn_BL.js +1 -0
- data/lib/mxrb/web_ui/assets/sankeyDiagram-W5VNT64P-Bpi3FiP2.js +40 -0
- data/lib/mxrb/web_ui/assets/sequenceDiagram-SI44F4Z6-DCRcDlVe.js +162 -0
- data/lib/mxrb/web_ui/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
- data/lib/mxrb/web_ui/assets/src-CvxfUak2.js +1 -0
- data/lib/mxrb/web_ui/assets/stateDiagram-OKZ733FA-C2GCEoxl.js +1 -0
- data/lib/mxrb/web_ui/assets/stateDiagram-v2-UEYNNEHI-D2gWgedh.js +1 -0
- data/lib/mxrb/web_ui/assets/swimlanes-SLNWSIFB-CRI9irEW.js +2 -0
- data/lib/mxrb/web_ui/assets/swimlanesDiagram-ULZ7WXOC-C5Wb_-hZ.js +8 -0
- data/lib/mxrb/web_ui/assets/theme-_mxc7BdY.css +1 -0
- data/lib/mxrb/web_ui/assets/timeline-definition-Z64GVDOM-B1-mNg-h.js +120 -0
- data/lib/mxrb/web_ui/assets/treeView-QDETBFTQ-7CTvIwKq.js +1 -0
- data/lib/mxrb/web_ui/assets/treemap-6X3UGDF4-CtL22pq5.js +1 -0
- data/lib/mxrb/web_ui/assets/uml-C3KAFGTq.js +51 -0
- data/lib/mxrb/web_ui/assets/uml-Dn-qd01X.css +1 -0
- data/lib/mxrb/web_ui/assets/vennDiagram-T6HMQDX7-BzVnUEAy.js +34 -0
- data/lib/mxrb/web_ui/assets/wardley-OPB4EBWU-JNi-QNag.js +1 -0
- data/lib/mxrb/web_ui/assets/wardleyDiagram-T6FBY63Y-BUwmAQ-B.js +78 -0
- data/lib/mxrb/web_ui/assets/xychartDiagram-ELKLHX3M-DEQS8sKp.js +7 -0
- data/lib/mxrb/web_ui/domain.html +15 -0
- data/lib/mxrb/web_ui/modeler.html +16 -0
- data/lib/mxrb/web_ui/uml.html +31 -0
- data/lib/mxrb/web_ui.rb +89 -0
- data/lib/mxrb/widget_certification.rb +208 -0
- data/lib/mxrb/writer.rb +278 -41
- data/lib/mxrb.rb +38 -8
- metadata +157 -5
data/lib/mxrb/model/module.rb
CHANGED
|
@@ -7,6 +7,40 @@ module Mxrb
|
|
|
7
7
|
# $Type: Projects$Module
|
|
8
8
|
# ContainmentName: "Modules"
|
|
9
9
|
class Module < Unit
|
|
10
|
+
INFRASTRUCTURE_DOCUMENT_ROUTES = {
|
|
11
|
+
'ExportMappings$ExportMapping' => 'mappings/exports',
|
|
12
|
+
'ImportMappings$ImportMapping' => 'mappings/imports',
|
|
13
|
+
'JsonStructures$JsonStructure' => 'mappings/json_structures',
|
|
14
|
+
'MessageDefinitions$MessageDefinitionCollection' => 'mappings/message_definitions',
|
|
15
|
+
'MessageDefinitions$MessageDefinition2' => 'mappings/message_definitions',
|
|
16
|
+
'XmlSchemas$XmlSchema' => 'mappings/xml_schemas',
|
|
17
|
+
'Rest$PublishedRestService' => 'endpoints',
|
|
18
|
+
'WebServices$PublishedService' => 'endpoints',
|
|
19
|
+
'WebServices$PublishedWebService' => 'endpoints',
|
|
20
|
+
'ODataPublish$PublishedODataService' => 'endpoints',
|
|
21
|
+
'ODataPublish$PublishedODataService2' => 'endpoints',
|
|
22
|
+
'Rest$ConsumedRestService' => 'integrations',
|
|
23
|
+
'Rest$ConsumedODataService' => 'integrations',
|
|
24
|
+
'AppServices$ConsumedAppService' => 'integrations',
|
|
25
|
+
'ODataImport$ConsumedODataService' => 'integrations'
|
|
26
|
+
}.freeze
|
|
27
|
+
MAPPING_DOCUMENT_TYPES = INFRASTRUCTURE_DOCUMENT_ROUTES.keys.grep(
|
|
28
|
+
/Mappings|JsonStructures|MessageDefinitions|XmlSchemas/
|
|
29
|
+
).freeze
|
|
30
|
+
APPLICATION_DOCUMENT_ROUTES = {
|
|
31
|
+
'DataSets$DataSet' => 'queries/datasets',
|
|
32
|
+
'ScheduledEvents$ScheduledEvent' => 'jobs/scheduled_events'
|
|
33
|
+
}.freeze
|
|
34
|
+
DOMAIN_DOCUMENT_ROUTES = {
|
|
35
|
+
'DomainModels$ViewEntitySourceDocument' => 'oql_views',
|
|
36
|
+
'Enumerations$Enumeration' => 'enumerations',
|
|
37
|
+
'Constants$Constant' => 'constants'
|
|
38
|
+
}.freeze
|
|
39
|
+
EDITABLE_DOCUMENT_TYPES = (
|
|
40
|
+
INFRASTRUCTURE_DOCUMENT_ROUTES.keys + APPLICATION_DOCUMENT_ROUTES.keys +
|
|
41
|
+
DOMAIN_DOCUMENT_ROUTES.keys
|
|
42
|
+
).freeze
|
|
43
|
+
|
|
10
44
|
attr_reader :name, :sort_index, :from_app_store,
|
|
11
45
|
:app_store_guid, :app_store_version, :export_level
|
|
12
46
|
|
|
@@ -84,6 +118,28 @@ module Mxrb
|
|
|
84
118
|
.map { unit_to_doc(_1) }
|
|
85
119
|
end
|
|
86
120
|
|
|
121
|
+
def mapping_documents
|
|
122
|
+
infrastructure_documents.select { MAPPING_DOCUMENT_TYPES.include?(_1[:type]) }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def infrastructure_documents
|
|
126
|
+
@infrastructure_documents ||= routed_documents(INFRASTRUCTURE_DOCUMENT_ROUTES)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def application_documents
|
|
130
|
+
@application_documents ||= routed_documents(APPLICATION_DOCUMENT_ROUTES)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def domain_documents
|
|
134
|
+
@domain_documents ||= routed_documents(DOMAIN_DOCUMENT_ROUTES)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def oql_view_documents
|
|
138
|
+
@oql_view_documents ||= domain_documents.select do |document|
|
|
139
|
+
document[:type] == 'DomainModels$ViewEntitySourceDocument'
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
87
143
|
def module_roles
|
|
88
144
|
@module_roles ||= begin
|
|
89
145
|
raw = @mpr.children_of(@id).find { _1["ContainmentName"] == "ModuleSecurity" }
|
|
@@ -109,6 +165,21 @@ module Mxrb
|
|
|
109
165
|
@mpr.parse_contents(unit_hash[:raw])
|
|
110
166
|
end
|
|
111
167
|
|
|
168
|
+
def routed_documents(routes)
|
|
169
|
+
document_units.filter_map do |unit|
|
|
170
|
+
route = routes[unit[:type]]
|
|
171
|
+
next unless route
|
|
172
|
+
|
|
173
|
+
raw = unit.fetch(:raw)
|
|
174
|
+
doc = @mpr.parse_contents(raw)
|
|
175
|
+
{
|
|
176
|
+
id: raw.fetch("UnitID"), container_id: raw.fetch("ContainerID"),
|
|
177
|
+
containment: raw.fetch("ContainmentName"), type: unit.fetch(:type),
|
|
178
|
+
name: doc["Name"] || doc["name"] || raw.fetch("UnitID"), doc:, route:
|
|
179
|
+
}
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
112
183
|
# Documents live in ContainmentName = "Documents" recursively under this module.
|
|
113
184
|
# We do a simple two-pass: direct Documents children + Documents inside Folders.
|
|
114
185
|
def document_units
|
data/lib/mxrb/model/page.rb
CHANGED
|
@@ -10,7 +10,8 @@ module Mxrb
|
|
|
10
10
|
attr_reader :name, :documentation, :url, :layout_id, :title,
|
|
11
11
|
:popup_width, :popup_height, :popup_resizable, :excluded,
|
|
12
12
|
:allowed_module_roles, :parameters, :export_level,
|
|
13
|
-
:widgets, :data_source, :
|
|
13
|
+
:widgets, :data_source, :appearance_class, :appearance_style,
|
|
14
|
+
:raw_document
|
|
14
15
|
|
|
15
16
|
def decode(doc)
|
|
16
17
|
@raw_document = doc
|
|
@@ -25,6 +26,8 @@ module Mxrb
|
|
|
25
26
|
@popup_resizable = doc["PopupResizable"] == true
|
|
26
27
|
@excluded = doc["Excluded"] == true
|
|
27
28
|
@export_level = doc["ExportLevel"] || "Hidden"
|
|
29
|
+
@appearance_class = doc.dig("Appearance", "Class").to_s
|
|
30
|
+
@appearance_style = doc.dig("Appearance", "Style").to_s
|
|
28
31
|
@allowed_module_roles = parse_array(doc["AllowedModuleRoles"] || doc["AllowedRoles"] || doc["allowedModuleRoles"])
|
|
29
32
|
@parameters = parse_array(doc["Parameters"] || doc["parameters"])
|
|
30
33
|
@widgets = []
|
|
@@ -112,7 +115,8 @@ module Mxrb
|
|
|
112
115
|
children = []
|
|
113
116
|
parse_widgets(parse_array(widget["Widgets"]), children)
|
|
114
117
|
target << { type: :container, name: widget["Name"] || "container",
|
|
115
|
-
options: container_options(widget), children: children,
|
|
118
|
+
options: container_options(widget), children: children,
|
|
119
|
+
events: container_events(widget) }
|
|
116
120
|
next
|
|
117
121
|
end
|
|
118
122
|
if layout_container?(widget)
|
|
@@ -171,17 +175,42 @@ module Mxrb
|
|
|
171
175
|
end
|
|
172
176
|
|
|
173
177
|
def widget_options(widget, widget_type)
|
|
174
|
-
options =
|
|
178
|
+
options = appearance_options(widget).merge(
|
|
175
179
|
attribute: attribute_path(widget),
|
|
176
180
|
caption: widget_caption(widget, widget_type)
|
|
177
|
-
|
|
181
|
+
).compact
|
|
182
|
+
parameters = template_parameters(widget)
|
|
183
|
+
options[:parameters] = parameters unless parameters.empty?
|
|
178
184
|
options[:lines] = widget["NumberOfLines"] if widget_type == :text_area && widget["NumberOfLines"]
|
|
179
185
|
options
|
|
180
186
|
end
|
|
181
187
|
|
|
182
188
|
def container_options(widget)
|
|
189
|
+
appearance_options(widget)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def appearance_options(widget)
|
|
183
193
|
class_name = widget.dig("Appearance", "Class") || widget["Class"]
|
|
184
|
-
|
|
194
|
+
style = widget.dig("Appearance", "Style") || widget["Style"]
|
|
195
|
+
dynamic = widget.dig("Appearance", "DynamicClasses")
|
|
196
|
+
visibility = widget.dig("ConditionalVisibilitySettings", "Expression")
|
|
197
|
+
{ class: class_name.to_s, style: style.to_s,
|
|
198
|
+
dynamic_class: dynamic.to_s, visible: visibility.to_s }.reject { |_key, value| value.empty? }
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def container_events(widget)
|
|
202
|
+
action = parse_action(widget["OnClickAction"] || widget["Action"])
|
|
203
|
+
action ? [action.merge(event: :on_click)] : []
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def template_parameters(widget)
|
|
207
|
+
template = widget["Content"] || widget["CaptionTemplate"] || widget["LabelTemplate"]
|
|
208
|
+
parse_array(template.is_a?(Hash) ? template["Parameters"] : nil).filter_map do |parameter|
|
|
209
|
+
expression = parameter["Expression"].to_s
|
|
210
|
+
attribute = parameter.dig("AttributeRef", "Attribute").to_s.split('.').last.to_s
|
|
211
|
+
expression = "$currentObject/#{attribute}" if expression.empty? && !attribute.empty?
|
|
212
|
+
expression unless expression.empty?
|
|
213
|
+
end
|
|
185
214
|
end
|
|
186
215
|
|
|
187
216
|
def attribute_path(widget)
|
|
@@ -210,27 +239,58 @@ module Mxrb
|
|
|
210
239
|
options = { entity: grid_entity(widget), columns: columns }.compact
|
|
211
240
|
options[:search_bar] = parse_search_bar(widget["SearchBar"]) if widget["SearchBar"].is_a?(Hash)
|
|
212
241
|
options[:toolbar] = parse_toolbar(widget["ToolBar"]) if widget["ToolBar"].is_a?(Hash)
|
|
213
|
-
{ type: :data_grid, name: widget["Name"], options: options, events:
|
|
242
|
+
{ type: :data_grid, name: widget["Name"], options: options, events: grid_events(widget) }
|
|
214
243
|
end
|
|
215
244
|
|
|
216
245
|
def pluggable_widget(widget)
|
|
217
246
|
case widget.dig("Type", "WidgetId")
|
|
218
247
|
when "com.mendix.widget.web.datagrid.Datagrid" then data_grid2_widget(widget)
|
|
219
248
|
when "com.mendix.widget.web.combobox.Combobox" then combo_box_widget(widget)
|
|
249
|
+
when "com.mendix.widget.web.gallery.Gallery" then gallery_widget(widget)
|
|
220
250
|
else native_widget(widget)
|
|
221
251
|
end
|
|
222
252
|
end
|
|
223
253
|
|
|
254
|
+
def gallery_widget(widget)
|
|
255
|
+
properties = custom_property_map(widget["Object"], widget.dig("Type", "ObjectType"))
|
|
256
|
+
source = properties.dig("datasource", "DataSource") || {}
|
|
257
|
+
children = []
|
|
258
|
+
parse_widgets(parse_array(properties.dig("content", "Widgets")), children)
|
|
259
|
+
sort = parse_array(source.dig("SortBar", "SortItems")).map do |item|
|
|
260
|
+
{
|
|
261
|
+
attribute: item.dig("AttributeRef", "Attribute"),
|
|
262
|
+
direction: item["SortDirection"].to_s
|
|
263
|
+
}.compact
|
|
264
|
+
end
|
|
265
|
+
xpath = source["XPathConstraint"].to_s
|
|
266
|
+
options = appearance_options(widget).merge(
|
|
267
|
+
entity: source.dig("EntityRef", "Entity"), xpath: xpath,
|
|
268
|
+
association: xpath[/\[([\w.]+)\s*=\s*\$\w+\]/, 1],
|
|
269
|
+
context_variable: xpath[/\$([A-Za-z_]\w*)/, 1], sort: sort
|
|
270
|
+
).compact
|
|
271
|
+
{
|
|
272
|
+
type: :gallery, name: widget["Name"], options: options,
|
|
273
|
+
children: children, events: []
|
|
274
|
+
}
|
|
275
|
+
end
|
|
276
|
+
|
|
224
277
|
def native_widget(widget)
|
|
225
278
|
deep = widget.reject { |key, _| %w[$ID $Type Name].include?(key.to_s) }
|
|
279
|
+
widget_id = widget.dig("Type", "WidgetId").to_s
|
|
280
|
+
platform = widget.dig("Type", "SupportedPlatform").to_s
|
|
281
|
+
options = { native_type: widget["$Type"], deep_structure: deep }
|
|
282
|
+
options[:widget_id] = widget_id unless widget_id.empty?
|
|
283
|
+
options[:platform] = platform unless platform.empty?
|
|
226
284
|
{
|
|
227
285
|
type: :native_widget, name: widget["Name"] || "widget",
|
|
228
|
-
options
|
|
286
|
+
options:, events: []
|
|
229
287
|
}
|
|
230
288
|
end
|
|
231
289
|
|
|
232
290
|
def data_grid2_widget(widget)
|
|
233
291
|
properties = custom_property_map(widget["Object"], widget.dig("Type", "ObjectType"))
|
|
292
|
+
return data_grid_widget(widget) if properties.empty? && widget["DataSource"]
|
|
293
|
+
|
|
234
294
|
entity = properties.dig("datasource", "DataSource", "EntityRef", "Entity")
|
|
235
295
|
column_type = custom_property_type(widget.dig("Type", "ObjectType"), "columns")
|
|
236
296
|
&.dig("ValueType", "ObjectType")
|
|
@@ -244,12 +304,35 @@ module Mxrb
|
|
|
244
304
|
end
|
|
245
305
|
{
|
|
246
306
|
type: :data_grid, name: widget["Name"],
|
|
247
|
-
options: { entity: entity, columns: columns }.compact, events:
|
|
307
|
+
options: { entity: entity, columns: columns }.compact, events: grid_events(widget)
|
|
248
308
|
}
|
|
249
309
|
end
|
|
250
310
|
|
|
311
|
+
def grid_events(widget)
|
|
312
|
+
{
|
|
313
|
+
"OnChangeAction" => :on_change,
|
|
314
|
+
"OnClickAction" => :on_click,
|
|
315
|
+
"Action" => :on_click
|
|
316
|
+
}.filter_map do |property, event|
|
|
317
|
+
action = parse_action(widget[property])
|
|
318
|
+
action&.merge(event: event)
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
251
322
|
def combo_box_widget(widget)
|
|
252
323
|
properties = custom_property_map(widget["Object"], widget.dig("Type", "ObjectType"))
|
|
324
|
+
if properties.empty? && widget["AttributePath"]
|
|
325
|
+
reference = widget["SelectorType"] == "Reference"
|
|
326
|
+
options = {
|
|
327
|
+
attribute: widget["AttributePath"], caption: extract_text(widget["LabelText"]),
|
|
328
|
+
display_attribute: widget["DisplayAttribute"]
|
|
329
|
+
}.compact
|
|
330
|
+
return {
|
|
331
|
+
type: reference ? :reference_selector : :drop_down,
|
|
332
|
+
name: widget["Name"], options: options, events: grid_events(widget)
|
|
333
|
+
}
|
|
334
|
+
end
|
|
335
|
+
|
|
253
336
|
association = parse_array(properties.dig("attributeAssociation", "EntityRef", "Steps"))
|
|
254
337
|
.first&.fetch("Association", nil)
|
|
255
338
|
reference = properties.dig("optionsSourceType", "PrimitiveValue") == "association"
|
|
@@ -267,7 +350,7 @@ module Mxrb
|
|
|
267
350
|
)
|
|
268
351
|
end
|
|
269
352
|
{ type: reference ? :reference_selector : :drop_down,
|
|
270
|
-
name: widget["Name"], options: options.compact, events:
|
|
353
|
+
name: widget["Name"], options: options.compact, events: grid_events(widget) }
|
|
271
354
|
end
|
|
272
355
|
|
|
273
356
|
def custom_property_map(object, object_type)
|
|
@@ -376,21 +459,49 @@ module Mxrb
|
|
|
376
459
|
def parse_source(source)
|
|
377
460
|
return nil unless source.is_a?(Hash)
|
|
378
461
|
result = if %w[Pages$NanoflowSource Forms$NanoflowSource].include?(source["$Type"])
|
|
379
|
-
{ kind: :nanoflow, name:
|
|
462
|
+
{ kind: :nanoflow, name: source_name(source["Nanoflow"] || source.dig("NanoflowSettings", "Nanoflow")) }
|
|
380
463
|
else
|
|
381
|
-
{ kind: :microflow, name:
|
|
464
|
+
{ kind: :microflow, name: source_name(source["Microflow"] || source.dig("MicroflowSettings", "Microflow")) }
|
|
382
465
|
end
|
|
383
466
|
result[:name].to_s.empty? ? nil : result
|
|
384
467
|
end
|
|
385
468
|
|
|
469
|
+
# Page data sources are architectural references. Unlike widget-local
|
|
470
|
+
# action handlers, their module qualification must survive export so a
|
|
471
|
+
# native Core.Flow reference cannot silently become Portal.Flow.
|
|
472
|
+
def source_name(name)
|
|
473
|
+
name.to_s
|
|
474
|
+
end
|
|
475
|
+
|
|
386
476
|
def parse_action(action)
|
|
387
477
|
return nil unless action.is_a?(Hash)
|
|
388
478
|
if %w[Pages$CallNanoflowClientAction Forms$CallNanoflowClientAction].include?(action["$Type"])
|
|
389
|
-
|
|
390
|
-
handler
|
|
479
|
+
settings = action["NanoflowSettings"] || action
|
|
480
|
+
handler = local_name(action["Nanoflow"] || settings["Nanoflow"])
|
|
481
|
+
return nil if handler.to_s.empty?
|
|
482
|
+
|
|
483
|
+
arguments = parse_array(settings["ParameterMappings"]).to_h do |mapping|
|
|
484
|
+
[local_name(mapping["Parameter"]), mapping["Expression"].to_s]
|
|
485
|
+
end
|
|
486
|
+
{ kind: :nanoflow, handler: handler, arguments: arguments }
|
|
391
487
|
elsif %w[Pages$MicroflowClientAction Forms$MicroflowAction Forms$MicroflowClientAction].include?(action["$Type"])
|
|
392
|
-
|
|
393
|
-
handler
|
|
488
|
+
settings = action["MicroflowSettings"] || action
|
|
489
|
+
handler = local_name(action["Microflow"] || settings["Microflow"])
|
|
490
|
+
return nil if handler.to_s.empty?
|
|
491
|
+
|
|
492
|
+
arguments = parse_array(settings["ParameterMappings"]).to_h do |mapping|
|
|
493
|
+
[local_name(mapping["Parameter"]), mapping["Expression"].to_s]
|
|
494
|
+
end
|
|
495
|
+
{ kind: :microflow, handler: handler, arguments: arguments }
|
|
496
|
+
elsif %w[Pages$FormAction Forms$FormAction].include?(action["$Type"])
|
|
497
|
+
settings = action["FormSettings"] || action
|
|
498
|
+
handler = settings["Form"].to_s
|
|
499
|
+
return nil if handler.empty?
|
|
500
|
+
|
|
501
|
+
arguments = parse_array(settings["ParameterMappings"]).to_h do |mapping|
|
|
502
|
+
[local_name(mapping["Parameter"]), mapping["Expression"].to_s]
|
|
503
|
+
end
|
|
504
|
+
{ kind: :page, handler: handler, arguments: arguments }
|
|
394
505
|
elsif %w[Pages$SaveChangesClientAction Forms$SaveChangesClientAction].include?(action["$Type"])
|
|
395
506
|
{ kind: :action, handler: "save_changes" }
|
|
396
507
|
elsif %w[Pages$CancelChangesClientAction Forms$CancelChangesClientAction].include?(action["$Type"])
|
data/lib/mxrb/model/project.rb
CHANGED
|
@@ -63,6 +63,9 @@ module Mxrb
|
|
|
63
63
|
analyzer = Oql::Analyzer.new(dialect:)
|
|
64
64
|
oql_queries.map { analyzer.analyze(_1) }.freeze
|
|
65
65
|
end
|
|
66
|
+
def sql_to_oql(source, dialect: :postgresql)
|
|
67
|
+
Oql::ReverseTranslator.new(dialect:, project: self).translate(source)
|
|
68
|
+
end
|
|
66
69
|
|
|
67
70
|
# ── Semantic analysis ───────────────────────────────────────────────
|
|
68
71
|
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mxrb
|
|
4
|
+
module Modeler
|
|
5
|
+
# Read-only, JSON-safe projection used by the React project modeler.
|
|
6
|
+
# rubocop:disable Metrics
|
|
7
|
+
class Catalog
|
|
8
|
+
SETTINGS_PATTERN = /(?:Settings|Configuration)\$|(?:Settings|Configuration)\z/
|
|
9
|
+
|
|
10
|
+
def initialize(path)
|
|
11
|
+
@path = File.expand_path(path)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_h
|
|
15
|
+
project = Model::Project.open(@path)
|
|
16
|
+
documents = project_documents(project)
|
|
17
|
+
modules = project.modules.map { module_payload(_1) }
|
|
18
|
+
{
|
|
19
|
+
project: project_payload(project),
|
|
20
|
+
summary: summary(modules),
|
|
21
|
+
modules:,
|
|
22
|
+
navigation: project.navigation.to_h,
|
|
23
|
+
security: security_payload(documents),
|
|
24
|
+
settings: settings_payload(documents)
|
|
25
|
+
}
|
|
26
|
+
ensure
|
|
27
|
+
project&.close
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def project_payload(project)
|
|
33
|
+
{
|
|
34
|
+
name: project.name,
|
|
35
|
+
mendix_version: project.mendix_version,
|
|
36
|
+
format_version: project.format_version
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def summary(modules)
|
|
41
|
+
keys = %i[entities pages microflows nanoflows integrations configurations]
|
|
42
|
+
{ modules: modules.size }.merge(
|
|
43
|
+
keys.to_h { |key| [key, modules.sum { Array(_1[key]).size }] }
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def module_payload(mod)
|
|
48
|
+
documents = catalog_documents(mod)
|
|
49
|
+
integration_groups = %w[endpoints integrations mappings]
|
|
50
|
+
{
|
|
51
|
+
id: mod.id,
|
|
52
|
+
name: mod.name,
|
|
53
|
+
marketplace: mod.from_app_store == true,
|
|
54
|
+
marketplace_guid: blank_to_nil(mod.app_store_guid),
|
|
55
|
+
marketplace_version: blank_to_nil(mod.app_store_version),
|
|
56
|
+
entities: mod.entities.map { entity_payload(_1, mod.name) },
|
|
57
|
+
pages: mod.pages.map { page_payload(_1, mod.name) },
|
|
58
|
+
microflows: mod.microflows.map { flow_payload(_1, mod.name, 'microflow') },
|
|
59
|
+
nanoflows: mod.nanoflows.map { flow_payload(_1, mod.name, 'nanoflow') },
|
|
60
|
+
module_roles: mod.module_roles,
|
|
61
|
+
integrations: documents.select { integration_groups.include?(_1[:group]) },
|
|
62
|
+
configurations: documents.reject { integration_groups.include?(_1[:group]) }
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def entity_payload(entity, module_name)
|
|
67
|
+
{
|
|
68
|
+
id: entity.id,
|
|
69
|
+
name: entity.name,
|
|
70
|
+
qualified_name: entity.qualified_name || "#{module_name}.#{entity.name}",
|
|
71
|
+
persistent: entity.persistable == true,
|
|
72
|
+
attributes: entity.attributes.map do |attribute|
|
|
73
|
+
{ name: attribute.name, type: attribute.type.to_s, required: attribute.required == true }
|
|
74
|
+
end
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def page_payload(page, module_name)
|
|
79
|
+
widgets = flatten_widgets(page.widgets)
|
|
80
|
+
{
|
|
81
|
+
id: page.id,
|
|
82
|
+
name: page.name,
|
|
83
|
+
qualified_name: "#{module_name}.#{page.name}",
|
|
84
|
+
title: page.title,
|
|
85
|
+
url: page.url,
|
|
86
|
+
layout_id: page.layout_id,
|
|
87
|
+
excluded: page.excluded == true,
|
|
88
|
+
allowed_module_roles: page.allowed_module_roles.map(&:to_s),
|
|
89
|
+
widget_count: widgets.size,
|
|
90
|
+
widget_types: widgets.filter_map { _1[:type]&.to_s }.tally
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def flow_payload(flow, module_name, kind)
|
|
95
|
+
{
|
|
96
|
+
id: flow.id,
|
|
97
|
+
name: flow.name,
|
|
98
|
+
qualified_name: "#{module_name}.#{flow.name}",
|
|
99
|
+
kind:,
|
|
100
|
+
documentation: flow.documentation.to_s,
|
|
101
|
+
allowed_module_roles: flow.allowed_module_roles.map(&:to_s),
|
|
102
|
+
parameter_count: flow.parameters.size,
|
|
103
|
+
object_count: flow.objects.size,
|
|
104
|
+
flow_count: flow.flows.size
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def flatten_widgets(widgets)
|
|
109
|
+
Array(widgets).flat_map do |widget|
|
|
110
|
+
next [] unless widget.is_a?(Hash)
|
|
111
|
+
|
|
112
|
+
[widget, *flatten_widgets(widget[:children] || widget['children'])]
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def catalog_documents(mod)
|
|
117
|
+
documents = mod.infrastructure_documents + mod.application_documents + mod.domain_documents
|
|
118
|
+
documents.map do |document|
|
|
119
|
+
{
|
|
120
|
+
id: document[:id],
|
|
121
|
+
name: document[:name].to_s,
|
|
122
|
+
type: document[:type],
|
|
123
|
+
group: document[:route].to_s.split('/').first
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def project_documents(project)
|
|
129
|
+
project.all_units.filter_map do |unit|
|
|
130
|
+
document = project.parse_bson(unit)
|
|
131
|
+
type = document['$Type'].to_s
|
|
132
|
+
next if type.empty?
|
|
133
|
+
|
|
134
|
+
{ id: unit['UnitID'], type:, document: }
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def security_payload(documents)
|
|
139
|
+
source = documents.find { _1[:type] == 'Security$ProjectSecurity' }&.fetch(:document)
|
|
140
|
+
return { configured: false, level: nil, user_roles: [] } unless source
|
|
141
|
+
|
|
142
|
+
roles = array(source['UserRoles']).map do |role|
|
|
143
|
+
{
|
|
144
|
+
name: role['Name'].to_s,
|
|
145
|
+
description: role['Description'].to_s,
|
|
146
|
+
module_roles: array(role['ModuleRoles']).map(&:to_s)
|
|
147
|
+
}
|
|
148
|
+
end
|
|
149
|
+
{ configured: true, level: source['SecurityLevel'], user_roles: roles }
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def settings_payload(documents)
|
|
153
|
+
documents.filter { _1[:type].match?(SETTINGS_PATTERN) }.map do |entry|
|
|
154
|
+
document = entry[:document]
|
|
155
|
+
{
|
|
156
|
+
id: entry[:id],
|
|
157
|
+
type: entry[:type],
|
|
158
|
+
name: document['Name'].to_s,
|
|
159
|
+
values: scalar_values(document)
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def scalar_values(document)
|
|
165
|
+
document.each_with_object({}) do |(key, value), result|
|
|
166
|
+
next if key.start_with?('$') || value.is_a?(Hash) || value.is_a?(Array)
|
|
167
|
+
|
|
168
|
+
result[key] = value
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def array(value) = IO::BsonCodec.parse_array(value)[:items]
|
|
173
|
+
|
|
174
|
+
def blank_to_nil(value) = value.to_s.empty? ? nil : value
|
|
175
|
+
end
|
|
176
|
+
# rubocop:enable Metrics
|
|
177
|
+
end
|
|
178
|
+
end
|
|
@@ -122,6 +122,7 @@ module Mxrb
|
|
|
122
122
|
def install(staged_files, protected_files = [])
|
|
123
123
|
staged_files.each do |relative, source|
|
|
124
124
|
install_file(relative, source) unless protected_files.include?(relative)
|
|
125
|
+
yield(relative) if block_given?
|
|
125
126
|
end
|
|
126
127
|
end
|
|
127
128
|
|
|
@@ -179,9 +180,12 @@ module Mxrb
|
|
|
179
180
|
end
|
|
180
181
|
|
|
181
182
|
def import!
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
Progress.with("Importing #{File.basename(@package_path)}") do |progress|
|
|
184
|
+
validate_inputs!
|
|
185
|
+
progress.update(detail: 'reading package')
|
|
186
|
+
Dir.mktmpdir('mxrb-module-import-') do |temporary|
|
|
187
|
+
Zip::File.open(@package_path) { import_archive(_1, temporary, progress) }
|
|
188
|
+
end
|
|
185
189
|
end
|
|
186
190
|
rescue Zip::Error, REXML::ParseException => e
|
|
187
191
|
raise MarketplaceError, "invalid Mendix module package: #{e.message}"
|
|
@@ -189,12 +193,12 @@ module Mxrb
|
|
|
189
193
|
|
|
190
194
|
private
|
|
191
195
|
|
|
192
|
-
def import_archive(archive, temporary)
|
|
196
|
+
def import_archive(archive, temporary, progress)
|
|
193
197
|
reader = ModulePackageReader.new(archive)
|
|
194
198
|
descriptor = reader.descriptor
|
|
195
199
|
source_path = reader.extract_project(descriptor, temporary)
|
|
196
200
|
staged_files = reader.stage_files(descriptor.files, temporary)
|
|
197
|
-
import_project(source_path, descriptor, staged_files, temporary)
|
|
201
|
+
import_project(source_path, descriptor, staged_files, temporary, progress)
|
|
198
202
|
end
|
|
199
203
|
|
|
200
204
|
def validate_inputs!
|
|
@@ -203,12 +207,13 @@ module Mxrb
|
|
|
203
207
|
raise MarketplaceError, "target root not found: #{@target_root}" unless File.directory?(@target_root)
|
|
204
208
|
end
|
|
205
209
|
|
|
206
|
-
def import_project(source_path, descriptor, staged_files, temporary) # rubocop:disable Metrics/MethodLength
|
|
210
|
+
def import_project(source_path, descriptor, staged_files, temporary, progress) # rubocop:disable Metrics/MethodLength
|
|
207
211
|
source = IO::MprFile.open(source_path, readonly: true)
|
|
208
212
|
target = IO::MprFile.open(@mpr_path)
|
|
209
213
|
assets = ModulePackageAssets.new(@target_root, temporary)
|
|
210
214
|
imported_ids = []
|
|
211
|
-
import_transaction(source, target, descriptor, staged_files, assets, imported_ids)
|
|
215
|
+
import_transaction(source, target, descriptor, staged_files, assets, imported_ids, progress)
|
|
216
|
+
progress.advance(detail: 'import transaction')
|
|
212
217
|
import_result(source, target, descriptor, staged_files, imported_ids)
|
|
213
218
|
rescue StandardError
|
|
214
219
|
assets&.rollback
|
|
@@ -220,24 +225,30 @@ module Mxrb
|
|
|
220
225
|
end
|
|
221
226
|
|
|
222
227
|
# rubocop:disable Metrics/ParameterLists
|
|
223
|
-
def import_transaction(source, target, descriptor, staged_files, assets, imported_ids)
|
|
228
|
+
def import_transaction(source, target, descriptor, staged_files, assets, imported_ids, progress) # rubocop:disable Metrics/MethodLength
|
|
224
229
|
validate_versions!(source, target, descriptor)
|
|
225
230
|
module_unit, units = package_units(source, descriptor.name)
|
|
226
231
|
validate_target!(target, descriptor.name, units)
|
|
232
|
+
progress.update(
|
|
233
|
+
current: 2, total: units.size + staged_files.size + 3,
|
|
234
|
+
detail: "#{units.size} model units"
|
|
235
|
+
)
|
|
227
236
|
target.transaction do
|
|
228
|
-
imported_ids.concat(insert_units(source, target, module_unit, units))
|
|
229
|
-
assets.install(staged_files, @protected_files)
|
|
237
|
+
imported_ids.concat(insert_units(source, target, module_unit, units, progress))
|
|
238
|
+
assets.install(staged_files, @protected_files) do |relative|
|
|
239
|
+
progress.advance(detail: "asset #{relative}")
|
|
240
|
+
end
|
|
230
241
|
end
|
|
231
242
|
end
|
|
232
243
|
# rubocop:enable Metrics/ParameterLists
|
|
233
244
|
|
|
234
|
-
def insert_units(source, target, module_unit, units)
|
|
245
|
+
def insert_units(source, target, module_unit, units, progress)
|
|
235
246
|
units.map do |unit|
|
|
236
247
|
container = unit == module_unit ? target.root_unit.fetch('UnitID') : unit.fetch('ContainerID')
|
|
237
248
|
target.insert_unit(
|
|
238
249
|
container_uuid: container, containment_name: unit.fetch('ContainmentName'),
|
|
239
250
|
contents_doc: source.parse_contents(unit)
|
|
240
|
-
)
|
|
251
|
+
).tap { progress.advance(detail: "unit #{unit.fetch('UnitID')}") }
|
|
241
252
|
end
|
|
242
253
|
end
|
|
243
254
|
|
|
@@ -200,17 +200,21 @@ module Mxrb
|
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
def install(archive, package)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
203
|
+
Progress.with("Installing widget #{package.name}") do |progress|
|
|
204
|
+
validate_target!
|
|
205
|
+
progress.update(detail: 'reading widget package')
|
|
206
|
+
inventory = WidgetPackageInventory.read(archive)
|
|
207
|
+
validate_identity!(inventory, package)
|
|
208
|
+
digest = Digest::SHA256.file(archive).hexdigest
|
|
209
|
+
destination = safe_path(File.join('widgets', inventory.project_filename))
|
|
210
|
+
cache = safe_path(cache_relative(inventory, package))
|
|
211
|
+
lock_path = safe_path(File.join('.mxrb', 'marketplace.lock.json'))
|
|
212
|
+
current = validate_boundaries!(package, destination, cache, lock_path)
|
|
213
|
+
progress.update(detail: 'installing project assets')
|
|
214
|
+
install_transaction(
|
|
215
|
+
archive, package, inventory, digest, destination, cache, lock_path, current
|
|
216
|
+
)
|
|
217
|
+
end
|
|
214
218
|
end
|
|
215
219
|
|
|
216
220
|
private
|
|
@@ -223,14 +223,20 @@ module Mxrb
|
|
|
223
223
|
end
|
|
224
224
|
|
|
225
225
|
def json(url, authorization: default_authorization)
|
|
226
|
-
|
|
226
|
+
Progress.with('Loading Marketplace data') do |progress|
|
|
227
|
+
progress.update(detail: URI.parse(url).host)
|
|
228
|
+
JSON.parse(get(url, accept: 'application/json', authorization:))
|
|
229
|
+
end
|
|
227
230
|
rescue JSON::ParserError => e
|
|
228
231
|
raise MarketplaceError, "invalid JSON response: #{e.message}"
|
|
229
232
|
end
|
|
230
233
|
|
|
231
234
|
def download(url, destination, authorization: default_authorization)
|
|
232
|
-
File.
|
|
233
|
-
|
|
235
|
+
Progress.with("Downloading #{File.basename(destination)}") do |progress|
|
|
236
|
+
progress.update(detail: URI.parse(url).host)
|
|
237
|
+
File.binwrite(destination, get(url, accept: 'application/octet-stream', authorization:))
|
|
238
|
+
destination
|
|
239
|
+
end
|
|
234
240
|
end
|
|
235
241
|
|
|
236
242
|
private
|