forest_admin_agent 1.12.5 → 1.12.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/forest_admin_agent/routes/action/actions.rb +2 -2
- data/lib/forest_admin_agent/routes/resources/delete.rb +13 -4
- data/lib/forest_admin_agent/routes/resources/related/associate_related.rb +15 -14
- data/lib/forest_admin_agent/routes/resources/related/count_related.rb +2 -2
- data/lib/forest_admin_agent/routes/resources/related/csv_related.rb +3 -3
- data/lib/forest_admin_agent/routes/resources/related/dissociate_related.rb +19 -11
- data/lib/forest_admin_agent/routes/resources/related/list_related.rb +2 -2
- data/lib/forest_admin_agent/routes/resources/related/update_related.rb +39 -36
- data/lib/forest_admin_agent/routes/resources/show.rb +2 -2
- data/lib/forest_admin_agent/routes/resources/store.rb +2 -2
- data/lib/forest_admin_agent/routes/resources/update.rb +2 -2
- data/lib/forest_admin_agent/routes/resources/update_field.rb +5 -5
- data/lib/forest_admin_agent/utils/condition_tree_parser.rb +41 -7
- data/lib/forest_admin_agent/utils/schema/schema_emitter.rb +1 -1
- data/lib/forest_admin_agent/version.rb +1 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: fd573234b76fd50731dee629e11f0f4aaf920752620bfd8578691f8c33cefc79
         | 
| 4 | 
            +
              data.tar.gz: 4df7f0a817d9ed945e45f9298258d98e7da2ebd2931776c89f0716812490209c
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: f7a71681bef6e70366de3ccc34482f924a9a45199d724705d46ebdb60371be315775196e57084d97e2e41513c20d57a60945cf1d8938eba5cadd856c5de8f3ff
         | 
| 7 | 
            +
              data.tar.gz: c58258f27f3725e02c2d02ced97f322b9289b36ddb82777116d78235ec5a380662491c0f859167da7db3b18461519ef8caa5bc61308038b776bb7b72b830c4d0
         | 
| @@ -159,9 +159,9 @@ module ForestAdminAgent | |
| 159 159 | 
             
                      unless attributes[:parent_association_name].nil?
         | 
| 160 160 | 
             
                        relation = attributes[:parent_association_name]
         | 
| 161 161 | 
             
                        parent = @datasource.get_collection(attributes[:parent_collection_name])
         | 
| 162 | 
            -
                         | 
| 162 | 
            +
                        parent_primary_key_values = Utils::Id.unpack_id(parent, attributes[:parent_collection_id])
         | 
