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
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ Metrics/ClassLength:
- 'packages/forest_admin_agent/lib/forest_admin_agent/routes/action/action.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/collection_customizer.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/relation/relation_collection_decorator.rb'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/rename_field/rename_field_collection_decorator.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/utils/collection.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/aggregation.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/filter_factory.rb'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def handle_request(args = {})
return {
name: args[:params]['collection_name'],
content: {
count: result.empty? ? 0 : result[0][:value]
count: result.empty? ? 0 : result[0]['value']
}
}
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module Resources
collection = Collection.new(datasource, 'user')
allow(collection).to receive(:aggregate).and_return(
[
{ value: 1, group: [] }
{ 'value' => 1, 'group' => [] }
]
)
allow(ForestAdminAgent::Builder::AgentFactory.instance).to receive(:send_schema).and_return(nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def respond_to?(arg)
'published_at' => '2000-07-07T21:00:00.000Z',
'price' => 6.75
}
book = Book.new(1, attributes['title'], attributes['published_at'], attributes['price'])
book = { 'id' => 1 }.merge(attributes)

datasource = Datasource.new
collection = instance_double(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ def respond_to?(arg)
it 'call update and return an serialized content' do
args[:params][:data] = { attributes: { 'title' => 'Harry potter and the goblet of fire' } }
args[:params]['id'] = '1'
book = Book.new(1, 'Harry potter and the goblet of fire')
book = { 'id' => 1, 'title' => 'Harry potter and the goblet of fire' }

allow(@datasource.get_collection('book')).to receive_messages(list: [book], update: true)
result = update.handle_request(args)
expect(result[:name]).to eq('book')
Expand All @@ -90,7 +91,7 @@ def respond_to?(arg)
it 'call update with the expected args' do
args[:params][:data] = { attributes: { 'title' => 'Harry potter and the goblet of fire' } }
args[:params]['id'] = '1'
book = Book.new(1, 'Harry potter and the goblet of fire')
book = { 'id' => 1, 'title' => 'Harry potter and the goblet of fire' }
allow(@datasource.get_collection('book')).to receive_messages(list: [book], update: true)
update.handle_request(args)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class DecoratorsStack
include ForestAdminDatasourceToolkit::Decorators

attr_reader :datasource, :schema, :search, :early_computed, :late_computed, :action, :relation, :late_op_emulate,
:early_op_emulate, :validation, :sort
:early_op_emulate, :validation, :sort, :rename_field

def initialize(datasource)
@customizations = []
Expand All @@ -26,6 +26,8 @@ def initialize(datasource)
last = @action = DatasourceDecorator.new(last, Action::ActionCollectionDecorator)
last = @schema = DatasourceDecorator.new(last, Schema::SchemaCollectionDecorator)
last = @validation = DatasourceDecorator.new(last, Validation::ValidationCollectionDecorator)

last = @rename_field = DatasourceDecorator.new(last, RenameField::RenameFieldCollectionDecorator)
@datasource = last
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
module ForestAdminDatasourceCustomizer
module Decorators
module RenameField
class RenameFieldCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::CollectionDecorator
include ForestAdminDatasourceToolkit::Decorators
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree

attr_accessor :from_child_collection, :to_child_collection

def initialize(child_collection, datasource)
super
@from_child_collection = {}
@to_child_collection = {}
end

def rename_field(current_name, new_name)
unless schema[:fields][current_name]
raise ForestAdminDatasourceToolkit::Exceptions::ForestException, "No such field '#{current_name}'"
end

initial_name = current_name

ForestAdminDatasourceToolkit::Validations::FieldValidator.validate_name(name, new_name)

# Do not update arrays if renaming is a no-op (ie: customer is cancelling a previous rename)
return unless initial_name != new_name

from_child_collection[initial_name] = new_name
to_child_collection[new_name] = initial_name
mark_all_schema_as_dirty
end

def refine_schema(sub_schema)
fields = {}

sub_schema[:fields].each do |old_name, schema|
case schema.type
when 'ManyToOne'
schema.foreign_key = from_child_collection[schema.foreign_key] || schema.foreign_key
when 'OneToMany', 'OneToOne'
relation = datasource.get_collection(schema.foreign_collection)
schema.origin_key = relation.from_child_collection[schema.origin_key] || schema.origin_key
when 'ManyToMany'
through = datasource.get_collection(schema.through_collection)
schema.foreign_key = through.from_child_collection[schema.foreign_key] || schema.foreign_key
schema.origin_key = through.from_child_collection[schema.origin_key] || schema.origin_key
end

fields[from_child_collection[old_name] || old_name] = schema
end

sub_schema[:fields] = fields

sub_schema
end

def refine_filter(_caller, filter = nil)
filter&.override(
condition_tree: filter.condition_tree&.replace_fields do |field|
path_to_child_collection(field)
end,
sort: filter.sort&.replace_clauses do |clause|
{
field: path_to_child_collection(clause[:field]),
ascending: clause[:ascending]
}
end
)
end

def create(caller, data)
record = super(
caller,
record_to_child_collection(data)
)

record_from_child_collection(record)
end

def list(caller, filter, projection)
child_projection = projection.replace { |field| path_to_child_collection(field) }
records = super(caller, filter, child_projection)
return records if child_projection == projection

records.map { |record| record_from_child_collection(record) }
end

def update(caller, filter, patch)
super(caller, filter, record_to_child_collection(patch))
end

def aggregate(caller, filter, aggregation, limit = nil)
rows = super(
caller,
filter,
aggregation.replace_fields { |field| path_to_child_collection(field) },
limit
)

rows.map do |row|
{
'value' => row['value'],
'group' => row['group']&.reduce({}) do |memo, group|
path, value = group
memo.merge({ path_from_child_collection(path) => value })
end
}
end
end

# rubocop:disable Lint/UselessMethodDefinition
def mark_schema_as_dirty
super
end
# rubocop:enable Lint/UselessMethodDefinition

def mark_all_schema_as_dirty
datasource.collections.each_value(&:mark_schema_as_dirty)
end

# Convert field path from child collection to this collection
def path_from_child_collection(path)
if path.include?(':')
paths = path.split(':')
child_field = paths[0]
relation_name = from_child_collection[child_field] || child_field
relation_schema = schema[:fields][relation_name]
relation = datasource.get_collection(relation_schema.foreign_collection)

return "#{relation_name}:#{relation.path_from_child_collection(paths[1])}"
end

from_child_collection[path] ||= path
end

# Convert field path from this collection to child collection
def path_to_child_collection(path)
if path.include?(':')
paths = path.split(':')
relation_name = paths[0]
relation_schema = schema[:fields][relation_name]
relation = datasource.get_collection(relation_schema.foreign_collection)
child_field = to_child_collection[relation_name] || relation_name

return "#{child_field}:#{relation.path_to_child_collection(paths[1])}"
end

to_child_collection[path] ||= path
end

# Convert record from this collection to the child collection
def record_to_child_collection(record)
child_record = {}
record.each do |field, value|
child_record[to_child_collection[field] || field] = value
end

child_record
end

def record_from_child_collection(child_record)
record = {}
child_record.each do |child_field, value|
field = from_child_collection[child_field] || child_field
field_schema = schema[:fields][field]

# Perform the mapping, recurse for relations
if field_schema.type == 'Column' || value.nil?
record[field] = value
else
relation = datasource.get_collection(field_schema.foreign_collection)
record[field] = relation.record_from_child_collection(value)
end
end

record
end
end
end
end
end
Loading