mxrb 0.1.0
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 +7 -0
- data/LICENSE +21 -0
- data/README.de-DE.md +108 -0
- data/README.md +172 -0
- data/README.pt-BR.md +109 -0
- data/bin/mxrb +1613 -0
- data/docs/README.md +10 -0
- data/docs/de-DE/README.md +24 -0
- data/docs/de-DE/architectural-patterns.md +26 -0
- data/docs/de-DE/architectural-standard.md +48 -0
- data/docs/de-DE/architecture.md +76 -0
- data/docs/de-DE/compiler.md +162 -0
- data/docs/de-DE/conventions.md +19 -0
- data/docs/de-DE/design-system.md +52 -0
- data/docs/de-DE/entity-dsl.md +53 -0
- data/docs/de-DE/native-runtime-quality-report.md +74 -0
- data/docs/de-DE/oql-sql.md +211 -0
- data/docs/de-DE/platform-api-integration.md +36 -0
- data/docs/de-DE/platform-operations.md +243 -0
- data/docs/de-DE/project-structure.md +32 -0
- data/docs/de-DE/ruby-first-roadmap.md +116 -0
- data/docs/de-DE/scaffolds.md +59 -0
- data/docs/de-DE/semantic-refactoring.md +56 -0
- data/docs/de-DE/team-server.md +33 -0
- data/docs/de-DE/validation-matrix.md +64 -0
- data/docs/de-DE/vetclinic-acceptance.md +24 -0
- data/docs/de-DE/writing.md +138 -0
- data/docs/en-US/README.md +24 -0
- data/docs/en-US/architectural-patterns.md +151 -0
- data/docs/en-US/architectural-standard.md +89 -0
- data/docs/en-US/architecture.md +170 -0
- data/docs/en-US/compiler.md +157 -0
- data/docs/en-US/conventions.md +82 -0
- data/docs/en-US/design-system.md +163 -0
- data/docs/en-US/entity-dsl.md +53 -0
- data/docs/en-US/native-runtime-quality-report.md +73 -0
- data/docs/en-US/oql-sql.md +201 -0
- data/docs/en-US/platform-api-integration.md +36 -0
- data/docs/en-US/platform-operations.md +244 -0
- data/docs/en-US/project-structure.md +49 -0
- data/docs/en-US/ruby-first-roadmap.md +186 -0
- data/docs/en-US/scaffolds.md +57 -0
- data/docs/en-US/semantic-refactoring.md +198 -0
- data/docs/en-US/team-server.md +32 -0
- data/docs/en-US/validation-matrix.md +257 -0
- data/docs/en-US/vetclinic-acceptance.md +23 -0
- data/docs/en-US/writing.md +655 -0
- data/docs/pt-BR/README.md +24 -0
- data/docs/pt-BR/architectural-patterns.md +25 -0
- data/docs/pt-BR/architectural-standard.md +533 -0
- data/docs/pt-BR/architecture.md +300 -0
- data/docs/pt-BR/compiler.md +162 -0
- data/docs/pt-BR/conventions.md +19 -0
- data/docs/pt-BR/design-system.md +65 -0
- data/docs/pt-BR/entity-dsl.md +116 -0
- data/docs/pt-BR/native-runtime-quality-report.md +73 -0
- data/docs/pt-BR/oql-sql.md +208 -0
- data/docs/pt-BR/platform-api-integration.md +36 -0
- data/docs/pt-BR/platform-operations.md +260 -0
- data/docs/pt-BR/project-structure.md +32 -0
- data/docs/pt-BR/ruby-first-roadmap.md +239 -0
- data/docs/pt-BR/scaffolds.md +104 -0
- data/docs/pt-BR/semantic-refactoring.md +55 -0
- data/docs/pt-BR/team-server.md +59 -0
- data/docs/pt-BR/validation-matrix.md +86 -0
- data/docs/pt-BR/vetclinic-acceptance.md +50 -0
- data/docs/pt-BR/writing.md +177 -0
- data/examples/sudoku_evaluation.rb +14 -0
- data/examples/sudoku_functional_test.rb +28 -0
- data/lib/mxrb/architecture/graph.rb +124 -0
- data/lib/mxrb/architecture/validator.rb +98 -0
- data/lib/mxrb/benchmark.rb +33 -0
- data/lib/mxrb/compare.rb +390 -0
- data/lib/mxrb/compiler/adapter.rb +96 -0
- data/lib/mxrb/compiler/artifact_document_compiler.rb +102 -0
- data/lib/mxrb/compiler/artifact_materializer.rb +53 -0
- data/lib/mxrb/compiler/client_model_materializer.rb +42 -0
- data/lib/mxrb/compiler/code_action_document_compiler.rb +64 -0
- data/lib/mxrb/compiler/code_action_materializer.rb +35 -0
- data/lib/mxrb/compiler/code_action_type_compiler.rb +32 -0
- data/lib/mxrb/compiler/combo_box_bundle_compiler.rb +314 -0
- data/lib/mxrb/compiler/compatibility_analyzer.rb +169 -0
- data/lib/mxrb/compiler/constants_materializer.rb +111 -0
- data/lib/mxrb/compiler/data_grid_bundle_compiler.rb +290 -0
- data/lib/mxrb/compiler/database_connector_action_compiler.rb +280 -0
- data/lib/mxrb/compiler/deployment_asset_copier.rb +60 -0
- data/lib/mxrb/compiler/deployment_bootstrapper.rb +208 -0
- data/lib/mxrb/compiler/deployment_materializer.rb +43 -0
- data/lib/mxrb/compiler/deployment_metadata.rb +64 -0
- data/lib/mxrb/compiler/domain_document_compiler.rb +153 -0
- data/lib/mxrb/compiler/domain_model_materializer.rb +35 -0
- data/lib/mxrb/compiler/domain_security_compiler.rb +82 -0
- data/lib/mxrb/compiler/gallery_bundle_compiler.rb +190 -0
- data/lib/mxrb/compiler/generic_widget_bundle_compiler.rb +281 -0
- data/lib/mxrb/compiler/image_bundle_compiler.rb +216 -0
- data/lib/mxrb/compiler/java_proxy_generator.rb +509 -0
- data/lib/mxrb/compiler/legacy_data_grid_compiler.rb +267 -0
- data/lib/mxrb/compiler/legacy_page_builder.rb +287 -0
- data/lib/mxrb/compiler/mda.rb +99 -0
- data/lib/mxrb/compiler/microflow_document_compiler.rb +85 -0
- data/lib/mxrb/compiler/microflow_materializer.rb +48 -0
- data/lib/mxrb/compiler/microflow_node_compiler.rb +250 -0
- data/lib/mxrb/compiler/model_package.rb +116 -0
- data/lib/mxrb/compiler/model_values.rb +67 -0
- data/lib/mxrb/compiler/nanoflow_program_compiler.rb +483 -0
- data/lib/mxrb/compiler/navigation_document_compiler.rb +93 -0
- data/lib/mxrb/compiler/packager.rb +100 -0
- data/lib/mxrb/compiler/page_bundle_builder.rb +56 -0
- data/lib/mxrb/compiler/page_bundle_compiler.rb +2004 -0
- data/lib/mxrb/compiler/page_document_compiler.rb +77 -0
- data/lib/mxrb/compiler/portable_packager.rb +356 -0
- data/lib/mxrb/compiler/project_jar_archive.rb +84 -0
- data/lib/mxrb/compiler/project_jar_builder.rb +121 -0
- data/lib/mxrb/compiler/project_materializer.rb +139 -0
- data/lib/mxrb/compiler/project_model_orderer.rb +52 -0
- data/lib/mxrb/compiler/runtime_data_types.rb +31 -0
- data/lib/mxrb/compiler/runtime_model_schema.rb +90 -0
- data/lib/mxrb/compiler/schemas/runtime-10.24.0.73019.json +1052 -0
- data/lib/mxrb/compiler/schemas/runtime-11.json +1215 -0
- data/lib/mxrb/compiler/schemas/runtime-6.10.8.json +1148 -0
- data/lib/mxrb/compiler/schemas/runtime-7.17.0.json +1144 -0
- data/lib/mxrb/compiler/schemas/runtime-7.5.0.json +1215 -0
- data/lib/mxrb/compiler/schemas/runtime-9.6.1.29396.json +868 -0
- data/lib/mxrb/compiler/schemas/system-model-10.24.0.73019.b64 +1135 -0
- data/lib/mxrb/compiler/schemas/system-model-11.12.1.b64 +815 -0
- data/lib/mxrb/compiler/schemas/system-model-6.10.8.b64 +497 -0
- data/lib/mxrb/compiler/schemas/system-model-7.17.0.b64 +519 -0
- data/lib/mxrb/compiler/schemas/system-model-7.5.0.b64 +504 -0
- data/lib/mxrb/compiler/schemas/system-model-9.6.1.29396.b64 +816 -0
- data/lib/mxrb/compiler/security_materializer.rb +104 -0
- data/lib/mxrb/compiler/settings_document_compiler.rb +45 -0
- data/lib/mxrb/compiler/settings_materializer.rb +25 -0
- data/lib/mxrb/compiler/source_model.rb +128 -0
- data/lib/mxrb/compiler/system_model_seed.rb +88 -0
- data/lib/mxrb/compiler/system_queue_materializer.rb +54 -0
- data/lib/mxrb/compiler/system_text_materializer.rb +49 -0
- data/lib/mxrb/compiler/translation_materializer.rb +95 -0
- data/lib/mxrb/compiler/web_bundle_builder.rb +155 -0
- data/lib/mxrb/compiler/web_list_data_source.rb +139 -0
- data/lib/mxrb/compiler/web_operation_compiler.rb +614 -0
- data/lib/mxrb/compiler/web_shell_materializer.rb +194 -0
- data/lib/mxrb/compiler/widget_package_extractor.rb +63 -0
- data/lib/mxrb/doctor.rb +116 -0
- data/lib/mxrb/dsl/builder.rb +1636 -0
- data/lib/mxrb/errors.rb +18 -0
- data/lib/mxrb/evaluation.rb +131 -0
- data/lib/mxrb/exporter.rb +1716 -0
- data/lib/mxrb/frontend/migrator.rb +775 -0
- data/lib/mxrb/functional.rb +307 -0
- data/lib/mxrb/github/annotator.rb +161 -0
- data/lib/mxrb/initializer.rb +257 -0
- data/lib/mxrb/integrity/validator.rb +136 -0
- data/lib/mxrb/io/bson_codec.rb +145 -0
- data/lib/mxrb/io/mpr_file.rb +558 -0
- data/lib/mxrb/io/mxunit_codec.rb +42 -0
- data/lib/mxrb/marketplace.rb +380 -0
- data/lib/mxrb/model/association.rb +98 -0
- data/lib/mxrb/model/attribute.rb +100 -0
- data/lib/mxrb/model/connector.rb +17 -0
- data/lib/mxrb/model/design_materializer.rb +133 -0
- data/lib/mxrb/model/design_migration.rb +126 -0
- data/lib/mxrb/model/design_system.rb +111 -0
- data/lib/mxrb/model/domain_model.rb +51 -0
- data/lib/mxrb/model/entity.rb +180 -0
- data/lib/mxrb/model/menu.rb +38 -0
- data/lib/mxrb/model/microflow.rb +100 -0
- data/lib/mxrb/model/module.rb +142 -0
- data/lib/mxrb/model/navigation.rb +127 -0
- data/lib/mxrb/model/page.rb +410 -0
- data/lib/mxrb/model/project.rb +187 -0
- data/lib/mxrb/model/unit.rb +48 -0
- data/lib/mxrb/module_initializer.rb +75 -0
- data/lib/mxrb/official_marketplace/content_api.rb +292 -0
- data/lib/mxrb/official_marketplace/dependency_resolver.rb +329 -0
- data/lib/mxrb/official_marketplace/lifecycle.rb +367 -0
- data/lib/mxrb/official_marketplace/module_package_importer.rb +318 -0
- data/lib/mxrb/official_marketplace/widget_package_installer.rb +414 -0
- data/lib/mxrb/official_marketplace.rb +720 -0
- data/lib/mxrb/oql/analyzer.rb +155 -0
- data/lib/mxrb/oql/index_advisor.rb +85 -0
- data/lib/mxrb/oql/plan_analyzer.rb +185 -0
- data/lib/mxrb/oql/server.rb +132 -0
- data/lib/mxrb/oql/sql_server_plan_analyzer.rb +190 -0
- data/lib/mxrb/oql/sql_server_workload_analyzer.rb +80 -0
- data/lib/mxrb/oql/workload_analyzer.rb +175 -0
- data/lib/mxrb/oql/workload_baseline.rb +60 -0
- data/lib/mxrb/oql.rb +424 -0
- data/lib/mxrb/project_lifecycle.rb +76 -0
- data/lib/mxrb/protocols/plan.rb +86 -0
- data/lib/mxrb/protocols.rb +123 -0
- data/lib/mxrb/runtime/database_workspace.rb +677 -0
- data/lib/mxrb/runtime/docker_executor.rb +41 -0
- data/lib/mxrb/runtime/docker_workspace.rb +29 -0
- data/lib/mxrb/runtime/executor.rb +169 -0
- data/lib/mxrb/runtime/java_locator.rb +26 -0
- data/lib/mxrb/runtime/native.rb +474 -0
- data/lib/mxrb/runtime/sql_server_database.rb +146 -0
- data/lib/mxrb/runtime/toolchain.rb +82 -0
- data/lib/mxrb/scaffold/cli.rb +113 -0
- data/lib/mxrb/scaffold/generator.rb +246 -0
- data/lib/mxrb/scaffold/help.rb +80 -0
- data/lib/mxrb/scaffold/page_templates.rb +54 -0
- data/lib/mxrb/scaffold/recipes.rb +198 -0
- data/lib/mxrb/scaffold/registry.rb +82 -0
- data/lib/mxrb/scaffold/templates.rb +723 -0
- data/lib/mxrb/scaffold/transaction.rb +84 -0
- data/lib/mxrb/schema/tables.rb +55 -0
- data/lib/mxrb/semantic/analyzer.rb +473 -0
- data/lib/mxrb/semantic/batch_plan.rb +68 -0
- data/lib/mxrb/semantic/domain_mutator.rb +419 -0
- data/lib/mxrb/semantic/embedder.rb +51 -0
- data/lib/mxrb/semantic/extractor.rb +290 -0
- data/lib/mxrb/semantic/index.rb +623 -0
- data/lib/mxrb/semantic/inliner.rb +216 -0
- data/lib/mxrb/semantic/mover.rb +153 -0
- data/lib/mxrb/semantic/onnx_embedder.rb +34 -0
- data/lib/mxrb/semantic/remover.rb +70 -0
- data/lib/mxrb/semantic/renamer.rb +211 -0
- data/lib/mxrb/semantic/tfidf_embedder.rb +46 -0
- data/lib/mxrb/semantic/vec_store.rb +75 -0
- data/lib/mxrb/team_server.rb +390 -0
- data/lib/mxrb/templates/project/10.24.0.73019.json +32 -0
- data/lib/mxrb/templates/project/11.12.1.json +32 -0
- data/lib/mxrb/templates/project/6.10.8.json +50 -0
- data/lib/mxrb/templates/project/7.17.0.json +50 -0
- data/lib/mxrb/templates/project/7.5.0.json +50 -0
- data/lib/mxrb/templates/project/9.6.1.29396.json +50 -0
- data/lib/mxrb/version.rb +5 -0
- data/lib/mxrb/widget_package.rb +338 -0
- data/lib/mxrb/widget_synchronizer.rb +31 -0
- data/lib/mxrb/writer.rb +3775 -0
- data/lib/mxrb.rb +188 -0
- data/marketplace/catalog.json +10 -0
- data/marketplace/modules/shared-kernel/domain/README.md +4 -0
- data/marketplace/modules/shared-kernel/module.rb +5 -0
- data/marketplace/modules/shared-kernel/mxrb-module.json +9 -0
- metadata +447 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'open3'
|
|
5
|
+
|
|
6
|
+
module Mxrb
|
|
7
|
+
module Compiler
|
|
8
|
+
WebBundleResult = Data.define(:directory, :files, :bytes)
|
|
9
|
+
|
|
10
|
+
# Generates the web-client entrypoint and runs the version-owned Rspack toolchain directly.
|
|
11
|
+
# rubocop:disable Metrics
|
|
12
|
+
class WebBundleBuilder
|
|
13
|
+
include ModelValues
|
|
14
|
+
|
|
15
|
+
def initialize(mpr_path, deployment:, mendix_home:)
|
|
16
|
+
@mpr_path = File.expand_path(mpr_path)
|
|
17
|
+
@deployment = File.expand_path(deployment)
|
|
18
|
+
@source = SourceModel.read(@mpr_path)
|
|
19
|
+
@version_root = version_root(mendix_home)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def build
|
|
23
|
+
web = File.join(@deployment, 'web')
|
|
24
|
+
profiles = Adapter.for(@source.version, source: @source).web_profiles
|
|
25
|
+
return LegacyPageBuilder.new(@source, web, profiles:).build unless profiles.include?(:react)
|
|
26
|
+
|
|
27
|
+
prepare(web)
|
|
28
|
+
run_bundler(web)
|
|
29
|
+
WebShellMaterializer.new(web, version: @source.version).materialize
|
|
30
|
+
result(web)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def run_bundler(web)
|
|
36
|
+
output, status = Open3.capture2e(environment(web), node, *bundler_arguments, chdir: web)
|
|
37
|
+
raise CompilationError, "#{bundler_name} web build failed:\n#{output}" unless status.success?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def prepare(web)
|
|
41
|
+
WidgetPackageExtractor.new(File.dirname(@mpr_path), web).extract
|
|
42
|
+
materialize_react_dom_compatibility(web)
|
|
43
|
+
WebOperationCompiler.new(@source).write(File.join(@deployment, 'model', 'operations.json'))
|
|
44
|
+
PageBundleBuilder.new(@source, web).build
|
|
45
|
+
write_entry(File.join(web, 'index.js'))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def materialize_react_dom_compatibility(web)
|
|
49
|
+
shim = File.join(web, 'react-dom-compat.mjs')
|
|
50
|
+
react_dom = File.join(@version_root, 'modeler', 'tools', 'node', 'node_modules',
|
|
51
|
+
'react-dom', 'index.js')
|
|
52
|
+
File.write(shim, <<~JS)
|
|
53
|
+
import * as ReactDOM from #{JSON.generate(react_dom)};
|
|
54
|
+
export * from #{JSON.generate(react_dom)};
|
|
55
|
+
export default ReactDOM;
|
|
56
|
+
export const findDOMNode = value => {
|
|
57
|
+
if (typeof Element !== "undefined" && value instanceof Element) return value;
|
|
58
|
+
const seen = new Set();
|
|
59
|
+
const pending = [value?._reactInternals, value?._reactInternalFiber].filter(Boolean);
|
|
60
|
+
while (pending.length) {
|
|
61
|
+
const fiber = pending.shift();
|
|
62
|
+
if (!fiber || seen.has(fiber)) continue;
|
|
63
|
+
seen.add(fiber);
|
|
64
|
+
if (typeof Element !== "undefined" && fiber.stateNode instanceof Element) {
|
|
65
|
+
return fiber.stateNode;
|
|
66
|
+
}
|
|
67
|
+
pending.push(fiber.child, fiber.sibling);
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
};
|
|
71
|
+
JS
|
|
72
|
+
config_path = File.join(web, 'rspack.config.mjs')
|
|
73
|
+
return unless File.file?(config_path)
|
|
74
|
+
|
|
75
|
+
config = File.read(config_path)
|
|
76
|
+
return if config.include?(JSON.generate(shim))
|
|
77
|
+
|
|
78
|
+
marker = 'alias: {'
|
|
79
|
+
replacement = "#{marker}\n \"react-dom\": #{JSON.generate(shim)},"
|
|
80
|
+
raise CompilationError, 'Rspack config has no resolve alias block' unless config.include?(marker)
|
|
81
|
+
|
|
82
|
+
File.write(config_path, config.sub(marker, replacement))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def version_root(path)
|
|
86
|
+
root = File.expand_path(path)
|
|
87
|
+
File.basename(root) == 'runtime' ? File.dirname(root) : root
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def result(web)
|
|
91
|
+
directory = File.join(web, 'dist')
|
|
92
|
+
files = Dir.glob(File.join(directory, '**', '*')).select { File.file?(_1) }
|
|
93
|
+
WebBundleResult.new(directory:, files: files.length, bytes: files.sum { File.size(_1) })
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def node = File.join(@version_root, 'modeler', 'tools', 'node', 'linux-x64', 'node')
|
|
97
|
+
def bundler_name = @source.version.to_i == 10 ? 'Rollup' : 'Rspack'
|
|
98
|
+
|
|
99
|
+
def bundler_arguments
|
|
100
|
+
return [File.join(node_modules, 'rollup', 'dist', 'bin', 'rollup'), '--config'] if @source.version.to_i == 10
|
|
101
|
+
|
|
102
|
+
[File.join(@version_root, 'modeler', 'tools', 'node', 'rspack-runner.mjs')]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def node_modules = File.join(@version_root, 'modeler', 'tools', 'node', 'node_modules')
|
|
106
|
+
|
|
107
|
+
def environment(web)
|
|
108
|
+
{
|
|
109
|
+
'NODE_ENV' => 'production', 'SOURCE_MAP_GENERATION' => 'disabled',
|
|
110
|
+
'SHOULD_GENERATE_EMBEDDED_INDEX' => 'false',
|
|
111
|
+
'MX_DEPLOYMENT_WEB_DIRECTORY' => web,
|
|
112
|
+
'MX_WEB_CLIENT_BUILD_LOG' => File.join(@deployment, 'log', 'mxrb-web-build.log'),
|
|
113
|
+
'NODE_PATH' => node_modules
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def write_entry(path)
|
|
118
|
+
payload = {
|
|
119
|
+
languages: languages, systemTexts: rendered_system_texts,
|
|
120
|
+
constants: { LAYOUT_SCOPE_ID_PREFIX: 'l' },
|
|
121
|
+
registerServiceWorker: false, enableServiceWorkerCaching: true
|
|
122
|
+
}
|
|
123
|
+
File.write(path, "import { startApp } from \"mendix\";\n\nstartApp(#{JSON.pretty_generate(payload)});\n")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def languages
|
|
127
|
+
values = translations.values.flat_map(&:keys).uniq.sort
|
|
128
|
+
values.empty? ? ['en_US'] : values
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def rendered_system_texts
|
|
132
|
+
translations.transform_values do |values|
|
|
133
|
+
languages.map { values.fetch(_1, '') }
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def translations
|
|
138
|
+
@translations ||= collect_translations
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def collect_translations
|
|
142
|
+
collection = @source.documents('Texts$SystemTextCollection').first
|
|
143
|
+
return {} unless collection
|
|
144
|
+
|
|
145
|
+
array(collection['SystemTexts']).to_h do |text|
|
|
146
|
+
translations = array(text.dig('Text', 'Items')).to_h do |item|
|
|
147
|
+
[item['LanguageCode'].to_s, item['Text'].to_s]
|
|
148
|
+
end
|
|
149
|
+
[text['InternalKey'].to_s, translations]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
# rubocop:enable Metrics
|
|
154
|
+
end
|
|
155
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mxrb
|
|
4
|
+
module Compiler
|
|
5
|
+
# Resolves React list data sources and safely lowers unusable simple connector reads to XPath.
|
|
6
|
+
# rubocop:disable Metrics
|
|
7
|
+
class WebListDataSource
|
|
8
|
+
include ModelValues
|
|
9
|
+
|
|
10
|
+
attr_reader :association_path, :entity, :microflow_name, :nanoflow_name, :xpath_constraint
|
|
11
|
+
|
|
12
|
+
def initialize(source, widget)
|
|
13
|
+
@source = source
|
|
14
|
+
@widget = widget
|
|
15
|
+
resolve_sources
|
|
16
|
+
resolve_flows
|
|
17
|
+
resolve_entity
|
|
18
|
+
@xpath_constraint = @xpath&.fetch('XPathConstraint', '').to_s
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def resolve_sources
|
|
22
|
+
direct = widget['DataSource'] if widget.is_a?(Hash)
|
|
23
|
+
@xpath = direct if database_source?(direct)
|
|
24
|
+
@xpath ||= nested(widget, 'CustomWidgets$CustomWidgetXPathSource').first
|
|
25
|
+
@association_source = direct if direct&.fetch('$Type', nil) == 'Forms$AssociationSource'
|
|
26
|
+
@association_source ||= nested(widget, 'Forms$AssociationSource').first
|
|
27
|
+
@microflow_source = direct if direct&.fetch('$Type', nil) == 'Forms$MicroflowSource'
|
|
28
|
+
@microflow_source ||= nested(widget, 'Forms$MicroflowSource').first
|
|
29
|
+
@nanoflow_source = direct if direct&.fetch('$Type', nil) == 'Forms$NanoflowSource'
|
|
30
|
+
@nanoflow_source ||= nested(widget, 'Forms$NanoflowSource').first
|
|
31
|
+
@microflow_name = flow_name(@microflow_source, 'Microflow')
|
|
32
|
+
@nanoflow_name = @nanoflow_source&.fetch('Nanoflow', '').to_s
|
|
33
|
+
@association_path = entity_ref_path(@association_source&.fetch('EntityRef', nil))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def flow_name(source, key)
|
|
37
|
+
source&.dig("#{key}Settings", key).to_s.then do |name|
|
|
38
|
+
name.empty? ? source&.fetch(key, '').to_s : name
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def resolve_flows
|
|
43
|
+
@flow = qualified_unit('Microflows$Microflow', microflow_name) unless microflow_name.empty?
|
|
44
|
+
@nanoflow = qualified_unit('Microflows$Nanoflow', nanoflow_name) unless nanoflow_name.empty?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def resolve_entity
|
|
48
|
+
@entity = [
|
|
49
|
+
entity_ref_destination(@xpath&.fetch('EntityRef', nil)),
|
|
50
|
+
entity_ref_destination(@association_source&.fetch('EntityRef', nil)),
|
|
51
|
+
@flow&.document&.dig('MicroflowReturnType', 'Entity'),
|
|
52
|
+
@nanoflow&.document&.dig('MicroflowReturnType', 'Entity')
|
|
53
|
+
].map(&:to_s).find { !_1.empty? }.to_s
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def supported? = xpath? || association? || microflow? || nanoflow?
|
|
57
|
+
def xpath? = !@xpath.nil? || connector_fallback?
|
|
58
|
+
def association? = !@association_source.nil? && !association_path.empty? && !entity.empty?
|
|
59
|
+
def microflow? = !@microflow_source.nil? && !microflow_name.empty? && !@flow.nil?
|
|
60
|
+
def nanoflow? = !@nanoflow_source.nil? && !nanoflow_name.empty? && !@nanoflow.nil?
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
attr_reader :widget
|
|
65
|
+
|
|
66
|
+
def database_source?(source)
|
|
67
|
+
%w[Forms$DatabaseSource Forms$NewListViewDatabaseSource Forms$ListViewXPathSource]
|
|
68
|
+
.include?(source&.fetch('$Type', nil))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def entity_ref_destination(reference)
|
|
72
|
+
steps = array(reference&.fetch('Steps', nil))
|
|
73
|
+
steps.last&.fetch('DestinationEntity', nil) || reference&.fetch('Entity', nil)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def entity_ref_path(reference)
|
|
77
|
+
array(reference&.fetch('Steps', nil)).flat_map do |step|
|
|
78
|
+
[step['Association'], step['DestinationEntity']]
|
|
79
|
+
end.map(&:to_s).reject(&:empty?).join('/')
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def connector_fallback?
|
|
83
|
+
return @connector_fallback unless @connector_fallback.nil?
|
|
84
|
+
|
|
85
|
+
@connector_fallback = connector_action.then do |action|
|
|
86
|
+
action && simple_unconfigured_query?(action)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def connector_action
|
|
91
|
+
actions = nested(@flow&.document, 'DatabaseConnector$ExecuteDatabaseQueryAction')
|
|
92
|
+
actions.one? ? actions.first : nil
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def simple_unconfigured_query?(action)
|
|
96
|
+
connection, query = connector_query(action['Query'].to_s)
|
|
97
|
+
return false unless connection && query && connection_unconfigured?(connection)
|
|
98
|
+
return false unless query['Query'].to_s.match?(/\A\s*select\s+.+\s+from\s+[A-Za-z_][A-Za-z0-9_]*\s*;?\s*\z/im)
|
|
99
|
+
|
|
100
|
+
mappings = array(query['TableMappings'])
|
|
101
|
+
mappings.one? && mappings.first['Entity'].to_s == entity
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def connector_query(qualified)
|
|
105
|
+
connection_name, _, query_name = qualified.rpartition('.')
|
|
106
|
+
unit = @source.units.find do |candidate|
|
|
107
|
+
candidate.document['$Type'] == 'DatabaseConnector$DatabaseConnection' &&
|
|
108
|
+
"#{candidate.module_name}.#{candidate.document['Name']}" == connection_name
|
|
109
|
+
end
|
|
110
|
+
[unit&.document, array(unit&.document&.fetch('Queries', nil)).find { _1['Name'] == query_name }]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def connection_unconfigured?(connection)
|
|
114
|
+
%w[ConnectionString UserName Password].all? do |field|
|
|
115
|
+
name = connection[field].to_s
|
|
116
|
+
constant = qualified_unit('Constants$Constant', name) unless name.empty?
|
|
117
|
+
constant && constant.document.fetch('DefaultValue', nil).to_s.empty?
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def qualified_unit(type, qualified)
|
|
122
|
+
@source.units_of(type).find do |unit|
|
|
123
|
+
"#{unit.module_name}.#{unit.document['Name']}" == qualified
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def nested(value, type, result = [])
|
|
128
|
+
case value
|
|
129
|
+
when Hash
|
|
130
|
+
result << value if value['$Type'] == type
|
|
131
|
+
value.each_value { nested(_1, type, result) }
|
|
132
|
+
when Array then value.each { nested(_1, type, result) }
|
|
133
|
+
end
|
|
134
|
+
result
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
# rubocop:enable Metrics
|
|
138
|
+
end
|
|
139
|
+
end
|