diff --git a/.rubocop.yml b/.rubocop.yml index 5478a02ce..55b0163a6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -242,6 +242,7 @@ Metrics/ClassLength: - 'packages/forest_admin_agent/lib/forest_admin_agent/routes/action/actions.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/routes/resources/related/update_related.rb' - 'packages/forest_admin_agent/lib/forest_admin_agent/utils/schema/frontend_validation_utils.rb' + - 'packages/forest_admin_agent/lib/forest_admin_agent/utils/query_string_parser.rb' - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/utils/query.rb' - 'packages/forest_admin_datasource_active_record/lib/forest_admin_datasource_active_record/collection.rb' - 'packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/collection_customizer.rb' diff --git a/packages/forest_admin_agent/lib/forest_admin_agent/utils/query_string_parser.rb b/packages/forest_admin_agent/lib/forest_admin_agent/utils/query_string_parser.rb index 747f38078..56ca9718a 100644 --- a/packages/forest_admin_agent/lib/forest_admin_agent/utils/query_string_parser.rb +++ b/packages/forest_admin_agent/lib/forest_admin_agent/utils/query_string_parser.rb @@ -69,7 +69,10 @@ def self.parse_projection(collection, args) end end - Projection.new(fields) + projection = Projection.new(fields) + ForestAdminDatasourceToolkit::Validations::ProjectionValidator.validate?(collection, projection) + + projection end def self.parse_projection_with_pks(collection, args) diff --git a/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/computed/utils/flattener.rb b/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/computed/utils/flattener.rb index bd17af2fe..add0941ee 100644 --- a/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/computed/utils/flattener.rb +++ b/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/computed/utils/flattener.rb @@ -31,7 +31,10 @@ def self.with_null_marker(projection) def self.flatten(records, projection) projection.map do |field| - parts = field.split(':') + # because we don't compute computed fields over polymorphic relation (only usage of *), + # we decide to consider the all record as a value instead of a relation + parts = field.split(':').reject { |part| part == '*' } + records.map do |record| value = record @@ -63,7 +66,7 @@ def self.un_flatten(flatten, projection) records[record_index] = {} projection.each_with_index do |path, path_index| - parts = path.split(':').reject { |part| part == MARKER_NAME } + parts = path.split(':').reject { |part| [MARKER_NAME, '*'].include?(part) } value = flatten[path_index][record_index] # Ignore undefined values. diff --git a/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/publication/publication_collection_decorator.rb b/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/publication/publication_collection_decorator.rb index 8685b1417..ea03a8f32 100644 --- a/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/publication/publication_collection_decorator.rb +++ b/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/publication/publication_collection_decorator.rb @@ -70,7 +70,8 @@ def published?(name) ) end - if field.type == 'OneToOne' || field.type == 'OneToMany' + if field.type == 'OneToOne' || field.type == 'OneToMany' || + field.type == 'PolymorphicOneToOne' || field.type == 'PolymorphicOneToMany' return ( datasource.published?(field.foreign_collection) && datasource.get_collection(field.foreign_collection).published?(field.origin_key) && diff --git a/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/rename_field/rename_field_collection_decorator.rb b/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/rename_field/rename_field_collection_decorator.rb index 74e331f94..dbfbe40d0 100644 --- a/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/rename_field/rename_field_collection_decorator.rb +++ b/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/rename_field/rename_field_collection_decorator.rb @@ -52,6 +52,8 @@ def refine_schema(sub_schema) fields = {} schema = sub_schema.dup + # we don't handle schema modification for polymorphic many to one and reverse relations because + # we forbid to rename foreign key and type fields on polymorphic many to one sub_schema[:fields].each do |old_name, old_schema| case old_schema.type when 'ManyToOne' @@ -160,7 +162,11 @@ def path_to_child_collection(path) paths = path.split(':') relation_name = paths[0] relation_schema = schema[:fields][relation_name] - if relation_schema.type != 'PolymorphicManyToOne' + if relation_schema.type == 'PolymorphicManyToOne' + relation_name = to_child_collection[relation_name] + + return "#{relation_name}:#{paths[1]}" + else relation = datasource.get_collection(relation_schema.foreign_collection) child_field = to_child_collection[relation_name] || relation_name @@ -188,7 +194,8 @@ def record_from_child_collection(child_record) field_schema = schema[:fields][field] # Perform the mapping, recurse for relation - if field_schema.type == 'Column' || field_schema.type == 'PolymorphicManyToOne' || value.nil? + if field_schema.type == 'Column' || value.nil? || field_schema.type == 'PolymorphicManyToOne' || + field_schema.type == 'PolymorphicOneToOne' record[field] = value else relation = datasource.get_collection(field_schema.foreign_collection) diff --git a/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/search/search_collection_decorator.rb b/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/search/search_collection_decorator.rb index ca60fe2a3..ed32f323e 100644 --- a/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/search/search_collection_decorator.rb +++ b/packages/forest_admin_datasource_customizer/lib/forest_admin_datasource_customizer/decorators/search/search_collection_decorator.rb @@ -104,7 +104,7 @@ def get_fields(collection, extended) collection.schema[:fields].each do |name, field| fields.push([name, field]) if field.type == 'Column' - if field.type == 'PolymorphicManyToOne' + if field.type == 'PolymorphicManyToOne' && extended ForestAdminAgent::Facades::Container.logger.log( 'Debug', "We're not searching through #{self.name}.#{name} because it's a polymorphic relation. " \ diff --git a/packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/utils/collection.rb b/packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/utils/collection.rb index 1f5ac3869..606350154 100644 --- a/packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/utils/collection.rb +++ b/packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/utils/collection.rb @@ -14,6 +14,8 @@ def self.get_inverse_relation(collection, relation_name) inverse = foreign_collection.schema[:fields].select do |_name, field| if polymorphic_relations.include?(relation_field.type) field.is_a?(PolymorphicManyToOneSchema) && + field.foreign_key_type_field == relation_field.origin_type_field && + field.foreign_key == relation_field.origin_key && field.foreign_collections.include?(collection.name) else field.is_a?(RelationSchema) && diff --git a/packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/validations/field_validator.rb b/packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/validations/field_validator.rb index 6cc120846..7cb4d1e5f 100644 --- a/packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/validations/field_validator.rb +++ b/packages/forest_admin_datasource_toolkit/lib/forest_admin_datasource_toolkit/validations/field_validator.rb @@ -22,19 +22,26 @@ def self.validate(collection, field, values = nil) end end else - prefix = field[0, dot_index] + prefix, suffix = field.split(':') schema = collection.schema[:fields][prefix] raise Exceptions::ValidationError, "Relation not found: '#{collection.name}.#{prefix}'" if schema.nil? - if schema.type != 'ManyToOne' && schema.type != 'OneToOne' + if schema.type == 'PolymorphicManyToOne' && suffix != '*' + raise Exceptions::ValidationError, "Unexpected nested field #{suffix} under generic relation: #{collection.name}.#{prefix}" + end + + if schema.type != 'ManyToOne' && schema.type != 'OneToOne' && schema.type != 'PolymorphicManyToOne' && + schema.type != 'PolymorphicOneToOne' raise Exceptions::ValidationError, - "Unexpected field type: '#{collection.name}.#{prefix}' (found '#{schema.type}' expected 'ManyToOne' or 'OneToOne')" + "Unexpected field type: '#{collection.name}.#{prefix}' (found '#{schema.type}')" end - suffix = field[dot_index + 1, field.length - dot_index - 1] - association = collection.datasource.get_collection(schema.foreign_collection) - validate(association, suffix, values) + if schema.type != 'PolymorphicManyToOne' + suffix = field[dot_index + 1, field.length - dot_index - 1] + association = collection.datasource.get_collection(schema.foreign_collection) + validate(association, suffix, values) + end end end diff --git a/packages/forest_admin_datasource_toolkit/spec/lib/forest_admin_datasource_toolkit/validations/field_validator_spec.rb b/packages/forest_admin_datasource_toolkit/spec/lib/forest_admin_datasource_toolkit/validations/field_validator_spec.rb index 6f2c273ef..e887cc2a3 100644 --- a/packages/forest_admin_datasource_toolkit/spec/lib/forest_admin_datasource_toolkit/validations/field_validator_spec.rb +++ b/packages/forest_admin_datasource_toolkit/spec/lib/forest_admin_datasource_toolkit/validations/field_validator_spec.rb @@ -77,7 +77,7 @@ module Validations described_class.validate(@collection_cars, 'id:address') end.to raise_error(ValidationError, - "🌳🌳🌳 Unexpected field type: 'cars.id' (found 'Column' expected 'ManyToOne' or 'OneToOne')") + "🌳🌳🌳 Unexpected field type: 'cars.id' (found 'Column')") end end