| 163 163 |  | 
| 164 | 
            -
                        filter = FilterFactory.make_foreign_filter(parent,  | 
| 164 | 
            +
                        filter = FilterFactory.make_foreign_filter(parent, parent_primary_key_values, relation, @caller, filter)
         | 
| 165 165 | 
             
                      end
         | 
| 166 166 |  | 
| 167 167 | 
             
                      filter
         | 
| @@ -19,8 +19,8 @@ module ForestAdminAgent | |
| 19 19 | 
             
                    def handle_request(args = {})
         | 
| 20 20 | 
             
                      build(args)
         | 
| 21 21 | 
             
                      @permissions.can?(:delete, @collection)
         | 
| 22 | 
            -
                       | 
| 23 | 
            -
                      delete_records(args, { ids: [ | 
| 22 | 
            +
                      primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
         | 
| 23 | 
            +
                      delete_records(args, { ids: [primary_key_values], are_excluded: false })
         | 
| 24 24 |  | 
| 25 25 | 
             
                      { content: nil, status: 204 }
         | 
| 26 26 | 
             
                    end
         | 
| @@ -41,11 +41,20 @@ module ForestAdminAgent | |
| 41 41 | 
             
                      @collection.schema[:fields].each_value do |field_schema|
         | 
| 42 42 | 
             
                        next unless ['PolymorphicOneToOne', 'PolymorphicOneToMany'].include?(field_schema.type)
         | 
| 43 43 |  | 
| 44 | 
            +
                        origin_values = selection_ids[:ids].map do |pk_hash|
         | 
| 45 | 
            +
                          if pk_hash.is_a?(Hash)
         | 
| 46 | 
            +
                            pk_hash[field_schema.origin_key_target]
         | 
| 47 | 
            +
                          else
         | 
| 48 | 
            +
                            pk_names = ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(@collection)
         | 
| 49 | 
            +
                            index = pk_names.index(field_schema.origin_key_target)
         | 
| 50 | 
            +
                            pk_hash[index] if index
         | 
| 51 | 
            +
                          end
         | 
| 52 | 
            +
                        end
         | 
| 53 | 
            +
             | 
| 44 54 | 
             
                        condition_tree = Nodes::ConditionTreeBranch.new(
         | 
| 45 55 | 
             
                          'And',
         | 
| 46 56 | 
             
                          [
         | 
| 47 | 
            -
                            Nodes::ConditionTreeLeaf.new(field_schema.origin_key, Operators::IN,
         | 
| 48 | 
            -
                                                         selection_ids[:ids].map { |value| value['id'] }),
         | 
| 57 | 
            +
                            Nodes::ConditionTreeLeaf.new(field_schema.origin_key, Operators::IN, origin_values),
         | 
| 49 58 | 
             
                            Nodes::ConditionTreeLeaf.new(field_schema.origin_type_field, Operators::EQUAL,
         | 
| 50 59 | 
             
                                                         @collection.name.gsub('__', '::'))
         | 
| 51 60 | 
             
                          ]
         | 
| @@ -24,17 +24,18 @@ module ForestAdminAgent | |
| 24 24 | 
             
                        build(args)
         | 
| 25 25 | 
             
                        @permissions.can?(:edit, @collection)
         | 
| 26 26 |  | 
| 27 | 
            -
                         | 
| 28 | 
            -
                         | 
| 27 | 
            +
                        parent_primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
         | 
| 28 | 
            +
                        target_primary_key_values = Utils::Id.unpack_id(@child_collection, args[:params]['data'][0]['id'],
         | 
| 29 | 
            +
                                                                        with_key: true)
         | 
| 29 30 | 
             
                        relation = Schema.get_to_many_relation(@collection, args[:params]['relation_name'])
         | 
| 30 31 |  | 
| 31 32 | 
             
                        case relation.type
         | 
| 32 33 | 
             
                        when 'OneToMany'
         | 
| 33 | 
            -
                          associate_one_to_many(relation,  | 
| 34 | 
            +
                          associate_one_to_many(relation, parent_primary_key_values, target_primary_key_values)
         | 
| 34 35 | 
             
                        when 'ManyToMany'
         | 
| 35 | 
            -
                          associate_many_to_many(relation,  | 
| 36 | 
            +
                          associate_many_to_many(relation, parent_primary_key_values, target_primary_key_values)
         | 
| 36 37 | 
             
                        when 'PolymorphicOneToMany'
         | 
| 37 | 
            -
                          associate_polymorphic_one_to_many(relation,  | 
| 38 | 
            +
                          associate_polymorphic_one_to_many(relation, parent_primary_key_values, target_primary_key_values)
         | 
| 38 39 | 
             
                        end
         | 
| 39 40 |  | 
| 40 41 | 
             
                        { content: nil, status: 204 }
         | 
| @@ -42,9 +43,9 @@ module ForestAdminAgent | |
| 42 43 |  | 
| 43 44 | 
             
                      private
         | 
| 44 45 |  | 
| 45 | 
            -
                      def associate_one_to_many(relation,  | 
| 46 | 
            +
                      def associate_one_to_many(relation, parent_primary_key_values, target_primary_key_values)
         | 
| 46 47 | 
             
                        id = Schema.primary_keys(@child_collection)[0]
         | 
| 47 | 
            -
                        value = Collection.get_value(@child_collection, @caller,  | 
| 48 | 
            +
                        value = Collection.get_value(@child_collection, @caller, target_primary_key_values, id)
         | 
| 48 49 | 
             
                        filter = Filter.new(
         | 
| 49 50 | 
             
                          condition_tree: ConditionTree::ConditionTreeFactory.intersect(
         | 
| 50 51 | 
             
                            [
         | 
| @@ -53,14 +54,14 @@ module ForestAdminAgent | |
| 53 54 | 
             
                            ]
         | 
| 54 55 | 
             
                          )
         | 
| 55 56 | 
             
                        )
         | 
| 56 | 
            -
                        value = Collection.get_value(@collection, @caller,  | 
| 57 | 
            +
                        value = Collection.get_value(@collection, @caller, parent_primary_key_values, relation.origin_key_target)
         | 
| 57 58 |  | 
| 58 59 | 
             
                        @child_collection.update(@caller, filter, { relation.origin_key => value })
         | 
| 59 60 | 
             
                      end
         | 
| 60 61 |  | 
| 61 | 
            -
                      def associate_polymorphic_one_to_many(relation,  | 
| 62 | 
            +
                      def associate_polymorphic_one_to_many(relation, parent_primary_key_values, target_primary_key_values)
         | 
| 62 63 | 
             
                        id = Schema.primary_keys(@child_collection)[0]
         | 
| 63 | 
            -
                        value = Collection.get_value(@child_collection, @caller,  | 
| 64 | 
            +
                        value = Collection.get_value(@child_collection, @caller, target_primary_key_values, id)
         | 
| 64 65 | 
             
                        filter = Filter.new(
         | 
| 65 66 | 
             
                          condition_tree: ConditionTree::ConditionTreeFactory.intersect(
         | 
| 66 67 | 
             
                            [
         | 
| @@ -70,7 +71,7 @@ module ForestAdminAgent | |
| 70 71 | 
             
                          )
         | 
| 71 72 | 
             
                        )
         | 
| 72 73 |  | 
| 73 | 
            -
                        value = Collection.get_value(@collection, @caller,  | 
| 74 | 
            +
                        value = Collection.get_value(@collection, @caller, parent_primary_key_values, relation.origin_key_target)
         | 
| 74 75 |  | 
| 75 76 | 
             
                        @child_collection.update(
         | 
| 76 77 | 
             
                          @caller,
         | 
| @@ -79,11 +80,11 @@ module ForestAdminAgent | |
| 79 80 | 
             
                        )
         | 
| 80 81 | 
             
                      end
         | 
| 81 82 |  | 
| 82 | 
            -
                      def associate_many_to_many(relation,  | 
| 83 | 
            +
                      def associate_many_to_many(relation, parent_primary_key_values, target_primary_key_values)
         | 
| 83 84 | 
             
                        id = Schema.primary_keys(@child_collection)[0]
         | 
| 84 | 
            -
                        foreign_value = Collection.get_value(@child_collection, @caller,  | 
| 85 | 
            +
                        foreign_value = Collection.get_value(@child_collection, @caller, target_primary_key_values, id)
         | 
| 85 86 | 
             
                        id = Schema.primary_keys(@collection)[0]
         | 
| 86 | 
            -
                        origin_value = Collection.get_value(@collection, @caller,  | 
| 87 | 
            +
                        origin_value = Collection.get_value(@collection, @caller, parent_primary_key_values, id)
         | 
| 87 88 | 
             
                        record = { relation.origin_key => origin_value, relation.foreign_key => foreign_value }
         | 
| 88 89 |  | 
| 89 90 | 
             
                        through_collection = @datasource.get_collection(relation.through_collection)
         | 
| @@ -25,10 +25,10 @@ module ForestAdminAgent | |
| 25 25 |  | 
| 26 26 | 
             
                        if @child_collection.is_countable?
         | 
| 27 27 | 
             
                          filter = Filter.new(condition_tree: @permissions.get_scope(@collection))
         | 
| 28 | 
            -
                           | 
| 28 | 
            +
                          primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
         | 
| 29 29 | 
             
                          result = Collection.aggregate_relation(
         | 
| 30 30 | 
             
                            @collection,
         | 
| 31 | 
            -
                             | 
| 31 | 
            +
                            primary_key_values,
         | 
| 32 32 | 
             
                            args[:params]['relation_name'],
         | 
| 33 33 | 
             
                            @caller,
         | 
| 34 34 | 
             
                            filter,
         | 
| @@ -34,8 +34,8 @@ module ForestAdminAgent | |
| 34 34 | 
             
                        )
         | 
| 35 35 | 
             
                        projection = ForestAdminAgent::Utils::QueryStringParser.parse_projection_with_pks(@child_collection, args)
         | 
| 36 36 |  | 
| 37 | 
            -
                        # Get the parent record  | 
| 38 | 
            -
                         | 
| 37 | 
            +
                        # Get the parent record primary keys
         | 
| 38 | 
            +
                        primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
         | 
| 39 39 | 
             
                        relation_name = args[:params]['relation_name']
         | 
| 40 40 |  | 
| 41 41 | 
             
                        # Generate timestamp for filename
         | 
| @@ -48,7 +48,7 @@ module ForestAdminAgent | |
| 48 48 | 
             
                        list_records = lambda do |batch_filter|
         | 
| 49 49 | 
             
                          ForestAdminDatasourceToolkit::Utils::Collection.list_relation(
         | 
| 50 50 | 
             
                            @collection,
         | 
| 51 | 
            -
                             | 
| 51 | 
            +
                            primary_key_values,
         | 
| 52 52 | 
             
                            relation_name,
         | 
| 53 53 | 
             
                            @caller,
         | 
| 54 54 | 
             
                            batch_filter,
         | 
| @@ -22,7 +22,7 @@ module ForestAdminAgent | |
| 22 22 | 
             
                      def handle_request(args = {})
         | 
| 23 23 | 
             
                        build(args)
         | 
| 24 24 |  | 
| 25 | 
            -
                         | 
| 25 | 
            +
                        parent_primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
         | 
| 26 26 | 
             
                        is_delete_mode = !args.dig(:params, :delete).nil?
         | 
| 27 27 |  | 
| 28 28 | 
             
                        if is_delete_mode
         | 
| @@ -35,11 +35,13 @@ module ForestAdminAgent | |
| 35 35 | 
             
                        relation = Schema.get_to_many_relation(@collection, args[:params]['relation_name'])
         | 
| 36 36 |  | 
| 37 37 | 
             
                        if ['OneToMany', 'PolymorphicOneToMany'].include?(relation.type)
         | 
| 38 | 
            -
                          dissociate_or_delete_one_to_many( | 
| 39 | 
            -
             | 
| 38 | 
            +
                          dissociate_or_delete_one_to_many(
         | 
| 39 | 
            +
                            relation, args[:params]['relation_name'], parent_primary_key_values, is_delete_mode, filter
         | 
| 40 | 
            +
                          )
         | 
| 40 41 | 
             
                        else
         | 
| 41 | 
            -
                          dissociate_or_delete_many_to_many( | 
| 42 | 
            -
             | 
| 42 | 
            +
                          dissociate_or_delete_many_to_many(
         | 
| 43 | 
            +
                            relation, args[:params]['relation_name'], parent_primary_key_values, is_delete_mode, filter
         | 
| 44 | 
            +
                          )
         | 
| 43 45 | 
             
                        end
         | 
| 44 46 |  | 
| 45 47 | 
             
                        { content: nil, status: 204 }
         | 
| @@ -47,8 +49,10 @@ module ForestAdminAgent | |
| 47 49 |  | 
| 48 50 | 
             
                      private
         | 
| 49 51 |  | 
| 50 | 
            -
                      def dissociate_or_delete_one_to_many(relation, relation_name,  | 
| 51 | 
            -
             | 
| 52 | 
            +
                      def dissociate_or_delete_one_to_many(relation, relation_name, parent_primary_key_values, is_delete_mode,
         | 
| 53 | 
            +
                                                           filter)
         | 
| 54 | 
            +
                        foreign_filter = FilterFactory.make_foreign_filter(@collection, parent_primary_key_values, relation_name,
         | 
| 55 | 
            +
                                                                           @caller, filter)
         | 
| 52 56 |  | 
| 53 57 | 
             
                        if is_delete_mode
         | 
| 54 58 | 
             
                          @child_collection.delete(@caller, foreign_filter)
         | 
| @@ -62,13 +66,16 @@ module ForestAdminAgent | |
| 62 66 | 
             
                        end
         | 
| 63 67 | 
             
                      end
         | 
| 64 68 |  | 
| 65 | 
            -
                      def dissociate_or_delete_many_to_many(relation, relation_name,  | 
| 69 | 
            +
                      def dissociate_or_delete_many_to_many(relation, relation_name, parent_primary_key_values, is_delete_mode,
         | 
| 70 | 
            +
                                                            filter)
         | 
| 66 71 | 
             
                        through_collection = @datasource.get_collection(relation.through_collection)
         | 
| 67 72 |  | 
| 68 73 | 
             
                        if is_delete_mode
         | 
| 69 74 | 
             
                          # Generate filters _BEFORE_ deleting stuff, otherwise things break.
         | 
| 70 | 
            -
                          foreign_filter = FilterFactory.make_foreign_filter(@collection,  | 
| 71 | 
            -
             | 
| 75 | 
            +
                          foreign_filter = FilterFactory.make_foreign_filter(@collection, parent_primary_key_values, relation_name,
         | 
| 76 | 
            +
                                                                             @caller, filter)
         | 
| 77 | 
            +
                          through_filter = FilterFactory.make_through_filter(@collection, parent_primary_key_values, relation_name,
         | 
| 78 | 
            +
                                                                             @caller, filter)
         | 
| 72 79 |  | 
| 73 80 | 
             
                          # Delete records from through collection
         | 
| 74 81 | 
             
                          through_collection.delete(@caller, through_filter)
         | 
| @@ -78,7 +85,8 @@ module ForestAdminAgent | |
| 78 85 | 
             
                          # - the underlying database/api is not cascading deletes
         | 
| 79 86 | 
             
                          @child_collection.delete(@caller, foreign_filter)
         | 
| 80 87 | 
             
                        else
         | 
| 81 | 
            -
                          through_filter = FilterFactory.make_through_filter(@collection,  | 
| 88 | 
            +
                          through_filter = FilterFactory.make_through_filter(@collection, parent_primary_key_values, relation_name,
         | 
| 89 | 
            +
                                                                             @caller, filter)
         | 
| 82 90 | 
             
                          through_collection.delete(@caller, through_filter)
         | 
| 83 91 | 
             
                        end
         | 
| 84 92 | 
             
                      end
         | 
| @@ -35,10 +35,10 @@ module ForestAdminAgent | |
| 35 35 | 
             
                          sort: ForestAdminAgent::Utils::QueryStringParser.parse_sort(@child_collection, args)
         | 
| 36 36 | 
             
                        )
         | 
| 37 37 | 
             
                        projection = ForestAdminAgent::Utils::QueryStringParser.parse_projection_with_pks(@child_collection, args)
         | 
| 38 | 
            -
                         | 
| 38 | 
            +
                        primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
         | 
| 39 39 | 
             
                        records = Collection.list_relation(
         | 
| 40 40 | 
             
                          @collection,
         | 
| 41 | 
            -
                           | 
| 41 | 
            +
                          primary_key_values,
         | 
| 42 42 | 
             
                          args[:params]['relation_name'],
         | 
| 43 43 | 
             
                          @caller,
         | 
| 44 44 | 
             
                          filter,
         | 
| @@ -25,21 +25,21 @@ module ForestAdminAgent | |
| 25 25 | 
             
                        @permissions.can?(:edit, @collection)
         | 
| 26 26 |  | 
| 27 27 | 
             
                        relation = @collection.schema[:fields][args[:params]['relation_name']]
         | 
| 28 | 
            -
                         | 
| 28 | 
            +
                        parent_primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'])
         | 
| 29 29 |  | 
| 30 | 
            -
                         | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 30 | 
            +
                        linked_primary_key_values = if (id = args.dig(:params, 'data', 'id'))
         | 
| 31 | 
            +
                                                      Utils::Id.unpack_id(@child_collection, id)
         | 
| 32 | 
            +
                                                    end
         | 
| 33 33 |  | 
| 34 34 | 
             
                        case relation.type
         | 
| 35 35 | 
             
                        when 'ManyToOne'
         | 
| 36 | 
            -
                          update_many_to_one(relation,  | 
| 36 | 
            +
                          update_many_to_one(relation, parent_primary_key_values, linked_primary_key_values)
         | 
| 37 37 | 
             
                        when 'PolymorphicManyToOne'
         | 
| 38 | 
            -
                          update_polymorphic_many_to_one(relation,  | 
| 38 | 
            +
                          update_polymorphic_many_to_one(relation, parent_primary_key_values, linked_primary_key_values)
         | 
| 39 39 | 
             
                        when 'OneToOne'
         | 
| 40 | 
            -
                          update_one_to_one(relation,  | 
| 40 | 
            +
                          update_one_to_one(relation, parent_primary_key_values, linked_primary_key_values)
         | 
| 41 41 | 
             
                        when 'PolymorphicOneToOne'
         | 
| 42 | 
            -
                          update_polymorphic_one_to_one(relation,  | 
| 42 | 
            +
                          update_polymorphic_one_to_one(relation, parent_primary_key_values, linked_primary_key_values)
         | 
| 43 43 | 
             
                        end
         | 
| 44 44 |  | 
| 45 45 | 
             
                        { content: nil, status: 204 }
         | 
| @@ -47,26 +47,27 @@ module ForestAdminAgent | |
| 47 47 |  | 
| 48 48 | 
             
                      private
         | 
| 49 49 |  | 
| 50 | 
            -
                      def update_many_to_one(relation,  | 
| 51 | 
            -
                        foreign_value = if  | 
| 52 | 
            -
                                          Collection.get_value(@child_collection, @caller,  | 
| 50 | 
            +
                      def update_many_to_one(relation, parent_primary_key_values, linked_primary_key_values)
         | 
| 51 | 
            +
                        foreign_value = if linked_primary_key_values
         | 
| 52 | 
            +
                                          Collection.get_value(@child_collection, @caller, linked_primary_key_values,
         | 
| 53 | 
            +
                                                               relation.foreign_key_target)
         | 
| 53 54 | 
             
                                        end
         | 
| 54 | 
            -
                        fk_owner = ConditionTree::ConditionTreeFactory.match_ids(@collection, [ | 
| 55 | 
            +
                        fk_owner = ConditionTree::ConditionTreeFactory.match_ids(@collection, [parent_primary_key_values])
         | 
| 55 56 | 
             
                        @collection.update(@caller, Filter.new(condition_tree: fk_owner), { relation.foreign_key => foreign_value })
         | 
| 56 57 | 
             
                      end
         | 
| 57 58 |  | 
| 58 | 
            -
                      def update_polymorphic_many_to_one(relation,  | 
| 59 | 
            -
                        foreign_value = if  | 
| 59 | 
            +
                      def update_polymorphic_many_to_one(relation, parent_primary_key_values, linked_primary_key_values)
         | 
| 60 | 
            +
                        foreign_value = if linked_primary_key_values
         | 
| 60 61 | 
             
                                          Collection.get_value(
         | 
| 61 62 | 
             
                                            @child_collection,
         | 
| 62 63 | 
             
                                            @caller,
         | 
| 63 | 
            -
                                             | 
| 64 | 
            +
                                            linked_primary_key_values,
         | 
| 64 65 | 
             
                                            relation.foreign_key_targets[@child_collection.name]
         | 
| 65 66 | 
             
                                          )
         | 
| 66 67 | 
             
                                        end
         | 
| 67 68 |  | 
| 68 69 | 
             
                        polymorphic_type = @child_collection.name.gsub('__', '::')
         | 
| 69 | 
            -
                        fk_owner = ConditionTree::ConditionTreeFactory.match_ids(@collection, [ | 
| 70 | 
            +
                        fk_owner = ConditionTree::ConditionTreeFactory.match_ids(@collection, [parent_primary_key_values])
         | 
| 70 71 | 
             
                        @collection.update(
         | 
| 71 72 | 
             
                          @caller,
         | 
| 72 73 | 
             
                          Filter.new(condition_tree: fk_owner),
         | 
| @@ -77,22 +78,24 @@ module ForestAdminAgent | |
| 77 78 | 
             
                        )
         | 
| 78 79 | 
             
                      end
         | 
| 79 80 |  | 
| 80 | 
            -
                      def update_polymorphic_one_to_one(relation,  | 
| 81 | 
            -
                        origin_value = Collection.get_value(@collection, @caller,  | 
| 81 | 
            +
                      def update_polymorphic_one_to_one(relation, parent_primary_key_values, linked_primary_key_values)
         | 
| 82 | 
            +
                        origin_value = Collection.get_value(@collection, @caller, parent_primary_key_values,
         | 
| 83 | 
            +
                                                            relation.origin_key_target)
         | 
| 82 84 |  | 
| 83 | 
            -
                        break_old_polymorphic_one_to_one_relationship(relation, origin_value,  | 
| 84 | 
            -
                        create_new_polymorphic_one_to_one_relationship(relation, origin_value,  | 
| 85 | 
            +
                        break_old_polymorphic_one_to_one_relationship(relation, origin_value, linked_primary_key_values)
         | 
| 86 | 
            +
                        create_new_polymorphic_one_to_one_relationship(relation, origin_value, linked_primary_key_values)
         | 
| 85 87 | 
             
                      end
         | 
| 86 88 |  | 
| 87 | 
            -
                      def update_one_to_one(relation,  | 
| 88 | 
            -
                        origin_value = Collection.get_value(@collection, @caller,  | 
| 89 | 
            +
                      def update_one_to_one(relation, parent_primary_key_values, linked_primary_key_values)
         | 
| 90 | 
            +
                        origin_value = Collection.get_value(@collection, @caller, parent_primary_key_values,
         | 
| 91 | 
            +
                                                            relation.origin_key_target)
         | 
| 89 92 |  | 
| 90 | 
            -
                        break_old_one_to_one_relationship(relation, origin_value,  | 
| 91 | 
            -
                        create_new_one_to_one_relationship(relation, origin_value,  | 
| 93 | 
            +
                        break_old_one_to_one_relationship(relation, origin_value, linked_primary_key_values)
         | 
| 94 | 
            +
                        create_new_one_to_one_relationship(relation, origin_value, linked_primary_key_values)
         | 
| 92 95 | 
             
                      end
         | 
| 93 96 |  | 
| 94 | 
            -
                      def break_old_polymorphic_one_to_one_relationship(relation, origin_value,  | 
| 95 | 
            -
                         | 
| 97 | 
            +
                      def break_old_polymorphic_one_to_one_relationship(relation, origin_value, linked_primary_key_values)
         | 
| 98 | 
            +
                        linked_primary_key_values ||= []
         | 
| 96 99 |  | 
| 97 100 | 
             
                        old_fk_owner_filter = Filter.new(
         | 
| 98 101 | 
             
                          condition_tree: ConditionTree::ConditionTreeFactory.intersect(
         | 
| @@ -115,7 +118,7 @@ module ForestAdminAgent | |
| 115 118 | 
             
                              ),
         | 
| 116 119 | 
             
                              # Don't set the new record's field to null
         | 
| 117 120 | 
             
                              # if it's already initialized with the right value
         | 
| 118 | 
            -
                              ConditionTree::ConditionTreeFactory.match_ids(@child_collection, [ | 
| 121 | 
            +
                              ConditionTree::ConditionTreeFactory.match_ids(@child_collection, [linked_primary_key_values]).inverse
         | 
| 119 122 | 
             
                            ]
         | 
| 120 123 | 
             
                          )
         | 
| 121 124 | 
             
                        )
         | 
| @@ -133,10 +136,10 @@ module ForestAdminAgent | |
| 133 136 | 
             
                        )
         | 
| 134 137 | 
             
                      end
         | 
| 135 138 |  | 
| 136 | 
            -
                      def create_new_polymorphic_one_to_one_relationship(relation, origin_value,  | 
| 137 | 
            -
                        return unless  | 
| 139 | 
            +
                      def create_new_polymorphic_one_to_one_relationship(relation, origin_value, linked_primary_key_values)
         | 
| 140 | 
            +
                        return unless linked_primary_key_values
         | 
| 138 141 |  | 
| 139 | 
            -
                        new_fk_owner = ConditionTree::ConditionTreeFactory.match_ids(@child_collection, [ | 
| 142 | 
            +
                        new_fk_owner = ConditionTree::ConditionTreeFactory.match_ids(@child_collection, [linked_primary_key_values])
         | 
| 140 143 |  | 
| 141 144 | 
             
                        @child_collection.update(
         | 
| 142 145 | 
             
                          @caller,
         | 
| @@ -151,8 +154,8 @@ module ForestAdminAgent | |
| 151 154 | 
             
                        )
         | 
| 152 155 | 
             
                      end
         | 
| 153 156 |  | 
| 154 | 
            -
                      def break_old_one_to_one_relationship(relation, origin_value,  | 
| 155 | 
            -
                         | 
| 157 | 
            +
                      def break_old_one_to_one_relationship(relation, origin_value, linked_primary_key_values)
         | 
| 158 | 
            +
                        linked_primary_key_values ||= []
         | 
| 156 159 | 
             
                        old_fk_owner_filter = Filter.new(
         | 
| 157 160 | 
             
                          condition_tree: ConditionTree::ConditionTreeFactory.intersect(
         | 
| 158 161 | 
             
                            [
         | 
| @@ -164,7 +167,7 @@ module ForestAdminAgent | |
| 164 167 | 
             
                              ),
         | 
| 165 168 | 
             
                              # Don't set the new record's field to null
         | 
| 166 169 | 
             
                              # if it's already initialized with the right value
         | 
| 167 | 
            -
                              ConditionTree::ConditionTreeFactory.match_ids(@child_collection, [ | 
| 170 | 
            +
                              ConditionTree::ConditionTreeFactory.match_ids(@child_collection, [linked_primary_key_values]).inverse
         | 
| 168 171 | 
             
                            ]
         | 
| 169 172 | 
             
                          )
         | 
| 170 173 | 
             
                        )
         | 
| @@ -178,10 +181,10 @@ module ForestAdminAgent | |
| 178 181 | 
             
                        @child_collection.update(@caller, old_fk_owner_filter, { relation.origin_key => nil })
         | 
| 179 182 | 
             
                      end
         | 
| 180 183 |  | 
| 181 | 
            -
                      def create_new_one_to_one_relationship(relation, origin_value,  | 
| 182 | 
            -
                        return unless  | 
| 184 | 
            +
                      def create_new_one_to_one_relationship(relation, origin_value, linked_primary_key_values)
         | 
| 185 | 
            +
                        return unless linked_primary_key_values
         | 
| 183 186 |  | 
| 184 | 
            -
                        new_fk_owner = ConditionTree::ConditionTreeFactory.match_ids(@child_collection, [ | 
| 187 | 
            +
                        new_fk_owner = ConditionTree::ConditionTreeFactory.match_ids(@child_collection, [linked_primary_key_values])
         | 
| 185 188 |  | 
| 186 189 | 
             
                        @child_collection.update(
         | 
| 187 190 | 
             
                          @caller,
         | 
| @@ -17,8 +17,8 @@ module ForestAdminAgent | |
| 17 17 | 
             
                      build(args)
         | 
| 18 18 | 
             
                      @permissions.can?(:read, @collection)
         | 
| 19 19 | 
             
                      scope = @permissions.get_scope(@collection)
         | 
| 20 | 
            -
                       | 
| 21 | 
            -
                      condition_tree = ConditionTree::ConditionTreeFactory.match_records(@collection, [ | 
| 20 | 
            +
                      primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
         | 
| 21 | 
            +
                      condition_tree = ConditionTree::ConditionTreeFactory.match_records(@collection, [primary_key_values])
         | 
| 22 22 | 
             
                      filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
         | 
| 23 23 | 
             
                        condition_tree: ConditionTree::ConditionTreeFactory.intersect([condition_tree, scope])
         | 
| 24 24 | 
             
                      )
         | 
| @@ -41,7 +41,7 @@ module ForestAdminAgent | |
| 41 41 | 
             
                        schema = @collection.schema[:fields][field]
         | 
| 42 42 | 
             
                        next unless %w[OneToOne PolymorphicOneToOne].include?(schema.type)
         | 
| 43 43 |  | 
| 44 | 
            -
                         | 
| 44 | 
            +
                        primary_key_values = Utils::Id.unpack_id(@collection, value['data']['id'], with_key: true)
         | 
| 45 45 | 
             
                        foreign_collection = @datasource.get_collection(schema.foreign_collection)
         | 
| 46 46 | 
             
                        # Load the value that will be used as origin_key
         | 
| 47 47 | 
             
                        origin_value = record[schema.origin_key_target]
         | 
| @@ -49,7 +49,7 @@ module ForestAdminAgent | |
| 49 49 | 
             
                        # update new relation (may update zero or one records).
         | 
| 50 50 | 
             
                        patch = { schema.origin_key => origin_value }
         | 
| 51 51 | 
             
                        patch[schema.origin_type_field] = @collection.name.gsub('__', '::') if schema.type == 'PolymorphicOneToOne'
         | 
| 52 | 
            -
                        condition_tree = ConditionTree::ConditionTreeFactory.match_records(foreign_collection, [ | 
| 52 | 
            +
                        condition_tree = ConditionTree::ConditionTreeFactory.match_records(foreign_collection, [primary_key_values])
         | 
| 53 53 | 
             
                        filter = Filter.new(condition_tree: condition_tree)
         | 
| 54 54 | 
             
                        foreign_collection.update(@caller, filter, patch)
         | 
| 55 55 | 
             
                      end
         | 
| @@ -18,8 +18,8 @@ module ForestAdminAgent | |
| 18 18 | 
             
                      build(args)
         | 
| 19 19 | 
             
                      @permissions.can?(:edit, @collection)
         | 
| 20 20 | 
             
                      scope = @permissions.get_scope(@collection)
         | 
| 21 | 
            -
                       | 
| 22 | 
            -
                      condition_tree = ConditionTree::ConditionTreeFactory.match_records(@collection, [ | 
| 21 | 
            +
                      primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
         | 
| 22 | 
            +
                      condition_tree = ConditionTree::ConditionTreeFactory.match_records(@collection, [primary_key_values])
         | 
| 23 23 | 
             
                      filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
         | 
| 24 24 | 
             
                        condition_tree: ConditionTree::ConditionTreeFactory.intersect([condition_tree, scope])
         | 
| 25 25 | 
             
                      )
         | 
| @@ -25,7 +25,7 @@ module ForestAdminAgent | |
| 25 25 | 
             
                    def handle_request(args = {})
         | 
| 26 26 | 
             
                      build(args)
         | 
| 27 27 |  | 
| 28 | 
            -
                       | 
| 28 | 
            +
                      primary_key_values = Utils::Id.unpack_id(@collection, args[:params]['id'], with_key: true)
         | 
| 29 29 | 
             
                      field_name = args[:params]['field_name']
         | 
| 30 30 | 
             
                      array_index = parse_index(args[:params]['index'])
         | 
| 31 31 |  | 
| @@ -34,7 +34,7 @@ module ForestAdminAgent | |
| 34 34 | 
             
                      field_schema = @collection.schema[:fields][field_name]
         | 
| 35 35 | 
             
                      validate_array_field!(field_schema, field_name)
         | 
| 36 36 |  | 
| 37 | 
            -
                      record = fetch_record( | 
| 37 | 
            +
                      record = fetch_record(primary_key_values)
         | 
| 38 38 |  | 
| 39 39 | 
             
                      array = record[field_name]
         | 
| 40 40 | 
             
                      validate_array_value!(array, field_name, array_index)
         | 
| @@ -45,7 +45,7 @@ module ForestAdminAgent | |
| 45 45 | 
             
                      updated_array[array_index] = new_value
         | 
| 46 46 |  | 
| 47 47 | 
             
                      scope = @permissions.get_scope(@collection)
         | 
| 48 | 
            -
                      condition_tree = ConditionTree::ConditionTreeFactory.match_records(@collection, [ | 
| 48 | 
            +
                      condition_tree = ConditionTree::ConditionTreeFactory.match_records(@collection, [primary_key_values])
         | 
| 49 49 | 
             
                      filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
         | 
| 50 50 | 
             
                        condition_tree: ConditionTree::ConditionTreeFactory.intersect([condition_tree, scope])
         | 
| 51 51 | 
             
                      )
         | 
| @@ -87,9 +87,9 @@ module ForestAdminAgent | |
| 87 87 | 
             
                      raise Http::Exceptions::ValidationError, e.message
         | 
| 88 88 | 
             
                    end
         | 
| 89 89 |  | 
| 90 | 
            -
                    def fetch_record( | 
| 90 | 
            +
                    def fetch_record(primary_key_values)
         | 
| 91 91 | 
             
                      scope = @permissions.get_scope(@collection)
         | 
| 92 | 
            -
                      condition_tree = ConditionTree::ConditionTreeFactory.match_records(@collection, [ | 
| 92 | 
            +
                      condition_tree = ConditionTree::ConditionTreeFactory.match_records(@collection, [primary_key_values])
         | 
| 93 93 | 
             
                      filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
         | 
| 94 94 | 
             
                        condition_tree: ConditionTree::ConditionTreeFactory.intersect([condition_tree, scope])
         | 
| 95 95 | 
             
                      )
         | 
| @@ -12,7 +12,7 @@ module ForestAdminAgent | |
| 12 12 | 
             
                  def self.from_plain_object(collection, filters)
         | 
| 13 13 | 
             
                    if leaf?(filters)
         | 
| 14 14 | 
             
                      operator = filters[:operator].titleize.tr(' ', '_').downcase
         | 
| 15 | 
            -
                      value = parse_value(collection, filters)
         | 
| 15 | 
            +
                      value = parse_value(collection, filters.merge(operator: operator))
         | 
| 16 16 |  | 
| 17 17 | 
             
                      return ConditionTreeLeaf.new(filters[:field], operator, value)
         | 
| 18 18 | 
             
                    end
         | 
| @@ -31,18 +31,52 @@ module ForestAdminAgent | |
| 31 31 |  | 
| 32 32 | 
             
                  def self.parse_value(collection, leaf)
         | 
| 33 33 | 
             
                    schema = Collection.get_field_schema(collection, leaf[:field])
         | 
| 34 | 
            +
                    expected_type = get_expected_type_for_condition(leaf, schema)
         | 
| 34 35 |  | 
| 35 | 
            -
                     | 
| 36 | 
            -
             | 
| 36 | 
            +
                    cast_to_type(leaf[:value], expected_type)
         | 
| 37 | 
            +
                  end
         | 
| 37 38 |  | 
| 38 | 
            -
             | 
| 39 | 
            +
                  def self.get_expected_type_for_condition(leaf, schema)
         | 
| 40 | 
            +
                    operators_expecting_number = [
         | 
| 41 | 
            +
                      Operators::SHORTER_THAN,
         | 
| 42 | 
            +
                      Operators::LONGER_THAN,
         | 
| 43 | 
            +
                      Operators::AFTER_X_HOURS_AGO,
         | 
| 44 | 
            +
                      Operators::BEFORE_X_HOURS_AGO,
         | 
| 45 | 
            +
                      Operators::PREVIOUS_X_DAYS,
         | 
| 46 | 
            +
                      Operators::PREVIOUS_X_DAYS_TO_DATE
         | 
| 47 | 
            +
                    ]
         | 
| 39 48 |  | 
| 40 | 
            -
             | 
| 49 | 
            +
                    return 'Number' if operators_expecting_number.include?(leaf[:operator])
         | 
| 41 50 |  | 
| 42 | 
            -
             | 
| 51 | 
            +
                    if [Operators::IN, Operators::NOT_IN, Operators::INCLUDES_ALL].include?(leaf[:operator])
         | 
| 52 | 
            +
                      return [schema.column_type]
         | 
| 43 53 | 
             
                    end
         | 
| 44 54 |  | 
| 45 | 
            -
                     | 
| 55 | 
            +
                    schema.column_type
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def self.cast_to_type(value, expected_type)
         | 
| 59 | 
            +
                    return value if value.nil?
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    if expected_type.is_a?(Array)
         | 
| 62 | 
            +
                      items = value.is_a?(String) ? value.split(',').map(&:strip) : value
         | 
| 63 | 
            +
                      filter_fn = expected_type[0] == 'Number' ? ->(item) { item.is_a?(Numeric) } : ->(_) { true }
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                      return value unless items.is_a?(Array)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      return items.map { |item| cast_to_type(item, expected_type[0]) }.select(&filter_fn)
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    case expected_type
         | 
| 71 | 
            +
                    when 'String', 'Dateonly', 'Date'
         | 
| 72 | 
            +
                      value.to_s
         | 
| 73 | 
            +
                    when 'Number'
         | 
| 74 | 
            +
                      value.to_f
         | 
| 75 | 
            +
                    when 'Boolean'
         | 
| 76 | 
            +
                      !%w[false 0 no].include?(value.to_s)
         | 
| 77 | 
            +
                    else
         | 
| 78 | 
            +
                      value
         | 
| 79 | 
            +
                    end
         | 
| 46 80 | 
             
                  end
         | 
| 47 81 |  | 
| 48 82 | 
             
                  def self.leaf?(filters)
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: forest_admin_agent
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1.12. | 
| 4 | 
            +
              version: 1.12.7
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Matthieu
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire:
         | 
| 10 10 | 
             
            bindir: exe
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2025-10- | 
| 12 | 
            +
            date: 2025-10-27 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: activesupport
         |