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
3 changes: 2 additions & 1 deletion packages/forest_admin_agent/spec/shared/factory.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,8 @@ def collection_build(args = {})
charts: [],
fields: {},
countable: false,
searchable: false
searchable: false,
segments: {}
}.merge(args[:schema]),
execute: nil,
get_form: nil,
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -238,6 +238,18 @@ def add_hook(position, type, &handler)
push_customization { @stack.hook.get_collection(@name).add_hook(position, type, handler) }
end

# Add a new segment on the collection.
# @param name the name of the segment
# @param definition a function used to generate a condition tree or a condition tree
# @example
# .add_segment(
# 'Wrote more than 2 books',
# { field: 'booksCount', operator: 'GreaterThan', value: 2 }
# );
def add_segment(name, definition)
push_customization { @stack.segment.get_collection(@name).add_segment(name, definition) }
end

private

def push_customization(&customization)
Expand Down
Original file line numberDiff line numberDiff 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, :rename_field, :publication, :write, :chart, :hook
:early_op_emulate, :validation, :sort, :rename_field, :publication, :write, :chart, :hook, :segment

def initialize(datasource)
@customizations = []
Expand All@@ -22,6 +22,7 @@ def initialize(datasource)
last = DatasourceDecorator.new(last, OperatorsEquivalence::OperatorsEquivalenceCollectionDecorator)

last = @search = DatasourceDecorator.new(last, Search::SearchCollectionDecorator)
last = @segment = DatasourceDecorator.new(last, Segment::SegmentCollectionDecorator)
last = @sort = DatasourceDecorator.new(last, Sort::SortCollectionDecorator)

last = @chart = Chart::ChartDatasourceDecorator.new(last)
Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
module ForestAdminDatasourceCustomizer
module Decorators
module Segment
class SegmentCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::CollectionDecorator
include ForestAdminDatasourceToolkit::Decorators
include ForestAdminDatasourceToolkit::Validations
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree

attr_reader :segments

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

def add_segment(name, definition)
@segments[name] = definition

mark_schema_as_dirty
end

def refine_schema(sub_schema)
sub_schema[:segments] = sub_schema[:segments].merge(@segments)

sub_schema
end

def refine_filter(caller, filter = nil)
return nil unless filter

condition_tree = filter.condition_tree
segment = filter.segment

if segment && @segments.key?(segment)
definition = @segments[segment]

result = if definition.respond_to? :call
definition.call(Context::CollectionCustomizationContext.new(self, caller))
else
definition
end

condition_tree_segment = if result.is_a? Nodes::ConditionTree
result
else
ConditionTreeFactory.from_plain_object(result)
end

ConditionTreeValidator.validate(condition_tree_segment, self)

condition_tree = ConditionTreeFactory.intersect([condition_tree_segment, filter.condition_tree])
segment = nil
end

