Skip to content

Commit 3d66b3b

Browse files
fix(smartaction): register custom endpoint for load/changes hooks (#626)
Co-authored-by: Nicolas Alexandre <[email protected]>
1 parent 0cde34e commit 3d66b3b

File tree

4 files changed

+160
-133
lines changed

4 files changed

+160
-133
lines changed

config/routes.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
delete ':collection/:id', to: router
6060
delete ':collection', to: router
6161

62-
# Smart Actions forms value
63-
post 'actions/:action_name/hooks/load' => 'actions#load'
64-
post 'actions/:action_name/hooks/change' => 'actions#change'
62+
draw(:actions)
63+
6564
end

config/routes/actions.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
ForestLiana.apimap.each do |collection|
2+
if !collection.actions.empty?
3+
collection.actions.each do |action|
4+
if action.hooks && action.hooks[:load].present?
5+
post action.endpoint.sub('forest', '') + '/hooks/load' => 'actions#load', action_name: ActiveSupport::Inflector.parameterize(action.name)
6+
end
7+
if action.hooks && action.hooks[:change].present?
8+
post action.endpoint.sub('forest', '') + '/hooks/change' => 'actions#change', action_name: ActiveSupport::Inflector.parameterize(action.name)
9+
end
10+
end
11+
end
12+
end

spec/dummy/lib/forest_liana/collections/island.rb

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,106 @@ class Forest::Island
33

44
collection :Island
55

6-
action 'my_action'
6+
foo = {
7+
field: 'foo',
8+
type: 'String',
9+
default_value: nil,
10+
enums: nil,
11+
is_required: false,
12+
is_read_only: false,
13+
reference: nil,
14+
description: nil,
15+
widget: nil,
16+
hook: 'on_foo_changed'
17+
}
18+
enum = {
19+
field: 'enum',
20+
type: 'Enum',
21+
enums: %w[a b c],
22+
}
23+
multiple_enum = {
24+
field: 'multipleEnum',
25+
type: ['Enum'],
26+
enums: %w[a b c],
27+
}
28+
29+
action 'my_action',
30+
fields: [foo],
31+
hooks: {
32+
:load => -> (context) {
33+
context[:fields]
34+
},
35+
:change => {
36+
'on_foo_changed' => -> (context) {
37+
foo = context[:fields].find{|field| field[:field] == 'foo'}
38+
foo[:value] = 'baz'
39+
context[:fields]
40+
}
41+
}
42+
}
43+
44+
action 'fail_action',
45+
fields: [foo],
46+
hooks: {
47+
:load => -> (context) {
48+
1
49+
},
50+
:change => {
51+
'on_foo_changed' => -> (context) {
52+
1
53+
}
54+
}
55+
}
56+
57+
action 'cheat_action',
58+
fields: [foo],
59+
hooks: {
60+
:load => -> (context) {
61+
{}
62+
},
63+
:change => {
64+
'on_foo_changed' => -> (context) {
65+
context[:fields]['baz'] = foo.clone.update({field: 'baz'})
66+
context[:fields]
67+
}
68+
}
69+
}
70+
71+
action 'enums_action',
72+
endpoint: 'forest/custom/islands/enums_action',
73+
fields: [foo, enum],
74+
hooks: {
75+
:change => {
76+
'on_foo_changed' => -> (context) {
77+
fields = context[:fields]
78+
enum_field = fields.find{|field| field[:field] == 'enum'}
79+
enum_field[:enums] = %w[c d e]
80+
fields
81+
}
82+
}
83+
}
84+
85+
action 'multiple_enums_action',
86+
fields: [foo, multiple_enum],
87+
hooks: {
88+
:change => {
89+
'on_foo_changed' => -> (context) {
90+
fields = context[:fields]
91+
enum_field = fields.find{|field| field[:field] == 'multipleEnum'}
92+
enum_field[:enums] = %w[c d z]
93+
fields
94+
}
95+
}
96+
}
97+
98+
action 'use_user_context',
99+
fields: [foo],
100+
hooks: {
101+
:load => -> (context) {
102+
foo = context[:fields].find{|field| field[:field] == 'foo'}
103+
foo[:value] = context[:user]['first_name']
104+
context[:fields]
105+
}
106+
}
107+
7108
end

spec/requests/actions_controller_spec.rb

Lines changed: 44 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -39,124 +39,7 @@
3939
}
4040

