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@@ -205,6 +205,7 @@ Metrics/MethodLength:
- 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/parser/validation.rb'
- 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb'
- 'packages/forest_admin_datasource_active_record/spec/dummy/**/*'
- 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/search/search_collection_decorator.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/collection.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/condition_tree/condition_tree_factory.rb'
- 'packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/components/query/filter_factory.rb'
Expand DownExpand Up@@ -261,3 +262,4 @@ Layout/LineLength:

RSpec/MultipleMemoizedHelpers:
Max: 10

Original file line numberDiff line numberDiff line change
Expand Up@@ -15,16 +15,18 @@ def setup_routes
def handle_request(args = {})
build(args)
@permissions.can?(:browse, @collection)

filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
condition_tree: ConditionTreeFactory.intersect([
@permissions.get_scope(@collection),
ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(
@collection, args
)
]),
page: ForestAdminAgent::Utils::QueryStringParser.parse_pagination(args)
page: ForestAdminAgent::Utils::QueryStringParser.parse_pagination(args),
search: ForestAdminAgent::Utils::QueryStringParser.parse_search(@collection, args),
search_extended: ForestAdminAgent::Utils::QueryStringParser.parse_search_extended(args)
)

projection = ForestAdminAgent::Utils::QueryStringParser.parse_projection_with_pks(@collection, args)
records = @collection.list(@caller, filter, projection)

Expand All@@ -34,10 +36,30 @@ def handle_request(args = {})
records,
is_collection: true,
serializer: Serializer::ForestSerializer,
include: projection.relations.keys
include: projection.relations.keys,
meta: handle_search_decorator(args[:params]['search'], records)
)
}
end

def handle_search_decorator(search_value, records)
decorator = { decorators: [] }
unless search_value.nil?
records.each_with_index do |entry, index|
decorator[:decorators][index] = { id: Utils::Id.pack_id(@collection, entry), search: [] }
# attributes method is defined on ActiveRecord::Base model
attributes = entry.respond_to?(:attributes) ? entry.attributes : entry

attributes.each do |field_key, field_value|
if !field_value.is_a?(Array) && field_value.to_s.downcase.include?(search_value.downcase)
decorator[:decorators][index][:search] << field_key
end
end
end
end

decorator
end
end
end
end
Expand Down
13 changes: 13 additions & 0 deletions packages/forest_admin_agent/lib/forest_admin_agent/utils/id.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,19 @@ module Utils
class Id
include ForestAdminDatasourceToolkit::Utils
include ForestAdminDatasourceToolkit

def self.pack_ids(schema, records)
records.map { |packed_id| pack_id(schema, packed_id) }
end

def self.pack_id(schema, record)
pk_names = ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(schema)

raise Exceptions::ForestException, 'This collection has no primary key' if pk_names.empty?

pk_names.map { |pk| record[pk].to_s }.join('|')
end

def self.unpack_id(collection, packed_id, with_key: false)
primary_keys = ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(collection)
primary_key_values = packed_id.to_s.split('|')
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -85,6 +85,23 @@ def self.parse_pagination(args)

Page.new(offset: offset, limit: items_per_pages.to_i)
end

def self.parse_search(collection, args)
search = args.dig(:params, :data, :attributes, :all_records_subset_query, :search) || args.dig(:params, :search)

raise ForestException, 'Collection is not searchable' if search && !collection.is_searchable?

search
end

def self.parse_search_extended(args)
extended = args.dig(:params, :data, :attributes, :all_records_subset_query,
:searchExtended) || args.dig(:params, :searchExtended)

return false if extended.nil?

extended != '0'
end
end
end
end
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,6 +41,7 @@ module Resources
},
list: [User.new(1, 'foo', 'foo')]
)

allow(ForestAdminAgent::Builder::AgentFactory.instance).to receive(:send_schema).and_return(nil)
datasource.add_collection(collection)
ForestAdminAgent::Builder::AgentFactory.instance.add_datasource(datasource)
Expand DownExpand Up@@ -75,7 +76,8 @@ module Resources
'links' => { 'self' => 'forest/user/1' }
}
],
'included' => []
'included' => [],
'meta' => { decorators: [] }
)
end

Expand Down
Comment thread
nicolasalexandre9 marked this conversation as resolved.
Original file line numberDiff line numberDiff line change
Expand Up@@ -141,6 +141,38 @@ module Utils
)
end
end

describe 'when pack_id is called' do
it 'return the id value' do
collection = datasource.get_collection('person')
expect(described_class.pack_id(collection, { 'id' => 1, 'foo' => 'bar' })).to eq('1')
end

it 'return the ids value' do
collection = datasource.get_collection('pks')
expect(described_class.pack_id(collection, { 'key1' => 1, 'key2' => 2 })).to eq('1|2')
end

it 'throws when collection does not have any primary keys' do
collection_foo = datasource.get_collection('foo')
expect do
described_class.pack_id(collection_foo, { 'id' => 1, 'foo' => 'bar' })
end.to raise_error(
ForestAdminDatasourceToolkit::Exceptions::ForestException,
'🌳🌳🌳 This collection has no primary key'
)
end
end