filter.override(condition_tree: condition_tree, segment: segment)
end
end
end
end
end
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,8 +12,7 @@ module ForestAdminDatasourceCustomizer
include_context 'with caller'
before do
datasource = Datasource.new
collection_book = instance_double(
Collection,
collection_book = collection_build(
name: 'book',
schema: {
charts: [],
Expand All@@ -40,8 +39,7 @@ module ForestAdminDatasourceCustomizer
}
)

collection_book_person = instance_double(
Collection,
collection_book_person = collection_build(
name: 'book_person',
schema: {
charts: [],
Expand All@@ -63,8 +61,7 @@ module ForestAdminDatasourceCustomizer
}
)

collection_person = instance_double(
Collection,
collection_person = collection_build(
name: 'person',
schema: {
charts: [],
Expand All@@ -89,8 +86,7 @@ module ForestAdminDatasourceCustomizer
}
)

collection_category = instance_double(
Collection,
collection_category = collection_build(
name: 'category',
schema: {
charts: [],
Expand DownExpand Up@@ -486,5 +482,22 @@ module ForestAdminDatasourceCustomizer
expect(hook_collection.hooks['list'].before.first).to eq(handler)
end
end

context 'when using add_segment' do
it 'add a segment' do
stack = @datasource_customizer.stack
stack.apply_queued_customizations({})
allow(stack.segment).to receive(:get_collection).with('book').and_return(@datasource_customizer.stack.segment.get_collection('book'))

customizer = described_class.new(@datasource_customizer, @datasource_customizer.stack, 'book')
definition = proc { Nodes::ConditionTreeLeaf.new('title', Operators::EQUAL, 'foo') }
customizer.add_segment('foo_segment', definition)
stack.apply_queued_customizations({})
segment_collection = @datasource_customizer.stack.segment.get_collection('book')

expect(segment_collection.segments).to have_key('foo_segment')
expect(segment_collection.segments['foo_segment']).to eq(definition)
end
end
end
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
require 'spec_helper'

module ForestAdminDatasourceCustomizer
module Decorators
module Segment
include ForestAdminDatasourceToolkit
include ForestAdminDatasourceToolkit::Components::Query
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
include ForestAdminDatasourceToolkit::Decorators
include ForestAdminDatasourceToolkit::Schema

describe SegmentCollectionDecorator do
before do
datasource = Datasource.new
@collection = collection_build(
name: 'book',
schema: {
fields: {
'name' => column_build({ filter_operators: [Operators::EQUAL, Operators::IN] })
}
}
)
datasource.add_collection(@collection)

@decorated_datasource = DatasourceDecorator.new(datasource, described_class)
@decorated_collection = @decorated_datasource.get_collection('book')
end

context 'when there is no filter' do
describe 'refine_filter' do
it 'return nil' do
condition_tree_generator = instance_double(Proc, call: nil)
@decorated_collection.add_segment('segment_name', condition_tree_generator)

filter = @decorated_collection.refine_filter(caller)

expect(filter).to be_nil
expect(condition_tree_generator).not_to have_received(:call)
end
end
end

context 'when there is a filter' do
context 'when the segment is not managed by this decorator' do
describe 'refine_filter' do
it 'return the given filter' do
condition_tree_generator = instance_double(Proc, call: nil)
@decorated_collection.add_segment('segment_name', condition_tree_generator)

a_filter = Filter.new(segment: 'a_segment')
filter = @decorated_collection.refine_filter(caller, a_filter)

expect(filter.to_h).to eq(a_filter.to_h)
expect(condition_tree_generator).not_to have_received(:call)
end
end
end

context 'when the segment is managed by this decorator' do
describe 'refine_filter' do
it 'return the filter with the merged conditionTree' do
condition_tree_generator = Nodes::ConditionTreeLeaf.new('name', Operators::EQUAL, 'foo')
@decorated_collection.add_segment('segment_name', condition_tree_generator)
a_filter = Filter.new(
segment: 'segment_name',
condition_tree: Nodes::ConditionTreeLeaf.new('name', Operators::EQUAL, 'other_value')
)
filter = @decorated_collection.refine_filter(caller, a_filter)

expect(filter.segment).to be_nil
expect(filter.condition_tree.to_h).to eq(
{
aggregator: 'And',
conditions: [
{ field: 'name', operator: Operators::EQUAL, value: 'foo' },
{ field: 'name', operator: Operators::EQUAL, value: 'other_value' }
]
}
)
end

it 'raise an error when a conditionTree is not valid' do
condition_tree_generator = instance_double(
Proc,
call: Nodes::ConditionTreeLeaf.new('do not exists', Operators::EQUAL, 'foo')
)
@decorated_collection.add_segment('segment_name', condition_tree_generator)

expect do
@decorated_collection.refine_filter(caller, Filter.new(segment: 'segment_name'))
end.to raise_error(Exceptions::ForestException, '🌳🌳🌳 Column not found book.do not exists')
end
end
end
end
end
end
end
end
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,10 +30,12 @@ def collection_build(args = {})
datasource: ForestAdminDatasourceToolkit::Datasource.new,
name: 'collection',
schema: {
charts: [],
fields: {},
countable: false,
searchable: false
},
searchable: false,
segments: {}
}.merge(args[:schema] || {}),
execute: nil,
get_form: nil,
render_chart: nil,
Expand All@@ -42,7 +44,7 @@ def collection_build(args = {})
update: nil,
delete: nil,
aggregate: nil,
**args
**args.except(:schema)
}
)
end
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
module ForestAdminDatasourceToolkit
class Collection < Components::Contracts::CollectionContract
attr_accessor :segments

attr_reader :actions,
:datasource,
:name,
Expand All@@ -17,10 +15,10 @@ def initialize(datasource, name, native_driver: nil)
fields: {},
countable: false,
searchable: false,
charts: []
charts: [],
segments: {}
}
@actions = {}
@segments = {}
end

def enable_count
Expand Down
13 changes: 8 additions & 5 deletions packages/forest_admin_datasource_toolkit/spec/shared/factory.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,26 +9,29 @@ def datasource_with_collections_build(collections)
datasource
end

def collection_build(args)
def collection_build(args = {})
instance_double(
ForestAdminDatasourceToolkit::Collection,
{
datasource: ForestAdminDatasourceToolkit::Datasource.new,
name: 'collection',
schema: {
charts: [],
fields: {},
countable: false,
searchable: false
},
searchable: false,
segments: {}
}.merge(args[:schema]),
execute: nil,
get_form: nil,
render_chart: nil,
create: nil,
list: nil,
update: nil,
delete: nil,
aggregate: nil
}.merge(args)
aggregate: nil,
**args.except!(:schema)
}
)
end
end