4141
describe 'hooks' do
42-
foo = {
43-
field: 'foo',
44-
type: 'String',
45-
default_value: nil,
46-
enums: nil,
47-
is_required: false,
48-
is_read_only: false,
49-
reference: nil,
50-
description: nil,
51-
widget: nil,
52-
hook: 'on_foo_changed'
53-
}
54-
enum = {
55-
field: 'enum',
56-
type: 'Enum',
57-
enums: %w[a b c],
58-
}
59-
multiple_enum = {
60-
field: 'multipleEnum',
61-
type: ['Enum'],
62-
enums: %w[a b c],
63-
}
64-
65-
action_definition = {
66-
name: 'my_action',
67-
fields: [foo],
68-
hooks: {
69-
:load => -> (context) {
70-
context[:fields]
71-
},
72-
:change => {
73-
'on_foo_changed' => -> (context) {
74-
foo = context[:fields].find{|field| field[:field] == 'foo'}
75-
foo[:value] = 'baz'
76-
context[:fields]
77-
}
78-
}
79-
}
80-
}
81-
fail_action_definition = {
82-
name: 'fail_action',
83-
fields: [foo],
84-
hooks: {
85-
:load => -> (context) {
86-
1
87-
},
88-
:change => {
89-
'on_foo_changed' => -> (context) {
90-
1
91-
}
92-
}
93-
}
94-
}
95-
cheat_action_definition = {
96-
name: 'cheat_action',
97-
fields: [foo],
98-
hooks: {
99-
:load => -> (context) {
100-
{}
101-
},
102-
:change => {
103-
'on_foo_changed' => -> (context) {
104-
context[:fields]['baz'] = foo.clone.update({field: 'baz'})
105-
context[:fields]
106-
}
107-
}
108-
}
109-
}
110-
enums_action_definition = {
111-
name: 'enums_action',
112-
fields: [foo, enum],
113-
hooks: {
114-
:change => {
115-
'on_foo_changed' => -> (context) {
116-
fields = context[:fields]
117-
enum_field = fields.find{|field| field[:field] == 'enum'}
118-
enum_field[:enums] = %w[c d e]
119-
fields
120-
}
121-
}
122-
}
123-
}
124-
125-
multiple_enums_action_definition = {
126-
name: 'multiple_enums_action',
127-
fields: [foo, multiple_enum],
128-
hooks: {
129-
:change => {
130-
'on_foo_changed' => -> (context) {
131-
fields = context[:fields]
132-
enum_field = fields.find{|field| field[:field] == 'multipleEnum'}
133-
enum_field[:enums] = %w[c d z]
134-
fields
135-
}
136-
}
137-
}
138-
}
139-
140-
use_user_context_action_definition = {
141-
name: 'use_user_context',
142-
fields: [foo],
143-
hooks: {
144-
:load => -> (context) {
145-
foo = context[:fields].find{|field| field[:field] == 'foo'}
146-
foo[:value] = context[:user]['first_name']
147-
context[:fields]
148-
}
149-
}
150-
}
151-
152-
action = ForestLiana::Model::Action.new(action_definition)
153-
fail_action = ForestLiana::Model::Action.new(fail_action_definition)
154-
cheat_action = ForestLiana::Model::Action.new(cheat_action_definition)
155-
enums_action = ForestLiana::Model::Action.new(enums_action_definition)
156-
multiple_enums_action = ForestLiana::Model::Action.new(multiple_enums_action_definition)
157-
use_user_context_action = ForestLiana::Model::Action.new(use_user_context_action_definition)
15842
island = ForestLiana.apimap.find {|collection| collection.name.to_s == ForestLiana.name_for(Island)}
159-
island.actions = [action, fail_action, cheat_action, enums_action, multiple_enums_action, use_user_context_action]
16043

