mxrb 0.1.1 → 0.1.3
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/README.de-DE.md +4 -0
- data/README.md +4 -0
- data/README.pt-BR.md +4 -0
- data/bin/mxrb +38 -2
- data/docs/de-DE/oql-sql.md +18 -0
- data/docs/de-DE/scaffolds.md +23 -0
- data/docs/en-US/oql-sql.md +18 -0
- data/docs/en-US/scaffolds.md +22 -0
- data/docs/pt-BR/entity-dsl.md +8 -0
- data/docs/pt-BR/oql-sql.md +18 -0
- data/docs/pt-BR/scaffolds.md +22 -0
- data/docs/pt-BR/writing.md +28 -0
- data/lib/mxrb/compare.rb +7 -0
- data/lib/mxrb/compiler/packager.rb +27 -13
- data/lib/mxrb/compiler/portable_packager.rb +16 -11
- data/lib/mxrb/dsl/builder.rb +100 -2
- data/lib/mxrb/exporter.rb +184 -32
- data/lib/mxrb/initializer.rb +1 -0
- data/lib/mxrb/integrity/validator.rb +18 -7
- data/lib/mxrb/model/entity.rb +17 -1
- data/lib/mxrb/model/module.rb +71 -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.rb +9 -3
- data/lib/mxrb/progress.rb +242 -0
- data/lib/mxrb/runtime/clipboard.rb +62 -0
- data/lib/mxrb/runtime/database_workspace.rb +28 -0
- data/lib/mxrb/scaffold/cli.rb +20 -1
- data/lib/mxrb/scaffold/generator.rb +125 -3
- data/lib/mxrb/scaffold/help.rb +16 -2
- data/lib/mxrb/scaffold/recipes.rb +27 -0
- data/lib/mxrb/scaffold/templates.rb +11 -0
- data/lib/mxrb/scaffold/transaction.rb +2 -2
- data/lib/mxrb/team_server.rb +10 -7
- data/lib/mxrb/version.rb +1 -1
- data/lib/mxrb/writer.rb +94 -19
- data/lib/mxrb.rb +13 -7
- metadata +3 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
3
5
|
module Mxrb
|
|
4
6
|
module Scaffold
|
|
5
7
|
# Creates conventional Ruby-first artifacts and connects missing aggregators.
|
|
@@ -24,8 +26,7 @@ module Mxrb
|
|
|
24
26
|
@root = File.expand_path(target)
|
|
25
27
|
@transaction = Transaction.new
|
|
26
28
|
@dry_run = dry_run
|
|
27
|
-
|
|
28
|
-
@page_template = page_options.delete(:page_template)
|
|
29
|
+
assign_options(page_options)
|
|
29
30
|
raise ArgumentError, "unknown generator options: #{page_options.keys.join(', ')}" unless
|
|
30
31
|
page_options.empty?
|
|
31
32
|
end
|
|
@@ -42,6 +43,13 @@ module Mxrb
|
|
|
42
43
|
|
|
43
44
|
private
|
|
44
45
|
|
|
46
|
+
def assign_options(options)
|
|
47
|
+
@page_chain = options.delete(:page_chain)
|
|
48
|
+
@page_template = options.delete(:page_template)
|
|
49
|
+
@demo_entity = options.delete(:demo_entity)
|
|
50
|
+
@demo_roles = options.delete(:demo_roles)
|
|
51
|
+
end
|
|
52
|
+
|
|
45
53
|
def page_chain
|
|
46
54
|
return unless @page_chain
|
|
47
55
|
return @page_chain if PAGE_CHAINS.include?(@page_chain)
|
|
@@ -55,8 +63,13 @@ module Mxrb
|
|
|
55
63
|
end
|
|
56
64
|
|
|
57
65
|
def stage_registry
|
|
66
|
+
files = @transaction.created.dup
|
|
67
|
+
if @kind == :demo_user
|
|
68
|
+
shared = [project_path('.env'), project_path('.env.example')]
|
|
69
|
+
files -= shared
|
|
70
|
+
end
|
|
58
71
|
Registry.stage(
|
|
59
|
-
@transaction, root: @root, key: "#{@kind}:#{@name}", files:
|
|
72
|
+
@transaction, root: @root, key: "#{@kind}:#{@name}", files:
|
|
60
73
|
)
|
|
61
74
|
end
|
|
62
75
|
|
|
@@ -125,6 +138,115 @@ module Mxrb
|
|
|
125
138
|
@transaction.write(path, lines.join)
|
|
126
139
|
end
|
|
127
140
|
|
|
141
|
+
def connect_project_demo_users
|
|
142
|
+
path = project_path('project.rb')
|
|
143
|
+
source = connect_project_dotenv(@transaction.content(path))
|
|
144
|
+
opening = source.lines.index { _1.match?(/^\s*security do\s*$/) }
|
|
145
|
+
raise ArgumentError, security_initialization_error unless opening
|
|
146
|
+
|
|
147
|
+
insert_demo_user_loader(path, source, opening)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def insert_demo_user_loader(path, source, opening)
|
|
151
|
+
line = 'evaluate_dir File.join(__dir__, "app", "security", "demo_users")'
|
|
152
|
+
return if source.include?(line)
|
|
153
|
+
|
|
154
|
+
lines = source.lines
|
|
155
|
+
indentation = "#{lines.fetch(opening)[/^\s*/]} "
|
|
156
|
+
lines.insert(opening + 1, "#{indentation}#{line}\n")
|
|
157
|
+
@transaction.write(path, lines.join)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def connect_project_dotenv(source)
|
|
161
|
+
dotenv_line = 'Dotenv.load(File.join(__dir__, ".env"))'
|
|
162
|
+
return source if source.include?(dotenv_line)
|
|
163
|
+
|
|
164
|
+
lines = source.lines
|
|
165
|
+
require_line = lines.index { _1.match?(%r{^require ["']dotenv/load["']\s*$}) }
|
|
166
|
+
raise ArgumentError, 'project.rb must require dotenv/load before scaffolding demo users' unless require_line
|
|
167
|
+
|
|
168
|
+
lines.insert(require_line + 1, "#{dotenv_line}\n").join
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def security_initialization_error
|
|
172
|
+
'project security is not initialized; run `mxrb security init Module` first'
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def create_demo_user_file(name, entity, roles, password_env)
|
|
176
|
+
path = project_path('app', 'security', 'demo_users', "#{Templates.snake_case(name)}.rb")
|
|
177
|
+
@transaction.create(
|
|
178
|
+
path,
|
|
179
|
+
Templates.render(
|
|
180
|
+
:demo_user, name:, entity:, roles:, password_env:
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def ensure_demo_user_secret(password_env)
|
|
186
|
+
path = project_path('.env')
|
|
187
|
+
source = @transaction.content(path)
|
|
188
|
+
if source
|
|
189
|
+
@transaction.write(path, append_env(source, password_env, generated_demo_password)) unless
|
|
190
|
+
env_key?(source, password_env)
|
|
191
|
+
else
|
|
192
|
+
create_demo_user_secret(path, password_env)
|
|
193
|
+
end
|
|
194
|
+
ensure_demo_user_env_example(password_env)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def create_demo_user_secret(path, password_env)
|
|
198
|
+
source = "# Local secrets; never commit this file.\n"
|
|
199
|
+
content = append_env(source, password_env, generated_demo_password)
|
|
200
|
+
@transaction.create(path, content, mode: 0o600)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def ensure_demo_user_env_example(password_env)
|
|
204
|
+
path = project_path('.env.example')
|
|
205
|
+
source = @transaction.content(path) || "# Copy to .env and keep real values local.\n"
|
|
206
|
+
@transaction.write(path, append_env(source, password_env, '')) unless env_key?(source, password_env)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def append_env(source, key, value)
|
|
210
|
+
"#{source.rstrip}\n#{key}=#{value}\n"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def env_key?(source, key)
|
|
214
|
+
source.lines.any? { _1.match?(/\A#{Regexp.escape(key)}=/) }
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def generated_demo_password
|
|
218
|
+
"Mxrb#{SecureRandom.alphanumeric(16)}7"
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def validate_demo_user_references!(name, entity, roles)
|
|
222
|
+
sources = project_ruby_sources
|
|
223
|
+
errors = []
|
|
224
|
+
missing_roles = roles - known_user_roles(sources)
|
|
225
|
+
errors << "missing user role(s): #{missing_roles.join(', ')}" unless missing_roles.empty?
|
|
226
|
+
errors << "missing user entity: #{entity}" unless
|
|
227
|
+
entity == 'System.User' || known_entities(sources).include?(entity)
|
|
228
|
+
return if errors.empty?
|
|
229
|
+
|
|
230
|
+
raise ArgumentError, "demo user #{name}: #{errors.join('; ')}"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def project_ruby_sources
|
|
234
|
+
Dir.glob(project_path('**', '*.rb')).to_h { [_1, File.read(_1)] }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def known_user_roles(sources)
|
|
238
|
+
pattern = /\buser_role\s+(?::([A-Za-z][A-Za-z0-9_]*)|["']([^"']+)["'])/
|
|
239
|
+
sources.values.flat_map { _1.scan(pattern).map { |match| match.compact.first } }.uniq
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def known_entities(sources)
|
|
243
|
+
pattern = /\bentity\s+(?::([A-Za-z][A-Za-z0-9_]*)|["']([^"']+)["'])/
|
|
244
|
+
sources.flat_map do |path, source|
|
|
245
|
+
mod = path.match(%r{/modules/([^/]+)/})&.[](1)
|
|
246
|
+
mod ? source.scan(pattern).map { "#{mod}.#{_1.compact.first}" } : []
|
|
247
|
+
end.uniq
|
|
248
|
+
end
|
|
249
|
+
|
|
128
250
|
def insert_before_closing(source, line, path)
|
|
129
251
|
lines = source.lines
|
|
130
252
|
closing = lines.rindex { _1.strip == 'end' }
|
data/lib/mxrb/scaffold/help.rb
CHANGED
|
@@ -23,6 +23,11 @@ module Mxrb
|
|
|
23
23
|
'nanoflow' => ['new Module.Flow', 'Create a client nanoflow', 'presentation/client_actions'],
|
|
24
24
|
'repository' => ['new Module.Repository', 'Create repository port and adapter', 'application/infrastructure'],
|
|
25
25
|
'security' => ['init Module', 'Create module roles template', 'security'],
|
|
26
|
+
'demo-user' => [
|
|
27
|
+
'new NAME [--entity Module.Entity] [--role ROLE]',
|
|
28
|
+
'Create a local Mendix demo user backed by an ignored .env secret',
|
|
29
|
+
'app/security/demo_users'
|
|
30
|
+
],
|
|
26
31
|
'scheduled-event' => ['new Module.Event', 'Create scheduled event and handler', 'application/jobs'],
|
|
27
32
|
'constant' => ['new Module.Constant', 'Create a string constant', 'domain/constants'],
|
|
28
33
|
'integration' => ['new Module.Adapter', 'Create integration adapter flow', 'infrastructure/integrations'],
|
|
@@ -54,6 +59,14 @@ module Mxrb
|
|
|
54
59
|
mxrb page templates [--json]
|
|
55
60
|
--template starter|blank|dashboard|form-vertical
|
|
56
61
|
HELP
|
|
62
|
+
DEMO_USER_OPTIONS = <<~HELP
|
|
63
|
+
Demo user options:
|
|
64
|
+
--entity Module.Entity User entity (default: System.User)
|
|
65
|
+
--role ROLE App user role (repeatable; default: User)
|
|
66
|
+
|
|
67
|
+
`new` is optional, so `mxrb demo-user manager ...` is also accepted.
|
|
68
|
+
The generated password is stored in the ignored .env file.
|
|
69
|
+
HELP
|
|
57
70
|
|
|
58
71
|
def text(command)
|
|
59
72
|
usage, description, destination = COMMANDS.fetch(command)
|
|
@@ -71,9 +84,10 @@ module Mxrb
|
|
|
71
84
|
end
|
|
72
85
|
|
|
73
86
|
def page_options(command)
|
|
74
|
-
return
|
|
87
|
+
return PAGE_OPTIONS if command == 'page'
|
|
88
|
+
return DEMO_USER_OPTIONS if command == 'demo-user'
|
|
75
89
|
|
|
76
|
-
|
|
90
|
+
''
|
|
77
91
|
end
|
|
78
92
|
end
|
|
79
93
|
end
|
|
@@ -129,6 +129,33 @@ module Mxrb
|
|
|
129
129
|
connect_project_security(module_name)
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
def scaffold_demo_user
|
|
133
|
+
ensure_project
|
|
134
|
+
name = identifier(@name, label: 'demo user')
|
|
135
|
+
entity = demo_user_entity
|
|
136
|
+
roles = demo_user_roles
|
|
137
|
+
validate_demo_user_references!(name, entity, roles)
|
|
138
|
+
password_env = "MXRB_DEMO_USER_#{name.gsub(/[^A-Za-z0-9]/, '_').upcase}_PASSWORD"
|
|
139
|
+
|
|
140
|
+
connect_project_demo_users
|
|
141
|
+
create_demo_user_file(name, entity, roles, password_env)
|
|
142
|
+
ensure_demo_user_secret(password_env) unless @dry_run
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def demo_user_entity
|
|
146
|
+
entity = (@demo_entity || 'System.User').to_s
|
|
147
|
+
pattern = /\A[A-Za-z][A-Za-z0-9_]*\.[A-Za-z][A-Za-z0-9_]*\z/
|
|
148
|
+
raise ArgumentError, 'demo user entity must be qualified as Module.Entity' unless entity.match?(pattern)
|
|
149
|
+
|
|
150
|
+
entity
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def demo_user_roles
|
|
154
|
+
roles = Array(@demo_roles)
|
|
155
|
+
roles = ['User'] if roles.empty?
|
|
156
|
+
roles.map { identifier(_1.to_s, label: 'user role') }.uniq
|
|
157
|
+
end
|
|
158
|
+
|
|
132
159
|
def scaffold_functional_test
|
|
133
160
|
module_name, artifact_name = qualified_name
|
|
134
161
|
ensure_project
|
|
@@ -293,6 +293,17 @@ module Mxrb
|
|
|
293
293
|
RUBY
|
|
294
294
|
end
|
|
295
295
|
|
|
296
|
+
def demo_user(_module_name, name, entity:, roles:, password_env:)
|
|
297
|
+
<<~RUBY
|
|
298
|
+
# frozen_string_literal: true
|
|
299
|
+
|
|
300
|
+
demo_user #{name.inspect},
|
|
301
|
+
entity: #{entity.inspect},
|
|
302
|
+
roles: #{roles.inspect},
|
|
303
|
+
password: ENV.fetch(#{password_env.inspect})
|
|
304
|
+
RUBY
|
|
305
|
+
end
|
|
306
|
+
|
|
296
307
|
def scheduled_event(module_name, name)
|
|
297
308
|
<<~RUBY
|
|
298
309
|
# frozen_string_literal: true
|
|
@@ -21,11 +21,11 @@ module Mxrb
|
|
|
21
21
|
nil
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def create(path, content)
|
|
24
|
+
def create(path, content, mode: 0o644)
|
|
25
25
|
abort "#{path}: file already exists" if File.exist?(path) || @changes.key?(path)
|
|
26
26
|
|
|
27
27
|
@created << path
|
|
28
|
-
@changes[path] = Change.new(path, content, nil,
|
|
28
|
+
@changes[path] = Change.new(path, content, nil, mode)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def write(path, new_content)
|
data/lib/mxrb/team_server.rb
CHANGED
|
@@ -161,7 +161,7 @@ module Mxrb
|
|
|
161
161
|
|
|
162
162
|
# Git transport for Team Server. PATs are passed to a short-lived
|
|
163
163
|
# GIT_ASKPASS process and are never embedded in a URL or git config.
|
|
164
|
-
class Repository
|
|
164
|
+
class Repository # rubocop:disable Metrics/ClassLength
|
|
165
165
|
def initialize(credentials: Credentials.new, runner: CommandRunner.new,
|
|
166
166
|
authenticator: GitAuthenticator.new(credentials))
|
|
167
167
|
@runner = runner
|
|
@@ -249,11 +249,14 @@ module Mxrb
|
|
|
249
249
|
end
|
|
250
250
|
|
|
251
251
|
def capture!(command, chdir: nil)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
252
|
+
Progress.with("Team Server #{command.first(2).join(' ')}") do |progress|
|
|
253
|
+
progress.update(detail: chdir || 'remote repository')
|
|
254
|
+
@authenticator.call do |environment|
|
|
255
|
+
output, status = @runner.capture(environment, command, chdir:)
|
|
256
|
+
raise TeamServerError, "Team Server Git operation failed: #{output.strip}" unless status.success?
|
|
257
|
+
|
|
258
|
+
output
|
|
259
|
+
end
|
|
257
260
|
end
|
|
258
261
|
end
|
|
259
262
|
|
|
@@ -285,7 +288,7 @@ module Mxrb
|
|
|
285
288
|
mpr
|
|
286
289
|
end
|
|
287
290
|
end
|
|
288
|
-
end
|
|
291
|
+
end # rubocop:enable Metrics/ClassLength
|
|
289
292
|
|
|
290
293
|
# Read-only client for Mendix's official App Repository API.
|
|
291
294
|
class Api
|
data/lib/mxrb/version.rb
CHANGED
data/lib/mxrb/writer.rb
CHANGED
|
@@ -31,19 +31,32 @@ module Mxrb
|
|
|
31
31
|
def initialize(path, definition)
|
|
32
32
|
@path = File.expand_path(path)
|
|
33
33
|
@definition = definition
|
|
34
|
+
@progress = Progress::NullTask.instance
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def write!
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
mpr = nil
|
|
39
|
+
native_units = prepared_native_units
|
|
40
|
+
asset_manifest = project_asset_manifest
|
|
41
|
+
total = 4 + native_units.count { _1["containment"] != "Modules" } +
|
|
42
|
+
@definition.fetch(:modules).size + Array(asset_manifest&.fetch("files", [])).size
|
|
43
|
+
Progress.with("Generating #{File.basename(@path)}", total:) do |progress|
|
|
44
|
+
@progress = progress
|
|
45
|
+
create_project! unless File.exist?(@path)
|
|
46
|
+
progress.advance(detail: "project container")
|
|
47
|
+
mpr = IO::MprFile.open(@path)
|
|
48
|
+
mpr.transaction do
|
|
49
|
+
apply(mpr, native_units)
|
|
50
|
+
mpr.write_architecture_definition(@definition)
|
|
51
|
+
end
|
|
52
|
+
progress.advance(detail: "model transaction")
|
|
53
|
+
materialize_project_assets(asset_manifest)
|
|
54
|
+
materialize_design_system
|
|
55
|
+
progress.advance(detail: "design system")
|
|
56
|
+
end
|
|
45
57
|
self
|
|
46
58
|
ensure
|
|
59
|
+
@progress = Progress::NullTask.instance
|
|
47
60
|
mpr&.close
|
|
48
61
|
end
|
|
49
62
|
|
|
@@ -56,11 +69,17 @@ module Mxrb
|
|
|
56
69
|
Model::DesignMaterializer.new(File.dirname(@path), design_system).materialize!
|
|
57
70
|
end
|
|
58
71
|
|
|
59
|
-
def
|
|
72
|
+
def project_asset_manifest
|
|
60
73
|
assets = @definition[:project_assets]
|
|
61
74
|
return unless assets
|
|
62
75
|
|
|
63
|
-
|
|
76
|
+
JSON.parse(File.read(assets.fetch(:manifest)))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def materialize_project_assets(manifest)
|
|
80
|
+
assets = @definition[:project_assets]
|
|
81
|
+
return unless assets && manifest
|
|
82
|
+
|
|
64
83
|
source_root = File.realpath(assets.fetch(:root))
|
|
65
84
|
target_root = File.dirname(@path)
|
|
66
85
|
manifest.fetch("files").each do |entry|
|
|
@@ -79,6 +98,7 @@ module Mxrb
|
|
|
79
98
|
FileUtils.mv(temporary, target)
|
|
80
99
|
ensure
|
|
81
100
|
FileUtils.rm_f(temporary) if temporary
|
|
101
|
+
@progress.advance(detail: "asset #{relative}") if defined?(relative) && relative
|
|
82
102
|
end
|
|
83
103
|
end
|
|
84
104
|
|
|
@@ -161,7 +181,7 @@ module Mxrb
|
|
|
161
181
|
JSON.parse(File.read(path))["format_version"]
|
|
162
182
|
end
|
|
163
183
|
|
|
164
|
-
def apply(mpr)
|
|
184
|
+
def apply(mpr, native_units)
|
|
165
185
|
root = mpr.root_unit
|
|
166
186
|
root_id = root.fetch("UnitID")
|
|
167
187
|
root_doc = mpr.parse_contents(root)
|
|
@@ -171,10 +191,6 @@ module Mxrb
|
|
|
171
191
|
"IsSystemProject" => false
|
|
172
192
|
}
|
|
173
193
|
mpr.update_unit(root_id, root_doc)
|
|
174
|
-
native_units = load_native_units(@definition[:native_units_path])
|
|
175
|
-
native_units = apply_native_unit_overrides(
|
|
176
|
-
native_units, @definition.fetch(:native_unit_overrides, [])
|
|
177
|
-
)
|
|
178
194
|
apply_native_project_units(mpr, root_id, native_units)
|
|
179
195
|
apply_default_project_units(mpr, root_id)
|
|
180
196
|
ensure_project_documents(mpr, root_id)
|
|
@@ -194,9 +210,18 @@ module Mxrb
|
|
|
194
210
|
write_module_security(mpr, module_id, mod) if mod.key?(:module_roles)
|
|
195
211
|
write_domain_model(mpr, module_id, mod)
|
|
196
212
|
write_documents(mpr, module_id, mod)
|
|
213
|
+
@progress.advance(detail: "module #{mod.fetch(:name)}")
|
|
197
214
|
end
|
|
198
215
|
write_project_security(mpr, root_id, @definition[:security]) if @definition[:security]
|
|
199
216
|
write_project_navigation(mpr, root_id, @definition[:navigation]) if @definition[:navigation]
|
|
217
|
+
@progress.advance(detail: "project security and navigation")
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def prepared_native_units
|
|
221
|
+
apply_native_unit_overrides(
|
|
222
|
+
load_native_units(@definition[:native_units_path]),
|
|
223
|
+
@definition.fetch(:native_unit_overrides, [])
|
|
224
|
+
)
|
|
200
225
|
end
|
|
201
226
|
|
|
202
227
|
def load_native_units(path)
|
|
@@ -323,8 +348,20 @@ module Mxrb
|
|
|
323
348
|
mod.fetch(:native_documents, []).each do |document|
|
|
324
349
|
doc = document.fetch(:doc)
|
|
325
350
|
doc = legacy_layout_doc(doc) if doc['$Type'] == 'Forms$Layout' && legacy_layout?
|
|
351
|
+
existing = mpr.unit(document[:unit_id]) if document[:unit_id]
|
|
352
|
+
if existing
|
|
353
|
+
current = mpr.parse_contents(existing)
|
|
354
|
+
preserved = current.merge(doc).merge(
|
|
355
|
+
'$ID' => current['$ID'] || existing.fetch('UnitID'), '$Type' => doc.fetch('$Type')
|
|
356
|
+
)
|
|
357
|
+
mpr.update_unit(existing.fetch('UnitID'), preserved)
|
|
358
|
+
next
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
requested_container = document[:container_id]
|
|
362
|
+
target_container = requested_container && mpr.unit(requested_container) ? requested_container : module_id
|
|
326
363
|
upsert_native_unit(
|
|
327
|
-
mpr,
|
|
364
|
+
mpr, target_container,
|
|
328
365
|
'containment' => document.fetch(:containment), 'doc' => doc
|
|
329
366
|
)
|
|
330
367
|
end
|
|
@@ -365,7 +402,10 @@ module Mxrb
|
|
|
365
402
|
|
|
366
403
|
def apply_native_unit_tree(mpr, target_root_id, units)
|
|
367
404
|
if units.any? { _1["unit_id"].to_s.empty? || _1["container_id"].to_s.empty? }
|
|
368
|
-
units.each
|
|
405
|
+
units.each do |unit|
|
|
406
|
+
upsert_native_unit(mpr, target_root_id, unit)
|
|
407
|
+
@progress.advance(detail: "native unit #{unit['name']}")
|
|
408
|
+
end
|
|
369
409
|
return
|
|
370
410
|
end
|
|
371
411
|
|
|
@@ -382,6 +422,7 @@ module Mxrb
|
|
|
382
422
|
target_container = mapped_containers.fetch(unit.fetch("container_id"))
|
|
383
423
|
actual_id = upsert_native_unit(mpr, target_container, unit)
|
|
384
424
|
mapped_containers[unit.fetch("unit_id")] = actual_id
|
|
425
|
+
@progress.advance(detail: "native unit #{unit['name']}")
|
|
385
426
|
end
|
|
386
427
|
pending = blocked
|
|
387
428
|
end
|
|
@@ -441,6 +482,7 @@ module Mxrb
|
|
|
441
482
|
end
|
|
442
483
|
{
|
|
443
484
|
demo_users_enabled: "EnableDemoUsers",
|
|
485
|
+
demo_users: "DemoUsers",
|
|
444
486
|
guest_access_enabled: "EnableGuestAccess",
|
|
445
487
|
guest_user_role: "GuestUserRole",
|
|
446
488
|
sign_in_microflow: "SignInMicroflow",
|
|
@@ -1359,7 +1401,7 @@ module Mxrb
|
|
|
1359
1401
|
previous&.dig(rules_key) || IO::BsonCodec.build_array([])
|
|
1360
1402
|
end
|
|
1361
1403
|
doc = (previous || {}).merge(
|
|
1362
|
-
"$ID" => id, "$Type" => "DomainModels$EntityImpl",
|
|
1404
|
+
"$ID" => id, "$Type" => previous&.fetch("$Type", nil) || "DomainModels$EntityImpl",
|
|
1363
1405
|
"Name" => entity.fetch(:name), "Documentation" => entity.fetch(:documentation, ""),
|
|
1364
1406
|
"GUID" => previous&.dig("GUID") || SecureRandom.uuid,
|
|
1365
1407
|
"Location" => previous&.dig("Location") || "#{(index % 4) * 220};#{(index / 4) * 160}",
|
|
@@ -1367,6 +1409,7 @@ module Mxrb
|
|
|
1367
1409
|
"IsRemote" => previous&.fetch("IsRemote", false) || false,
|
|
1368
1410
|
"RemoteSource" => previous&.fetch("RemoteSource", "") || ""
|
|
1369
1411
|
)
|
|
1412
|
+
apply_oql_view!(doc, entity.fetch(:oql_view, nil), previous)
|
|
1370
1413
|
doc[attrs_key] = IO::BsonCodec.build_array(attrs)
|
|
1371
1414
|
doc[rules_key] = access_rules
|
|
1372
1415
|
doc[validation_key] = validation_rules_doc(
|
|
@@ -1396,6 +1439,25 @@ module Mxrb
|
|
|
1396
1439
|
doc
|
|
1397
1440
|
end
|
|
1398
1441
|
|
|
1442
|
+
def apply_oql_view!(doc, view, previous)
|
|
1443
|
+
return unless view
|
|
1444
|
+
|
|
1445
|
+
if view[:source]
|
|
1446
|
+
source_key = native_existing_key(previous, 'source', 'Source') || 'Source'
|
|
1447
|
+
current_source = previous&.dig(source_key)
|
|
1448
|
+
source = (current_source.is_a?(Hash) ? current_source : {}).merge(
|
|
1449
|
+
'$ID' => current_source&.fetch('$ID', nil) || SecureRandom.uuid,
|
|
1450
|
+
'$Type' => 'DomainModels$OqlViewEntitySource',
|
|
1451
|
+
'SourceDocument' => view.fetch(:source)
|
|
1452
|
+
)
|
|
1453
|
+
doc[source_key] = source
|
|
1454
|
+
end
|
|
1455
|
+
return unless view[:query]
|
|
1456
|
+
|
|
1457
|
+
query_key = native_existing_key(previous, 'oqlQuery', 'OqlQuery', 'OQLQuery') || 'OqlQuery'
|
|
1458
|
+
doc[query_key] = view.fetch(:query)
|
|
1459
|
+
end
|
|
1460
|
+
|
|
1399
1461
|
def attribute_doc(attr, previous)
|
|
1400
1462
|
storage_type = Model::Attribute::TYPE_MAP.fetch(attr.fetch(:type))
|
|
1401
1463
|
type_key = native_existing_key(previous, "type", "Type", "newType", "NewType") || "NewType"
|
|
@@ -1923,7 +1985,9 @@ module Mxrb
|
|
|
1923
1985
|
"StrictMode" => false,
|
|
1924
1986
|
"StrictPageUrlCheck" => true,
|
|
1925
1987
|
"UserRoles" => IO::BsonCodec.build_array(roles, marker: 2),
|
|
1926
|
-
"DemoUsers" => IO::BsonCodec.build_array(
|
|
1988
|
+
"DemoUsers" => IO::BsonCodec.build_array(
|
|
1989
|
+
Array(security[:demo_users]).map { demo_user_doc(_1) }, marker: 2
|
|
1990
|
+
),
|
|
1927
1991
|
"FileDocumentAccess" => access_container("Security$FileDocumentAccessRuleContainer"),
|
|
1928
1992
|
"ImageAccess" => access_container("Security$ImageAccessRuleContainer"),
|
|
1929
1993
|
"PasswordPolicySettings" => password_policy
|
|
@@ -1945,6 +2009,17 @@ module Mxrb
|
|
|
1945
2009
|
}
|
|
1946
2010
|
end
|
|
1947
2011
|
|
|
2012
|
+
def demo_user_doc(user)
|
|
2013
|
+
{
|
|
2014
|
+
"$ID" => SecureRandom.uuid,
|
|
2015
|
+
"$Type" => "Security$DemoUserImpl",
|
|
2016
|
+
"UserName" => user.fetch(:name),
|
|
2017
|
+
"Password" => user.fetch(:password),
|
|
2018
|
+
"Entity" => user.fetch(:entity),
|
|
2019
|
+
"UserRoles" => IO::BsonCodec.build_array(user.fetch(:roles), marker: 1)
|
|
2020
|
+
}
|
|
2021
|
+
end
|
|
2022
|
+
|
|
1948
2023
|
def module_security_doc(roles, legacy: false)
|
|
1949
2024
|
{
|
|
1950
2025
|
"$ID" => SecureRandom.uuid,
|
data/lib/mxrb.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "mxrb/version"
|
|
4
4
|
require_relative "mxrb/errors"
|
|
5
|
+
require_relative "mxrb/progress"
|
|
5
6
|
require_relative "mxrb/doctor"
|
|
6
7
|
require_relative "mxrb/benchmark"
|
|
7
8
|
require_relative "mxrb/project_lifecycle"
|
|
@@ -126,6 +127,7 @@ require_relative "mxrb/runtime/docker_workspace"
|
|
|
126
127
|
require_relative "mxrb/runtime/executor"
|
|
127
128
|
require_relative "mxrb/runtime/native"
|
|
128
129
|
require_relative "mxrb/runtime/docker_executor"
|
|
130
|
+
require_relative "mxrb/runtime/clipboard"
|
|
129
131
|
require_relative "mxrb/runtime/database_workspace"
|
|
130
132
|
require_relative "mxrb/runtime/sql_server_database"
|
|
131
133
|
require_relative "mxrb/oql/server"
|
|
@@ -138,14 +140,18 @@ module Mxrb
|
|
|
138
140
|
#
|
|
139
141
|
# Mxrb.open("MyApp.mpr") { |p| puts p.modules.map(&:name) }
|
|
140
142
|
#
|
|
141
|
-
def self.open(path, readonly: true, &block)
|
|
142
|
-
|
|
143
|
-
return project unless block
|
|
143
|
+
def self.open(path, readonly: true, &block) # rubocop:disable Metrics/MethodLength
|
|
144
|
+
return Model::Project.open(path, readonly: readonly) unless block
|
|
144
145
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
Progress.with("Loading #{File.basename(path)}") do |progress|
|
|
147
|
+
progress.update(detail: "opening model")
|
|
148
|
+
project = Model::Project.open(path, readonly: readonly)
|
|
149
|
+
begin
|
|
150
|
+
progress.update(detail: "processing model")
|
|
151
|
+
block.call(project)
|
|
152
|
+
ensure
|
|
153
|
+
project.close
|
|
154
|
+
end
|
|
149
155
|
end
|
|
150
156
|
end
|
|
151
157
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mxrb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lucas Moura
|
|
@@ -372,9 +372,11 @@ files:
|
|
|
372
372
|
- lib/mxrb/oql/sql_server_workload_analyzer.rb
|
|
373
373
|
- lib/mxrb/oql/workload_analyzer.rb
|
|
374
374
|
- lib/mxrb/oql/workload_baseline.rb
|
|
375
|
+
- lib/mxrb/progress.rb
|
|
375
376
|
- lib/mxrb/project_lifecycle.rb
|
|
376
377
|
- lib/mxrb/protocols.rb
|
|
377
378
|
- lib/mxrb/protocols/plan.rb
|
|
379
|
+
- lib/mxrb/runtime/clipboard.rb
|
|
378
380
|
- lib/mxrb/runtime/database_workspace.rb
|
|
379
381
|
- lib/mxrb/runtime/docker_executor.rb
|
|
380
382
|
- lib/mxrb/runtime/docker_workspace.rb
|