Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,6 +227,7 @@ Metrics/MethodLength:
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/actions/action_field_factory.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/action/base_action.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/rename_field/rename_field_collection_decorator.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/action/action_collection_decorator.rb'

Metrics/BlockLength:
Exclude:
Expand DownExpand Up@@ -258,6 +259,7 @@ Metrics/ClassLength:
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/validations/rules.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/generator_action_field_widget.rb'
- 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb'
- 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/generator_action.rb'

Style/OpenStructUse:
Exclude:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
{
"name": "agent-php",
"name": "agent-ruby",
"version": "1.0.0-beta.66",
"description": "The official agent Ruby for Forest.",
"private": false,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,7 +69,11 @@ def handle_request(args = {})
)

# Now that we have the field list, we can parse the data again.
data = Schema::ForestValueConverter.make_form_data(@datasource, raw_data, fields)
data = Schema::ForestValueConverter.make_form_data(
@datasource,
raw_data,
fields.reject { |field| field.type == 'Layout' }
)

{ content: @collection.execute(@caller, @action_name, data, filter_for_caller) }
end
Expand All@@ -82,7 +86,7 @@ def handle_hook_request(args = {})
search_values = {}
forest_fields&.each { |field| search_values[field['field']] = field['searchValue'] }

fields = @collection.get_form(
form = @collection.get_form(
@caller,
@action_name,
data,
Expand All@@ -94,10 +98,12 @@ def handle_hook_request(args = {})
includeHiddenFields: false
}
)
form_elements = Schema::GeneratorAction.extract_fields_and_layout(form)

{
content: {
fields: fields&.map { |field| Schema::GeneratorAction.build_field_schema(@datasource, field) } || {}
fields: Schema::GeneratorAction.build_fields(@collection, form_elements[:fields]),
layout: Schema::GeneratorAction.build_layout(form_elements[:layout])
}
}
end
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,8 @@ module ForestAdminAgent
module Utils
module Schema
class GeneratorAction
include ForestAdminDatasourceToolkit::Components