16144
describe 'call /load' do
16245
params = {
@@ -167,6 +50,8 @@
16750

16851
it 'should respond 200' do
16952
post '/forest/actions/my_action/hooks/load', params: JSON.dump(params), headers: headers
53+
action = island.actions.select { |action| action.name == 'my_action' }.first
54+
foo = action.fields.select { |field| field[:field] == 'foo' }.first
17055
expect(response.status).to eq(200)
17156
expect(JSON.parse(response.body)).to eq({'fields' => [foo.merge({:value => nil}).transform_keys { |key| key.to_s.camelize(:lower) }.stringify_keys]})
17257
end
@@ -190,26 +75,30 @@
19075

19176
it 'should return the first_name of the user who call the action' do
19277
post '/forest/actions/use_user_context/hooks/load', params: JSON.dump(params), headers: headers
78+
action = island.actions.select { |action| action.name == 'use_user_context' }.first
79+
foo = action.fields.select { |field| field[:field] == 'foo' }.first
19380
expect(response.status).to eq(200)
19481
expect(JSON.parse(response.body)).to eq({'fields' => [foo.merge({:value => 'Michael'}).transform_keys { |key| key.to_s.camelize(:lower) }.stringify_keys]})
19582
end
19683
end
19784

19885
describe 'call /change' do
199-
updated_foo = foo.clone.merge({:previousValue => nil, :value => 'bar'})
200-
params = {
201-
data: {
202-
attributes: {
203-
ids: [1],
204-
fields: [updated_foo],
205-
collection_name: 'Island',
206-
changed_field: 'foo',
207-
is_read_only: true
86+
it 'should respond 200' do
87+
action = island.actions.select { |action| action.name == 'my_action' }.first
88+
foo = action.fields.select { |field| field[:field] == 'foo' }.first
89+
updated_foo = foo.clone.merge({:previousValue => nil, :value => 'bar'})
90+
params = {
91+
data: {
92+
attributes: {
93+
ids: [1],
94+
fields: [updated_foo],
95+
collection_name: 'Island',
96+
changed_field: 'foo',
97+
is_read_only: true
98+
}
20899
}
209100
}
210-
}
211101

212-
it 'should respond 200' do
213102
post '/forest/actions/my_action/hooks/change', params: JSON.dump(params), headers: headers
214103
expect(response.status).to eq(200)
215104
expected = updated_foo.clone.merge({:value => 'baz'})
@@ -226,12 +115,31 @@
226115
end
227116

228117
it 'should respond 500 with bad hook result type' do
118+
action = island.actions.select { |action| action.name == 'fail_action' }.first
119+
foo = action.fields.select { |field| field[:field] == 'foo' }.first
120+
updated_foo = foo.clone.merge({:previousValue => nil, :value => 'bar'})
121+
params = {
122+
data: {
123+
attributes: {
124+
ids: [1],
125+
fields: [updated_foo],
126+
collection_name: 'Island',
127+
changed_field: 'foo',
128+
is_read_only: true
129+
}
130+
}
131+
}
132+
229133
post '/forest/actions/fail_action/hooks/change', params: JSON.dump(params), headers: headers
230134
expect(response.status).to eq(500)
231135
expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
232136
end
233137

234138
it 'should reset value when enums has changed' do
139+
action = island.actions.select { |action| action.name == 'enums_action' }.first
140+
foo = action.fields.select { |field| field[:field] == 'foo' }.first
141+
enum = action.fields.select { |field| field[:field] == 'enum' }.first
142+
updated_foo = foo.clone.merge({:previousValue => nil, :value => 'bar'})
235143
updated_enum = enum.clone.merge({:previousValue => nil, :value => 'a'}) # set value to a
236144
p = {
237145
data: {
@@ -243,7 +151,8 @@
243151
}
244152
}
245153
}
246-
post '/forest/actions/enums_action/hooks/change', params: JSON.dump(p), headers: headers
154+
155+
post '/forest/custom/islands/enums_action/hooks/change', params: JSON.dump(p), headers: headers
247156
expect(response.status).to eq(200)
248157

249158
expected_enum = updated_enum.clone.merge({ :enums => %w[c d e], :value => nil, :widgetEdit => nil})
@@ -258,6 +167,9 @@
258167
end
259168

260169
it 'should not reset value when every enum values are in the enums definition' do
170+
action = island.actions.select { |action| action.name == 'multiple_enums_action' }.first
171+
foo = action.fields.select { |field| field[:field] == 'foo' }.first
172+
multiple_enum = action.fields.select { |field| field[:field] == 'multipleEnum' }.first
261173
updated_multiple_enum = multiple_enum.clone.merge({:previousValue => nil, :value => %w[c]})
262174
p = {
263175
data: {
@@ -284,6 +196,9 @@
284196
end
285197

286198
it 'should reset value when one of the enum values is not in the enums definition' do
199+
action = island.actions.select { |action| action.name == 'multiple_enums_action' }.first
200+
foo = action.fields.select { |field| field[:field] == 'foo' }.first
201+
multiple_enum = action.fields.select { |field| field[:field] == 'multipleEnum' }.first
287202
wrongly_updated_multiple_enum = multiple_enum.clone.merge({:previousValue => nil, :value => %w[a b]})
288203
p = {
289204
data: {

0 commit comments

Comments
 (0)