File: hierarchy.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (134 lines) | stat: -rw-r--r-- 4,487 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# frozen_string_literal: true

module WorkItems
  module Callbacks
    class Hierarchy < Base
      INVALID_RELATIVE_POSITION_ERROR = 'Relative position is not valid.'
      CHILDREN_REORDERING_ERROR = 'Relative position cannot be combined with childrenIds.'
      UNRELATED_ADJACENT_HIERARCHY_ERROR = 'The adjacent work item\'s parent must match the new parent work item.'
      INVALID_ADJACENT_PARENT_ERROR = 'The adjacent work item\'s parent must match the current parent work item.'

      def after_create
        handle_service_response!(handle_hierarchy_changes)
      end

      def after_update
        if positioning?
          handle_service_response!(handle_positioning)
        else
          handle_service_response!(handle_hierarchy_changes)
        end
      end

      private

      def handle_service_response!(response)
        work_item.reload_work_item_parent
        work_item.work_item_children.reset

        raise_error response[:message] if response&.fetch(:status) == :error
      end

      def positioning?
        params[:relative_position].present? || params[:adjacent_work_item].present?
      end

      def handle_positioning
        validate_positioning_params!

        arguments = {
          target_issuable: work_item,
          adjacent_work_item: params[:adjacent_work_item],
          relative_position: params[:relative_position]
        }
        work_item_parent = params[:parent] || work_item.work_item_parent
        ::WorkItems::ParentLinks::ReorderService.new(work_item_parent, current_user, arguments).execute
      end

      def validate_positioning_params!
        raise_error(INVALID_RELATIVE_POSITION_ERROR) if incomplete_relative_position?
        raise_error(CHILDREN_REORDERING_ERROR) if missing_positioning_children?
        raise_error(UNRELATED_ADJACENT_HIERARCHY_ERROR) if unrelated_adjacent_hierarchy?
        raise_error(INVALID_ADJACENT_PARENT_ERROR) if invalid_adjacent_parent?
      end

      def handle_hierarchy_changes
        validate_hierarchy_change_params!

        if params.key?(:parent)
          update_work_item_parent(params[:parent])
        elsif params.key?(:children)
          update_work_item_children(params[:children])
        elsif params.key?(:remove_child)
          remove_parent_link(params[:remove_child])
        end
      end

      def validate_hierarchy_change_params!
        hierarchy_change_params = [:children, :parent, :remove_child]

        param_count = params.slice(*hierarchy_change_params).size

        if param_count > 1
          raise_error(format(
            _("One and only one of %{params} is required"),
            params: hierarchy_change_params.to_sentence(last_word_connector: ' or ')
          ))
        elsif param_count == 0
          raise_error(format(
            _("One or more arguments are invalid: %{args}."),
            args: params.keys.to_sentence
          ))
        end
      end

      def update_work_item_parent(parent)
        return remove_parent_link(work_item) if parent.nil?

        service_response = ::WorkItems::ParentLinks::CreateService
          .new(parent, current_user, { target_issuable: work_item })
          .execute

        # Reference the parent instead because the error is returned in the child context
        if service_response[:status] == :error
          service_response[:message].sub!(/#.* cannot be added/, "#{parent.to_reference} cannot be added")
        end

        service_response
      end

      def update_work_item_children(children)
        ::WorkItems::ParentLinks::CreateService
          .new(work_item, current_user, { issuable_references: children })
          .execute
      end

      def remove_parent_link(child)
        link = ::WorkItems::ParentLink.find_by_work_item_id(child)
        return unless link.present?

        ::WorkItems::ParentLinks::DestroyService.new(link, current_user).execute
      end

      def incomplete_relative_position?
        params[:adjacent_work_item].blank? || params[:relative_position].blank?
      end

      def missing_positioning_children?
        params.key?(:children)
      end

      def unrelated_adjacent_hierarchy?
        return false if params[:parent].blank?

        params[:parent] != params[:adjacent_work_item].work_item_parent
      end

      def invalid_adjacent_parent?
        return false if params[:parent].present?

        work_item.work_item_parent != params[:adjacent_work_item].work_item_parent
      end
    end
  end
end