habarico 7.3.0.pre.beta.8 → 7.3.0.pre.beta.10
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/VERSION +1 -1
- data/lib/habarico/contrib/preferences/base_preferences.rb +0 -1
- data/lib/habarico/contrib/preferences/config.rb +2 -3
- data/lib/habarico/contrib/preferences/registry.rb +35 -16
- data/lib/habarico/contrib/preferences/schemas.rb +0 -80
- data/lib/habarico/definition/action_reference.rb +2 -1
- data/lib/habarico/definition/boxes/box.rb +1 -1
- data/lib/habarico/definition/concerns/definition_model.rb +17 -5
- data/lib/habarico/definition/default_actions_reference.rb +18 -0
- data/lib/habarico/definition/definition.rb +1 -2
- data/lib/habarico/generators/action/resource_action_endpoint_parameters_generator.rb +13 -3
- data/lib/habarico/generators/action/trigger_action_schema_generator.rb +7 -1
- data/lib/habarico/generators/action/upload_files_action_parameters_generator.rb +1 -1
- data/lib/habarico/generators/action/view_action_parameters_generator.rb +27 -7
- data/lib/habarico/generators/endpoint/field_schema_generator.rb +2 -2
- data/lib/habarico/generators/endpoint/filter_schema_generator.rb +1 -1
- data/lib/habarico/generators/menu/menu_view_endpoint_parameters_generator.rb +3 -3
- data/lib/habarico/generators/menu/menu_view_parameters_generator.rb +2 -2
- data/lib/habarico/generators/view/action_descriptor_generator.rb +42 -1
- data/lib/habarico/generators/view/box_schema_generator.rb +21 -3
- data/lib/habarico/generators/view/detail_view_parameters_generator.rb +9 -5
- data/lib/habarico/generators/view/gallery_box_parameters_generator.rb +4 -7
- data/lib/habarico/generators/view/list_box_parameters_generator.rb +33 -33
- data/lib/habarico/generators/view/list_view_parameters_generator.rb +19 -12
- data/lib/habarico/generators/view/view_endpoint_parameters_generator.rb +13 -2
- data/lib/habarico/helpers/box_helper.rb +27 -0
- data/lib/habarico/schema/action/resource_action_endpoint_parameters.rb +2 -8
- data/lib/habarico/schema/action/upload_files_action_parameters.rb +1 -1
- data/lib/habarico/schema/action/view_action_parameters.rb +3 -2
- data/lib/habarico/schema/endpoint/field_schema.rb +1 -1
- data/lib/habarico/schema/endpoint/filter_schema.rb +1 -1
- data/lib/habarico/schema/resource_reference.rb +72 -0
- data/lib/habarico/schema/types.rb +3 -0
- data/lib/habarico/schema/view/action_descriptor.rb +21 -0
- data/lib/habarico/schema/view/box_schema.rb +2 -2
- data/lib/habarico/schema/view/default_actions_descriptor.rb +30 -0
- data/lib/habarico/schema/view/detail_view_parameters.rb +2 -2
- data/lib/habarico/schema/view/gallery_box_parameters.rb +2 -5
- data/lib/habarico/schema/view/list_box_parameters.rb +5 -12
- data/lib/habarico/schema/view/list_view_parameters.rb +3 -3
- data/lib/habarico/schema/view/view_endpoint_parameters.rb +2 -5
- data/lib/habarico.rb +5 -2
- metadata +4 -3
- data/lib/habarico/generators/action/view_action_endpoint_parameters_generator.rb +0 -42
- data/lib/habarico/schema/action/view_action_endpoint_parameters.rb +0 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7be061178763fb2bb412e36877d25e44ff109c82c90955279bcf7ff038f1216b
|
|
4
|
+
data.tar.gz: 7f478d3e668b336f2b2226b0bd2ea099aae08a22514ede701142cfea3f40457a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06bf7e5f65f10d91c1443235e11d180db58a3f34941367a5073f825b33ea26307bbaa522f4a283fd34ce9e11041626146c0636184d19c481ac761e231545d130
|
|
7
|
+
data.tar.gz: ca148d5c2f91c5fb53fdca5bb64dad14f12756a01b8408bdcb22bb5f92ce66eae70c20fa804a7819c0e6817137d1ad49895d36adddbcbfb6e64e22785a531022
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.3.0-beta.
|
|
1
|
+
7.3.0-beta.10
|
|
@@ -5,7 +5,6 @@ module Habarico
|
|
|
5
5
|
BASE_PREFERENCES = {
|
|
6
6
|
"theme" => Schemas::THEME,
|
|
7
7
|
"locale" => Schemas::LOCALE,
|
|
8
|
-
"preferences.dashboard" => Schemas::DASHBOARD,
|
|
9
8
|
"preferences.layout.app.pageWidth" => Schemas::APP_PAGE_WIDTH,
|
|
10
9
|
"preferences.layout.app.contrastMode" => Schemas::CONTRAST_MODE,
|
|
11
10
|
"preferences.layout.app.stripedRows" => Schemas::STRIPED_ROWS,
|
|
@@ -13,11 +13,10 @@ module Habarico
|
|
|
13
13
|
# no limit. Mirrors the Python `HABARICO_PREFERENCES["MAX_VALUE_BYTES"]` setting.
|
|
14
14
|
#
|
|
15
15
|
# The values below sit an order of magnitude above what the frontend actually writes:
|
|
16
|
-
# 100 datagrid columns are ~2 KB of `columns`/`columnWidths
|
|
17
|
-
#
|
|
16
|
+
# 100 datagrid columns are ~2 KB of `columns`/`columnWidths` and 20 saved queries ~11 KB at
|
|
17
|
+
# their fullest. A package registering its own key brings its own limit with it.
|
|
18
18
|
mattr_accessor :max_value_bytes, default: {
|
|
19
19
|
"default" => 16 * 1024,
|
|
20
|
-
"preferences.dashboard" => 32 * 1024,
|
|
21
20
|
%r{[a-zA-Z][a-zA-Z0-9_./-]*\.savedQueries} => 32 * 1024,
|
|
22
21
|
}
|
|
23
22
|
|
|
@@ -23,7 +23,7 @@ module Habarico
|
|
|
23
23
|
# `max_bytes` needs no sentinel, unlike `default`: nil is not a usable size, so it simply
|
|
24
24
|
# means "not given at registration" and defers to `max_value_bytes`. It is resolved once, at
|
|
25
25
|
# registration, exactly like `defaults` - nothing reads the configuration per write.
|
|
26
|
-
Entry = Struct.new(:schema, :default, :validator, :max_bytes, keyword_init: true)
|
|
26
|
+
Entry = Struct.new(:schema, :default, :validator, :max_bytes, :migrate_value, keyword_init: true)
|
|
27
27
|
|
|
28
28
|
class PreferenceRegistry
|
|
29
29
|
def initialize
|
|
@@ -37,11 +37,14 @@ module Habarico
|
|
|
37
37
|
@pattern_entries.clear
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
# `migrate_value` is called with a stored value that no longer validates, before it would be
|
|
41
|
+
# deleted; whatever it returns is validated and saved. It is how the package that owns a
|
|
42
|
+
# shape changes it without a data migration.
|
|
43
|
+
def register(key, schema:, default: EMPTY, max_bytes: nil, migrate_value: nil)
|
|
41
44
|
raise InvalidPreferenceKey, key unless key.is_a?(String) && key.match?(KEY_PATTERN)
|
|
42
45
|
raise PreferenceAlreadyRegistered, key if @entries.key?(key)
|
|
43
46
|
|
|
44
|
-
@entries[key] = build_entry(key, schema, default, max_bytes)
|
|
47
|
+
@entries[key] = build_entry(key, schema, default, max_bytes, migrate_value)
|
|
45
48
|
nil
|
|
46
49
|
end
|
|
47
50
|
|
|
@@ -147,20 +150,20 @@ module Habarico
|
|
|
147
150
|
raise PreferenceValueTooLarge.new(key, limit, size) if size > limit
|
|
148
151
|
end
|
|
149
152
|
|
|
150
|
-
# Configured limit for the key - or pattern - a preference is being registered under.
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
# unlimited. A pattern may be configured as
|
|
154
|
-
#
|
|
155
|
-
# already-registered limit.
|
|
153
|
+
# Configured limit for the key - or pattern - a preference is being registered under.
|
|
154
|
+
# `max_value_bytes` decides first, by the very string the preference is registered under: a
|
|
155
|
+
# deployment's word outranks a limit a gem registered, which is itself a default above the
|
|
156
|
+
# hash's "default" entry. A nil anywhere there means unlimited. A pattern may be configured as
|
|
157
|
+
# a Regexp or as its source string. Read once, at registration, exactly like `defaults`:
|
|
158
|
+
# changing the config afterwards does not move an already-registered limit.
|
|
156
159
|
def resolve_max_bytes(registered_as, max_bytes)
|
|
157
|
-
return max_bytes unless max_bytes.nil?
|
|
158
|
-
|
|
159
160
|
limits = Preferences.max_value_bytes || {}
|
|
160
161
|
limits.each do |configured, bytes|
|
|
161
162
|
name = configured.is_a?(Regexp) ? configured.source : configured.to_s
|
|
162
163
|
return bytes if name == registered_as
|
|
163
164
|
end
|
|
165
|
+
return max_bytes unless max_bytes.nil?
|
|
166
|
+
|
|
164
167
|
limits["default"]
|
|
165
168
|
end
|
|
166
169
|
|
|
@@ -191,10 +194,10 @@ module Habarico
|
|
|
191
194
|
raise PreferenceNotRegistered, key
|
|
192
195
|
end
|
|
193
196
|
|
|
194
|
-
def build_entry(key, schema, default, max_bytes = nil)
|
|
197
|
+
def build_entry(key, schema, default, max_bytes = nil, migrate_value = nil)
|
|
195
198
|
validator = JSONSchemer.schema(schema)
|
|
196
199
|
entry = Entry.new(schema: schema, default: default, validator: validator,
|
|
197
|
-
max_bytes: resolve_max_bytes(key, max_bytes))
|
|
200
|
+
max_bytes: resolve_max_bytes(key, max_bytes), migrate_value: migrate_value)
|
|
198
201
|
return entry if default.equal?(EMPTY) || default.respond_to?(:call)
|
|
199
202
|
|
|
200
203
|
errors = validator.validate(default).to_a
|
|
@@ -203,6 +206,22 @@ module Habarico
|
|
|
203
206
|
entry
|
|
204
207
|
end
|
|
205
208
|
|
|
209
|
+
# Gives the owner's schema a chance to bring a stored value up to date, in place.
|
|
210
|
+
#
|
|
211
|
+
# A migration that does not validate, or one that raises, leaves the record to be deleted:
|
|
212
|
+
# a schema's own bug must not make reading preferences fail.
|
|
213
|
+
def migrate_stored_value(entry, record)
|
|
214
|
+
return false if entry.migrate_value.nil?
|
|
215
|
+
|
|
216
|
+
migrated = entry.migrate_value.call(record.value)
|
|
217
|
+
return false unless entry.validator.valid?(migrated)
|
|
218
|
+
|
|
219
|
+
record.update!(value: migrated)
|
|
220
|
+
true
|
|
221
|
+
rescue StandardError
|
|
222
|
+
false
|
|
223
|
+
end
|
|
224
|
+
|
|
206
225
|
# Resolves a (possibly callable) default. A callable that returns EMPTY or an
|
|
207
226
|
# invalid value degrades to EMPTY, mirroring Python `_resolve_default`.
|
|
208
227
|
def resolve_default(entry)
|
|
@@ -221,7 +240,7 @@ module Habarico
|
|
|
221
240
|
# returns nil (single-GET => 404, list => omit).
|
|
222
241
|
def resolve(key, entry, record)
|
|
223
242
|
if record
|
|
224
|
-
if entry.validator.valid?(record.value)
|
|
243
|
+
if entry.validator.valid?(record.value) || migrate_stored_value(entry, record)
|
|
225
244
|
return ResolvedPreference.new(key: key, value: record.value, updated_at: record.updated_at)
|
|
226
245
|
end
|
|
227
246
|
|
|
@@ -241,8 +260,8 @@ module Habarico
|
|
|
241
260
|
@registry ||= PreferenceRegistry.new
|
|
242
261
|
end
|
|
243
262
|
|
|
244
|
-
def self.register_preference(key, schema:, default: EMPTY, max_bytes: nil)
|
|
245
|
-
registry.register(key, schema: schema, default: default, max_bytes: max_bytes)
|
|
263
|
+
def self.register_preference(key, schema:, default: EMPTY, max_bytes: nil, migrate_value: nil)
|
|
264
|
+
registry.register(key, schema: schema, default: default, max_bytes: max_bytes, migrate_value: migrate_value)
|
|
246
265
|
end
|
|
247
266
|
|
|
248
267
|
def self.register_preference_pattern(pattern, schema:, max_bytes: nil)
|
|
@@ -46,86 +46,6 @@ module Habarico
|
|
|
46
46
|
},
|
|
47
47
|
},
|
|
48
48
|
}.freeze
|
|
49
|
-
|
|
50
|
-
WIDGET_LAYOUT = {
|
|
51
|
-
"type" => "object",
|
|
52
|
-
"required" => %w[x y w h],
|
|
53
|
-
"properties" => {
|
|
54
|
-
"x" => { "type" => "integer", "minimum" => 0 },
|
|
55
|
-
"y" => { "type" => "integer", "minimum" => 0 },
|
|
56
|
-
"w" => { "type" => "integer", "minimum" => 1 },
|
|
57
|
-
"h" => { "type" => "integer", "minimum" => 1 },
|
|
58
|
-
"stackedH" => { "type" => "integer", "minimum" => 1 },
|
|
59
|
-
"minW" => { "type" => "integer", "minimum" => 1 },
|
|
60
|
-
"minH" => { "type" => "integer", "minimum" => 1 },
|
|
61
|
-
},
|
|
62
|
-
}.freeze
|
|
63
|
-
|
|
64
|
-
GROUP_BY_BUCKET = {
|
|
65
|
-
"oneOf" => [
|
|
66
|
-
{
|
|
67
|
-
"type" => "object",
|
|
68
|
-
"required" => %w[type granularity],
|
|
69
|
-
"properties" => {
|
|
70
|
-
"type" => { "const" => "trunc" },
|
|
71
|
-
"granularity" => { "enum" => %w[second minute hour day week month quarter year] },
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"type" => "object",
|
|
76
|
-
"required" => %w[type size],
|
|
77
|
-
"properties" => {
|
|
78
|
-
"type" => { "const" => "width" },
|
|
79
|
-
"size" => { "type" => "number" },
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
}.freeze
|
|
84
|
-
|
|
85
|
-
GROUP_BY_ITEM = {
|
|
86
|
-
"type" => "object",
|
|
87
|
-
"required" => %w[field],
|
|
88
|
-
"properties" => {
|
|
89
|
-
"field" => { "type" => "string" },
|
|
90
|
-
"bucket" => { "anyOf" => [GROUP_BY_BUCKET, { "type" => "null" }] },
|
|
91
|
-
},
|
|
92
|
-
}.freeze
|
|
93
|
-
|
|
94
|
-
CHART_OPTIONS = {
|
|
95
|
-
"type" => "object",
|
|
96
|
-
"properties" => {
|
|
97
|
-
"orientation" => { "enum" => %w[vertical horizontal] },
|
|
98
|
-
"variant" => { "enum" => %w[pie doughnut] },
|
|
99
|
-
},
|
|
100
|
-
}.freeze
|
|
101
|
-
|
|
102
|
-
WIDGET_CONFIG = {
|
|
103
|
-
"type" => "object",
|
|
104
|
-
"required" => %w[id type layout],
|
|
105
|
-
"properties" => {
|
|
106
|
-
"id" => { "type" => "string" },
|
|
107
|
-
"type" => { "type" => "string" },
|
|
108
|
-
"resource" => { "type" => %w[string null] },
|
|
109
|
-
"label" => { "type" => %w[string null] },
|
|
110
|
-
"layout" => WIDGET_LAYOUT,
|
|
111
|
-
"filters" => { "type" => %w[object null], "additionalProperties" => { "type" => "string" } },
|
|
112
|
-
"sort" => { "anyOf" => [SORT, { "type" => "null" }] },
|
|
113
|
-
"perPage" => { "type" => %w[integer null], "minimum" => 1 },
|
|
114
|
-
"columns" => { "type" => %w[array null], "items" => { "type" => "string" } },
|
|
115
|
-
"field" => { "type" => %w[string null] },
|
|
116
|
-
"aggregateFunction" => { "type" => %w[string null], "enum" => [nil, "count", "sum", "avg", "min", "max"] },
|
|
117
|
-
"color" => { "type" => %w[string null], "enum" => [nil, "primary", "secondary", "success", "error", "warning", "info", "inherit"] },
|
|
118
|
-
"groupBy" => { "type" => %w[array null], "items" => GROUP_BY_ITEM },
|
|
119
|
-
"chartOptions" => { "anyOf" => [CHART_OPTIONS, { "type" => "null" }] },
|
|
120
|
-
},
|
|
121
|
-
}.freeze
|
|
122
|
-
|
|
123
|
-
DASHBOARD = {
|
|
124
|
-
"type" => "object",
|
|
125
|
-
"properties" => {
|
|
126
|
-
"widgets" => { "type" => "array", "items" => WIDGET_CONFIG },
|
|
127
|
-
},
|
|
128
|
-
}.freeze
|
|
129
49
|
end
|
|
130
50
|
end
|
|
131
51
|
end
|
|
@@ -2,7 +2,8 @@ module Habarico
|
|
|
2
2
|
class ActionReference
|
|
3
3
|
include DefinitionModel
|
|
4
4
|
|
|
5
|
-
attr_accessor :name, :label, :icon, :color, :action, :if, :permission, :menu_items, :view_mode, :dialog_width, :view_choices, :view_parameters, :view_keep_linked_parent
|
|
5
|
+
attr_accessor :name, :label, :icon, :color, :action, :if, :permission, :menu_items, :view_mode, :dialog_width, :view_choices, :view_parameters, :view_keep_linked_parent,
|
|
6
|
+
:reference, :confirm_dialog_title, :confirm_dialog_message
|
|
6
7
|
|
|
7
8
|
def initialize(params = {})
|
|
8
9
|
@permission = case params[:name].to_s
|
|
@@ -15,7 +15,7 @@ module Habarico
|
|
|
15
15
|
attr_accessor :grid_item
|
|
16
16
|
|
|
17
17
|
# List
|
|
18
|
-
attr_accessor :reference, :target, :extra_filters, :row_actions, :
|
|
18
|
+
attr_accessor :reference, :target, :extra_filters, :row_actions, :list_actions, :filters, :default_sorting, # rubocop:disable Lint/DuplicateMethods -- intentional for readability
|
|
19
19
|
:sortable_field_name, :sortable_sort_for_filters, :single_click_action, :double_click_action
|
|
20
20
|
|
|
21
21
|
# Object
|
|
@@ -3,11 +3,15 @@ module Habarico
|
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
5
|
def initialize(params = {})
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
assign(params)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Whether the author said anything about this attribute, as opposed to leaving it alone.
|
|
10
|
+
#
|
|
11
|
+
# The distinction an action slot needs: a slot nobody mentioned publishes the resource's default
|
|
12
|
+
# actions, while `nil` and `[]` are both decisions to render none of them.
|
|
13
|
+
def assigned?(attr)
|
|
14
|
+
@assigned_attrs.present? && @assigned_attrs.include?(attr)
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def assign(params = {})
|
|
@@ -28,9 +32,17 @@ module Habarico
|
|
|
28
32
|
@attrs
|
|
29
33
|
end
|
|
30
34
|
|
|
35
|
+
# Records every assignment, so `assigned?` can tell a slot left alone from one emptied on
|
|
36
|
+
# purpose - whichever way the host app wrote it, a constructor hash or a plain setter.
|
|
31
37
|
def attr_accessor(*attrs)
|
|
32
38
|
self.attrs.concat(attrs)
|
|
33
39
|
super(*attrs)
|
|
40
|
+
attrs.each do |attr|
|
|
41
|
+
define_method("#{attr}=") do |value|
|
|
42
|
+
(@assigned_attrs ||= []) << attr
|
|
43
|
+
instance_variable_set("@#{attr}", value)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
34
46
|
end
|
|
35
47
|
end
|
|
36
48
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Habarico
|
|
2
|
+
# What an author writes in an action slot to mean "this resource's default actions".
|
|
3
|
+
#
|
|
4
|
+
# The client expands it into one ordinary action descriptor per supported action, so it names no
|
|
5
|
+
# action and carries none of a button's appearance - a label, an icon and a colour belong to each
|
|
6
|
+
# action it expands into.
|
|
7
|
+
class DefaultActionsReference
|
|
8
|
+
include DefinitionModel
|
|
9
|
+
|
|
10
|
+
# Resource whose defaults expand here: a name, `ResourceReference::CURRENT`,
|
|
11
|
+
# `ResourceReference::LIST`, or nothing for the resource the slot is read in
|
|
12
|
+
attr_accessor :reference
|
|
13
|
+
|
|
14
|
+
# Which of them: `:list`, `:detail` or `:all`
|
|
15
|
+
attr_accessor :scope
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -38,7 +38,7 @@ module Habarico
|
|
|
38
38
|
# Generator classes - action
|
|
39
39
|
:link_action_parameters_generator, :resource_action_endpoint_parameters_generator,
|
|
40
40
|
:resource_action_parameters_generator, :save_action_parameters_generator, :action_schema_generator,
|
|
41
|
-
:
|
|
41
|
+
:view_action_parameters_generator,
|
|
42
42
|
:confirm_dialog_properties_generator, :global_resource_action_schema_generator,
|
|
43
43
|
:resource_action_schema_generator, :upload_files_action_parameters_generator,
|
|
44
44
|
:trigger_action_schema_generator,
|
|
@@ -117,7 +117,6 @@ module Habarico
|
|
|
117
117
|
@resource_action_parameters_generator = Habarico::ResourceActionParametersGenerator
|
|
118
118
|
@save_action_parameters_generator = Habarico::SaveActionParametersGenerator
|
|
119
119
|
@action_schema_generator = Habarico::ActionSchemaGenerator
|
|
120
|
-
@view_action_endpoint_parameters_generator = Habarico::ViewActionEndpointParametersGenerator
|
|
121
120
|
@view_action_parameters_generator = Habarico::ViewActionParametersGenerator
|
|
122
121
|
@confirm_dialog_properties_generator = Habarico::ConfirmDialogPropertiesGenerator
|
|
123
122
|
@global_resource_action_schema_generator = Habarico::GlobalResourceActionSchemaGenerator
|
|
@@ -9,9 +9,7 @@ module Habarico
|
|
|
9
9
|
Habarico::ResourceActionEndpointParameters.new(
|
|
10
10
|
path: generate_path(action, context),
|
|
11
11
|
method: generate_method(action, context),
|
|
12
|
-
reference:
|
|
13
|
-
action_name: generate_action_name(action, context),
|
|
14
|
-
record_pk: generate_record_pk(action, context)
|
|
12
|
+
reference: generate_endpoint_reference(action, context),
|
|
15
13
|
)
|
|
16
14
|
end
|
|
17
15
|
|
|
@@ -59,6 +57,18 @@ module Habarico
|
|
|
59
57
|
action.endpoint_reference
|
|
60
58
|
end
|
|
61
59
|
|
|
60
|
+
# What the endpoint addresses, as one address. `action_name` and `record_pk` live inside it, so
|
|
61
|
+
# they need a resource to belong to: an action without a declared reference is this admin's own.
|
|
62
|
+
def generate_endpoint_reference(action, context)
|
|
63
|
+
action_name = generate_action_name(action, context)
|
|
64
|
+
record_pk = generate_record_pk(action, context)
|
|
65
|
+
reference = generate_reference(action, context)
|
|
66
|
+
reference = resource_name if reference.blank? && (action_name.present? || record_pk.present?)
|
|
67
|
+
return nil if reference.blank?
|
|
68
|
+
|
|
69
|
+
Habarico::ResourceReference.build(reference, action_name: action_name, record_pk: record_pk)
|
|
70
|
+
end
|
|
71
|
+
|
|
62
72
|
def generate_action_name(action, _context)
|
|
63
73
|
return action.endpoint_action_name if action.endpoint_action_name.present?
|
|
64
74
|
return nil if Habarico::ActionHelper.is_standard_action?(action)
|
|
@@ -17,7 +17,7 @@ module Habarico
|
|
|
17
17
|
)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
action_schema_generator.generate(retarget(action, record_pk, reference), context)
|
|
20
|
+
action_schema_generator.generate(retarget(action, record_pk || detail_record_pk(action, context), reference), context)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
protected
|
|
@@ -26,6 +26,12 @@ module Habarico
|
|
|
26
26
|
@action_schema_generator ||= definition.action_schema_generator.new(definition, resource_path)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# A trigger runs outside the page of the record it was raised for, so a detail action names
|
|
30
|
+
# that record explicitly - a schema endpoint leaves it to the page.
|
|
31
|
+
def detail_record_pk(action, context)
|
|
32
|
+
context.record_pk if action.is_detail && action.endpoint_record_pk.blank?
|
|
33
|
+
end
|
|
34
|
+
|
|
29
35
|
# Copies the action before setting the target, so a declared action is never mutated.
|
|
30
36
|
def retarget(action, record_pk, reference)
|
|
31
37
|
return action if record_pk.blank? && reference.blank?
|
|
@@ -8,7 +8,7 @@ module Habarico
|
|
|
8
8
|
|
|
9
9
|
# Create model
|
|
10
10
|
Habarico::UploadFilesActionParameters.new(
|
|
11
|
-
reference: generate_reference(action, context),
|
|
11
|
+
reference: Habarico::ResourceReference.build(generate_reference(action, context)),
|
|
12
12
|
target: generate_target(action, context),
|
|
13
13
|
accept: generate_accept(action, context),
|
|
14
14
|
file_field: generate_file_field(action, context),
|
|
@@ -13,15 +13,35 @@ module Habarico
|
|
|
13
13
|
dialog_width: generate_dialog_width(action, context),
|
|
14
14
|
parameters: generate_parameters(action, context),
|
|
15
15
|
choices: generate_choices(action, context),
|
|
16
|
-
|
|
16
|
+
reference: generate_reference(action, context),
|
|
17
17
|
keep_linked_parent: generate_keep_linked_parent(action, context)
|
|
18
18
|
)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
protected
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
# What the view action addresses, left unaddressed when the action points at nothing of its
|
|
24
|
+
# own. `action_name` and `record_pk` live inside the address, so an action that has either names
|
|
25
|
+
# the resource it belongs to - the same rule the resource action endpoint follows. Only an
|
|
26
|
+
# explicit `endpoint_record_pk` is carried: the record of the request is the one the frontend
|
|
27
|
+
# already renders.
|
|
28
|
+
def view_action_reference(action, _context)
|
|
29
|
+
action_name = target_view_action_name(action)
|
|
30
|
+
record_pk = action.endpoint_record_pk.presence
|
|
31
|
+
reference = action.endpoint_reference
|
|
32
|
+
reference = resource_name if reference.blank? && (action_name.present? || record_pk.present?)
|
|
33
|
+
return nil if reference.blank?
|
|
34
|
+
|
|
35
|
+
Habarico::ResourceReference.build(reference, action_name: action_name, record_pk: record_pk)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# The action segment is the *target view's*, not the action's own name: a reserved view
|
|
39
|
+
# (`list` / `create` / `edit` / `show`) is addressed by its type and has none, so an action
|
|
40
|
+
# opening one carries no address at all.
|
|
41
|
+
def target_view_action_name(action)
|
|
42
|
+
return action.endpoint_action_name if action.endpoint_action_name.present?
|
|
43
|
+
|
|
44
|
+
definition.normalized_views.find { |view| view.name == action.view_name }&.endpoint_action_name
|
|
25
45
|
end
|
|
26
46
|
|
|
27
47
|
# ***********************************************************************
|
|
@@ -48,12 +68,12 @@ module Habarico
|
|
|
48
68
|
Habarico::CallableHelper.resolve(action.view_choices, context)
|
|
49
69
|
end
|
|
50
70
|
|
|
51
|
-
def
|
|
52
|
-
# A standard action of the current resource needs no
|
|
53
|
-
# over the record it already has. One that names its own target does: the
|
|
71
|
+
def generate_reference(action, context)
|
|
72
|
+
# A standard action of the current resource needs no reference - the frontend opens the view
|
|
73
|
+
# over the record it already has. One that names its own target does: the reference is where
|
|
54
74
|
# the target resource and record are written down.
|
|
55
75
|
return nil if Habarico::ActionHelper.is_standard_action?(action) && !Habarico::ActionHelper.has_explicit_target?(action)
|
|
56
|
-
|
|
76
|
+
view_action_reference(action, context)
|
|
57
77
|
end
|
|
58
78
|
|
|
59
79
|
def generate_keep_linked_parent(action, _context)
|
|
@@ -44,7 +44,7 @@ module Habarico
|
|
|
44
44
|
required: generate_required(model, field_name, attr_name, variant),
|
|
45
45
|
sortable: generate_sortable(model, field_name, attr_name, variant),
|
|
46
46
|
sort_by: generate_sort_by(model, field_name, attr_name, variant),
|
|
47
|
-
reference: generate_reference(model, field_name, attr_name, variant),
|
|
47
|
+
reference: Habarico::ResourceReference.build(generate_reference(model, field_name, attr_name, variant)),
|
|
48
48
|
enum: generate_enum(model, field_name, attr_name, variant),
|
|
49
49
|
items: generate_items(model, field_name, attr_name, variant, subfields),
|
|
50
50
|
properties: generate_properties(model, field_name, attr_name, variant, subfields),
|
|
@@ -315,7 +315,7 @@ module Habarico
|
|
|
315
315
|
label: label,
|
|
316
316
|
type: 'string',
|
|
317
317
|
format: 'relation',
|
|
318
|
-
reference: reference_class.const_get(:REFERENCE),
|
|
318
|
+
reference: Habarico::ResourceReference.build(reference_class.const_get(:REFERENCE)),
|
|
319
319
|
)
|
|
320
320
|
end
|
|
321
321
|
end
|
|
@@ -14,7 +14,7 @@ module Habarico
|
|
|
14
14
|
format: generate_format(model, filter_name, filter_param),
|
|
15
15
|
label: generate_label(model, filter_name, filter_param),
|
|
16
16
|
help_text: generate_help_text(model, filter_name, filter_param),
|
|
17
|
-
reference: generate_reference(model, filter_name, filter_param),
|
|
17
|
+
reference: Habarico::ResourceReference.build(generate_reference(model, filter_name, filter_param)),
|
|
18
18
|
enum: generate_enum(model, filter_name, filter_param),
|
|
19
19
|
items: generate_items(model, filter_name, filter_param),
|
|
20
20
|
properties: generate_properties(model, filter_name, filter_param),
|
|
@@ -7,10 +7,10 @@ module Habarico
|
|
|
7
7
|
raise Eluvia::Errors::StandardError.new('Argument `menu_item` must be of :view type') unless menu_item.type.to_sym == :view
|
|
8
8
|
|
|
9
9
|
# Create model
|
|
10
|
-
Habarico::
|
|
11
|
-
|
|
10
|
+
Habarico::ResourceReference.build(
|
|
11
|
+
generate_reference(menu_item) || resource_name,
|
|
12
12
|
action_name: generate_action_name(menu_item),
|
|
13
|
-
record_pk: generate_record_pk(menu_item)
|
|
13
|
+
record_pk: generate_record_pk(menu_item),
|
|
14
14
|
)
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -11,7 +11,7 @@ module Habarico
|
|
|
11
11
|
name: generate_name(menu_item),
|
|
12
12
|
mode: generate_mode(menu_item),
|
|
13
13
|
dialog_width: generate_dialog_width(menu_item),
|
|
14
|
-
|
|
14
|
+
reference: generate_reference(menu_item),
|
|
15
15
|
keep_linked_parent: generate_keep_linked_parent(menu_item)
|
|
16
16
|
)
|
|
17
17
|
end
|
|
@@ -38,7 +38,7 @@ module Habarico
|
|
|
38
38
|
menu_item.dialog_width
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def
|
|
41
|
+
def generate_reference(menu_item)
|
|
42
42
|
menu_view_endpoint_parameters_generator.generate(menu_item)
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
module Habarico
|
|
2
2
|
class ActionDescriptorGenerator < AbstractGenerator
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
# `list_source` is the address of the resource a surrounding box lists, which only that box
|
|
5
|
+
# knows - it is what `ResourceReference::LIST` resolves to.
|
|
6
|
+
def generate(action_reference, context, list_source: nil)
|
|
5
7
|
# Fallback to a simple ActionReference object if a string or symbol is provided
|
|
6
8
|
action_reference = Habarico::ActionReference.new(name: action_reference.to_s) if action_reference.is_a?(String) || action_reference.is_a?(Symbol)
|
|
7
9
|
|
|
10
|
+
# A group of defaults names no action, so none of the checks below apply to it
|
|
11
|
+
return generate_default_actions(action_reference, list_source) if action_reference.is_a?(Habarico::DefaultActionsReference)
|
|
12
|
+
|
|
8
13
|
# Validate arguments
|
|
9
14
|
raise Eluvia::Errors::StandardError.new('Argument `action_reference` must be a Habarico::ActionReference') unless action_reference.is_a?(Habarico::ActionReference)
|
|
10
15
|
|
|
@@ -25,6 +30,8 @@ module Habarico
|
|
|
25
30
|
icon: generate_icon(action_reference, context),
|
|
26
31
|
color: generate_color(action_reference, context),
|
|
27
32
|
action: generate_action(action_reference, context),
|
|
33
|
+
reference: resolve_reference(action_reference.reference, context, list_source),
|
|
34
|
+
confirm_dialog: generate_confirm_dialog(action_reference, context),
|
|
28
35
|
view: generate_view(action_reference, context),
|
|
29
36
|
menu_items: generate_menu_items(action_reference, context),
|
|
30
37
|
)
|
|
@@ -32,6 +39,34 @@ module Habarico
|
|
|
32
39
|
|
|
33
40
|
protected
|
|
34
41
|
|
|
42
|
+
def confirm_dialog_properties_generator
|
|
43
|
+
@confirm_dialog_properties_generator ||= definition.confirm_dialog_properties_generator.new(definition, resource_path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def generate_default_actions(action_reference, list_source)
|
|
47
|
+
Habarico::DefaultActionsDescriptor.new(
|
|
48
|
+
reference: resolve_reference(action_reference.reference, nil, list_source),
|
|
49
|
+
scope: action_reference.scope,
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The address a descriptor binds to: a name as written, or what a sentinel stands for.
|
|
54
|
+
#
|
|
55
|
+
# `CURRENT` is the resource the schema is being generated for, narrowed to the record when the
|
|
56
|
+
# request has one. `LIST` is the collection the surrounding box lists, which the box passes in.
|
|
57
|
+
def resolve_reference(reference, context, list_source)
|
|
58
|
+
case reference
|
|
59
|
+
when nil then nil
|
|
60
|
+
when Habarico::ResourceReference then reference
|
|
61
|
+
when Habarico::ResourceReference::CURRENT
|
|
62
|
+
Habarico::ResourceReference.build(resource_name, record_pk: context&.record_pk)
|
|
63
|
+
when Habarico::ResourceReference::LIST
|
|
64
|
+
raise Eluvia::Errors::StandardError.new('`ResourceReference::LIST` is stamped by the box that lists a resource, so this reference is not inside a list or gallery box') if list_source.nil?
|
|
65
|
+
list_source
|
|
66
|
+
else Habarico::ResourceReference.build(reference)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
35
70
|
def action_schema_generator
|
|
36
71
|
@action_schema_generator ||= definition.action_schema_generator.new(definition, resource_path)
|
|
37
72
|
end
|
|
@@ -56,6 +91,12 @@ module Habarico
|
|
|
56
91
|
action_reference.color
|
|
57
92
|
end
|
|
58
93
|
|
|
94
|
+
def generate_confirm_dialog(action_reference, context)
|
|
95
|
+
return nil if action_reference.confirm_dialog_title.blank? && action_reference.confirm_dialog_message.blank?
|
|
96
|
+
raise Eluvia::Errors::StandardError.new('A confirm dialog needs both a title and a message, or neither') if action_reference.confirm_dialog_title.blank? || action_reference.confirm_dialog_message.blank?
|
|
97
|
+
confirm_dialog_properties_generator.generate(action_reference, context)
|
|
98
|
+
end
|
|
99
|
+
|
|
59
100
|
def generate_action(action_reference, context)
|
|
60
101
|
if action_reference.action.present?
|
|
61
102
|
return action_schema_generator.generate(action_reference.action, context)
|
|
@@ -124,18 +124,36 @@ module Habarico
|
|
|
124
124
|
|
|
125
125
|
# Box-level toolbar (box header / card header area), relative to the box root. Emitted for all box types
|
|
126
126
|
# including 'list' - for a list box the table's own toolbar is carried separately by 'list.toolbar_actions'.
|
|
127
|
+
# A descriptor here is rendered in the parent's context, so one meant for the resource a list or
|
|
128
|
+
# gallery box points at has to name it - `ResourceReference::LIST` is how an author says that
|
|
129
|
+
# without repeating the name the box already carries.
|
|
127
130
|
def generate_toolbar_actions(_view, box, _index, context)
|
|
128
131
|
return nil if box.toolbar_actions.nil?
|
|
132
|
+
list_source = listed_resource_address(box, context)
|
|
129
133
|
box.toolbar_actions.map do |action|
|
|
130
|
-
action_descriptor_generator.generate(action, context)
|
|
134
|
+
action_descriptor_generator.generate(action, context, list_source: list_source)
|
|
131
135
|
end.compact
|
|
132
136
|
end
|
|
133
137
|
|
|
134
|
-
#
|
|
138
|
+
# What `ResourceReference::LIST` stands for inside this box: the collection it lists, with the
|
|
139
|
+
# query its own schema request carries. Only a list or gallery box has one.
|
|
140
|
+
LISTING_BOX_TYPES = %i[list gallery].freeze
|
|
141
|
+
|
|
142
|
+
def listed_resource_address(box, context)
|
|
143
|
+
return nil unless LISTING_BOX_TYPES.include?(box.type&.to_sym)
|
|
144
|
+
return nil if box.reference.blank?
|
|
145
|
+
|
|
146
|
+
link = Habarico::BoxHelper.parent_link_params(box, context, resource_name)
|
|
147
|
+
Habarico::ResourceReference.build(box.reference, params: (box.schema_params || {}).merge(link || {}).presence)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Box-level bottom actions (rendered below the box fields), relative to the box root - the same
|
|
151
|
+
# parent context as the header, so a descriptor here resolves `ResourceReference::LIST` too.
|
|
135
152
|
def generate_bottom_actions(_view, box, _index, context)
|
|
136
153
|
return nil if box.bottom_actions.nil?
|
|
154
|
+
list_source = listed_resource_address(box, context)
|
|
137
155
|
box.bottom_actions.map do |action|
|
|
138
|
-
action_descriptor_generator.generate(action, context)
|
|
156
|
+
action_descriptor_generator.generate(action, context, list_source: list_source)
|
|
139
157
|
end.compact
|
|
140
158
|
end
|
|
141
159
|
|