describe 'when pack_ids is called' do
it 'return an array of ids' do
collection = datasource.get_collection('person')
expect(described_class.pack_ids(collection, [
{ 'id' => 1, 'foo' => 'bar' },
{ 'id' => 2, 'foo' => 'foo' }
])).to eq(['1', '2'])
end
end
end
end
end
Original file line numberDiff line numberDiff line change
Expand Up@@ -358,6 +358,100 @@ module Utils
)
end
end

describe 'when parse_search' do
let(:collection_category) do
datasource = Datasource.new
collection_category = Collection.new(datasource, 'Category')
collection_category.add_fields(
{
'id' => ColumnSchema.new(column_type: 'Number', is_primary_key: true,
filter_operators: [Operators::EQUAL]),
'label' => ColumnSchema.new(column_type: 'String')
}
)

datasource.add_collection(collection_category)

return collection_category
end

let(:collection_user) do
datasource = Datasource.new
collection_user = Collection.new(datasource, 'User')
collection_user.add_fields(
{
'id' => ColumnSchema.new(column_type: 'Number', is_primary_key: true,
filter_operators: [Operators::EQUAL]),
'name' => ColumnSchema.new(column_type: 'String')
}
)
collection_user.schema[:searchable] = true

datasource.add_collection(collection_user)

return collection_user
end

it 'returns null when not provided' do
args = { params: {} }

expect(described_class.parse_search(collection_category, args)).to be_nil
end

it 'hrows an error when the collection is not searchable' do
args = { params: { search: 'searched argument' } }

expect do
described_class.parse_search(collection_category, args)
end.to raise_error(
ForestAdminDatasourceToolkit::Exceptions::ForestException,
'🌳🌳🌳 Collection is not searchable'
)
end

it 'returns the query search parameter' do
args = { params: { search: 'searched argument' } }

expect(described_class.parse_search(collection_user, args)).to eq('searched argument')
end

it 'converts the query search parameter as string' do
args = { params: { search: 1234 } }

expect(described_class.parse_search(collection_user, args)).to eq(1234)
end

it 'works when passed in the body (actions)' do
args = {
params: {
data: {
attributes: {
all_records_subset_query: {
search: 'searched argument'
}
}
}
}
}

expect(described_class.parse_search(collection_user, args)).to eq('searched argument')
end
end

describe 'when parse_search_extended' do
it 'returns the query searchExtended parameter' do
args = { params: { searchExtended: true } }

expect(described_class.parse_search_extended(args)).to be(true)
end

it 'returns false for falsy "0" string' do
args = { params: { searchExtended: '0' } }

expect(described_class.parse_search_extended(args)).to be(false)
end
end
end
end
end
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,5 +27,11 @@ def disable_count
-> { @stack.schema.get_collection(@name).override_schema(countable: false) }
)
end

def replace_search(definition)
push_customization(
-> { @stack.search.get_collection(@name).replace_search(definition) }
)
end
end
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
module ForestAdminDatasourceCustomizer
module Context
class AgentCustomizationContext
attr_reader :caller

def initialize(datasource, caller)
@real_datasource = datasource
@caller = caller
end

def datasource
RelaxedDataSource.new(@real_datasource, @caller)
end
end
end
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
module ForestAdminDatasourceCustomizer
module Context
class CollectionCustomizationContext < AgentCustomizationContext
def initialize(collection, caller)
super(collection.datasource, caller)
@real_collection = collection
end

def collection
RelaxedCollection.new(@real_collection, @caller)
end
end
end
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
module ForestAdminDatasourceCustomizer
module Context
module RelaxedWrappers
class RelaxedCollection
def initialize(collection, caller)
@collection = collection
@caller = caller
end
end
end
end
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
module ForestAdminDatasourceCustomizer
module Context
module RelaxedWrappers
class RelaxedDataSource
def initialize(datasource, caller)
@real_datasource = datasource
@caller = caller
end

# Get a collection from a datasource
# @param name the name of the collection
def get_collection(name)
RelaxedCollection.new(@real_datasource.get_collection(name), @caller)
end
end
end
end
end
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@ def initialize(datasource)
last = datasource
last = DatasourceDecorator.new(last, Empty::EmptyCollectionDecorator)
last = DatasourceDecorator.new(last, OperatorsEquivalence::OperatorsEquivalenceCollectionDecorator)
last = DatasourceDecorator.new(last, Search::SearchCollectionDecorator)
last = @schema = DatasourceDecorator.new(last, Schema::SchemaCollectionDecorator)
@datasource = last
end
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
module ForestAdminDatasourceToolkit
module ForestAdminDatasourceCustomizer
module Decorators
module Schema
class SchemaCollectionDecorator < CollectionDecorator
class SchemaCollectionDecorator < ForestAdminDatasourceToolkit::Decorators::CollectionDecorator
def initialize(child_collection, datasource)
super
@schema_override = {}
Expand Down
Loading