DEFAULT_FIELDS = [
{
field: 'Loading...',
Expand All@@ -23,27 +25,46 @@ def self.get_action_slug(name)
end

def self.build_schema(collection, name)
schema = collection.schema[:actions][name]
action = collection.schema[:actions][name]
action_index = collection.schema[:actions].keys.index(name)
slug = get_action_slug(name)
fields = build_fields(collection, name, schema)

{
form_elements = extract_fields_and_layout(collection.get_form(nil, name))
if action.static_form? && form_elements[:layout].empty?
# if action.static_form?
fields = build_fields(collection, form_elements[:fields])
layout = form_elements[:layout]
else
fields = DEFAULT_FIELDS
layout = nil
end

schema = {
id: "#{collection.name}-#{action_index}-#{slug}",
name: name,
type: schema.scope.downcase,
type: action.scope.downcase,
baseUrl: nil,
endpoint: "/forest/_actions/#{collection.name}/#{action_index}/#{slug}",
httpMethod: 'POST',
redirect: nil, # frontend ignores this attribute
download: schema.is_generate_file,
download: action.is_generate_file,
fields: fields,
hooks: {
load: !schema.static_form?,
load: !action.static_form?,
# Always registering the change hook has no consequences, even if we don't use it.
change: ['changeHook']
}
}

return schema unless layout && !layout.empty?

schema[:layout] = build_layout(layout)

schema
end

def self.build_layout_schema(field)
{ **field.to_h, component: field.component.downcase }
end

def self.build_field_schema(datasource, field)
Expand DownExpand Up@@ -76,26 +97,49 @@ def self.build_field_schema(datasource, field)
output
end

class << self
private

def build_fields(collection, name, action)
return DEFAULT_FIELDS unless action.static_form?
def self.build_fields(collection, fields)
if fields
return fields.map do |field|
new_field = build_field_schema(collection.datasource, field)
new_field[:default_value] = new_field[:value]
new_field.delete(:value)

fields = collection.get_form(nil, name)
new_field
end
end

if fields
return fields.map do |field|
new_field = build_field_schema(collection.datasource, field)
new_field[:default_value] = new_field[:value]
new_field.delete(:value)
[]
end

new_field
end
def self.build_layout(elements)
if elements
return elements.map do |element|
build_layout_schema(element)
end
end

[]
end

def self.extract_fields_and_layout(form)
fields = []
layout = []
has_real_layout = false

[]
form&.each do |element|
if element.type == Actions::FieldType::LAYOUT
layout << element
has_real_layout = true
else
fields << element
# frontend rule
layout << Actions::ActionLayoutElement::InputElement.new(component: 'Input', field_id: element.label)
end
end

layout = [] unless has_real_layout

{ fields: fields, layout: layout }
end
end
end
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -66,6 +66,7 @@ def serialize(schema)
schema[:collections].each do |collection|
collection_actions = collection[:actions]
collection_segments = collection[:segments]

collection.delete(:actions)
collection.delete(:segments)

Expand DownExpand Up@@ -94,6 +95,7 @@ def serialize(schema)

def get_smart_features_by_collection(type, data, with_attributes: false)
smart_features = []

data.each do |value|
smart_feature = { id: value[:id], type: type }
smart_feature[:attributes] = value if with_attributes
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -162,7 +162,7 @@ module Action

describe 'handle_hook' do
it 'generate a clean form if called without params' do
allow(@action_collection).to receive(:get_form)
allow(@action_collection).to receive(:get_form).and_return([])
action.handle_hook_request(args)
expect(@action_collection).to have_received(:get_form) do |caller, action, data, filter, meta|
expect(caller).to be_instance_of(Components::Caller)
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -200,6 +200,86 @@ module Schema
)
end
end

describe 'with layout element' do
before do
@collection = collection_build(
schema: {
actions: {
'Send email' => BaseAction.new(
scope: Types::ActionScope::SINGLE
)
}
},
get_form: [
ActionField.new(label: 'label', type: 'String'),
ActionLayoutElement::SeparatorElement.new
]
)
end

describe 'build_schema' do
it 'generate schema correctly' do
schema = described_class.build_schema(@collection, 'Send email')

expect(schema).to eq(
{
id: 'collection-0-send-email',
name: 'Send email',
type: 'single',
baseUrl: nil,
endpoint: '/forest/_actions/collection/0/send-email',
httpMethod: 'POST',
redirect: nil,
download: false,
fields: [
{
defaultValue: 'Form is loading',
description: '',
enums: nil,
field: 'Loading...',
hook: nil,
isReadOnly: true,
isRequired: false,
reference: nil,
type: 'String',
value: nil,
widgetEdit: nil
}
],
# uncomment when back validations will be done ...
# fields: [
# {
# default_value: nil,
# description: nil,
# field: 'label',
# isReadOnly: false,
# isRequired: false,
# type: 'String',
# widgetEdit: nil
# }
# ],
# layout: [
# { component: 'input', fieldId: 'label', type: 'Layout' },
# { component: 'separator', type: 'Layout' }
# ],
hooks: { load: false, change: ['changeHook'] }
}
)
end
end

describe 'extract_fields_and_layout' do
it 'return fields and layout separately from form' do
result = described_class.extract_fields_and_layout(@collection.get_form(nil, 'Send email'))

expect(result).to have_key(:fields)
expect(result).to have_key(:layout)
expect(result[:fields]).to all(be_a(ActionField))
expect(result[:layout]).to all(be_a(ActionLayoutElement::BaseLayoutElement))
end
end
end
end
end
end
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,7 +10,7 @@ def initialize(child_collection, datasource)
end

def add_action(name, action)
action.build_fields
action.build_elements
@actions[name] = action

mark_schema_as_dirty
Expand DownExpand Up@@ -51,6 +51,8 @@ def get_form(caller, name, data = nil, filter = nil, metas = {})
fields = drop_deferred(context, metas[:search_values], dynamic_fields)

fields.each do |field|
next if field.type == 'Layout'

if field.value.nil?
# customer did not define a handler to rewrite the previous value => reuse current one.
field.value = form_values[field.label]
Expand All@@ -72,13 +74,20 @@ def refine_schema(sub_schema)
private

def drop_defaults(context, fields, data)
unvalued_fields = fields.reject { |field| data.key?(field.label) }
defaults = unvalued_fields.map { |field| evaluate(context, field.default_value) }
unvalued_fields.each_with_index { |field, index| data[field.label] = defaults[index] }
fields.map do |field|
if field.type == 'Layout'
field
else
drop_default(context, field, data)
end
end
end

fields.each { |field| field.default_value = nil }
def drop_default(context, field, data)
data[field.label] = evaluate(context, field.default_value) unless data.key?(field.label)
field.default_value = nil

fields
field
end

def drop_ifs(context, fields)
Expand All@@ -105,7 +114,8 @@ def drop_deferred(context, search_values, fields)
value = field.send(key)
key = key.to_s.concat('=').to_sym

field.send(key, evaluate(context, value, search_values&.dig(field.label)))
search_value = field.type == 'Layout' ? nil : search_values&.dig(field.label)
field.send(key, evaluate(context, value, search_value))
end

new_fields << Actions::ActionFieldFactory.build(field.to_h)
Expand Down
Loading