todos.rb 3.51 KB
Newer Older
1 2
# frozen_string_literal: true

Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
3
module API
Stan Hu's avatar
Stan Hu committed
4
  class Todos < Grape::API::Instance
5 6
    include PaginationParams

Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
7 8
    before { authenticate! }

9
    ISSUABLE_TYPES = {
10 11
      'merge_requests' => ->(iid) { find_merge_request_with_access(iid) },
      'issues' => ->(iid) { find_project_issue(iid) }
Douwe Maan's avatar
Douwe Maan committed
12
    }.freeze
13

Robert Schilling's avatar
Robert Schilling committed
14 15 16
    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
17
    resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
18
      ISSUABLE_TYPES.each do |type, finder|
19
        type_id_str = "#{type.singularize}_iid".to_sym
20

Robert Schilling's avatar
Robert Schilling committed
21 22 23 24
        desc 'Create a todo on an issuable' do
          success Entities::Todo
        end
        params do
25
          requires type_id_str, type: Integer, desc: 'The IID of an issuable'
Robert Schilling's avatar
Robert Schilling committed
26
        end
27 28 29 30 31 32 33 34 35 36 37 38 39
        post ":id/#{type}/:#{type_id_str}/todo" do
          issuable = instance_exec(params[type_id_str], &finder)
          todo = TodoService.new.mark_todo(issuable, current_user).first

          if todo
            present todo, with: Entities::Todo, current_user: current_user
          else
            not_modified!
          end
        end
      end
    end

Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
40 41
    resource :todos do
      helpers do
42
        params :todo_filters do
43
          optional :action, String, values: Todo::ACTION_NAMES.values.map(&:to_s)
44 45 46 47 48 49 50
          optional :author_id, Integer
          optional :state, String, values: Todo.state_machine.states.map(&:name).map(&:to_s)
          optional :type, Array[String], values: TodosFinder.todo_types
          optional :project_id, Integer
          optional :group_id, Integer
        end

51
        def find_todos
52
          TodosFinder.new(current_user, declared_params(include_missing: false)).execute
53 54
        end

55
        def issuable_and_awardable?(type)
56
          obj_type = Object.const_get(type, false)
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

          (obj_type < Issuable) && (obj_type < Awardable)
        rescue NameError
          false
        end

        def batch_load_issuable_metadata(todos, options)
          # This should be paginated and will cause Rails to SELECT for all the Todos
          todos_by_type = todos.group_by(&:target_type)

          todos_by_type.keys.each do |type|
            next unless issuable_and_awardable?(type)

            collection = todos_by_type[type]

            next unless collection

            targets = collection.map(&:target)
Felipe Artur's avatar
Felipe Artur committed
75
            options[type] = { issuable_metadata: Gitlab::IssuableMetadata.new(current_user, targets).data }
76 77
          end
        end
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
78 79
      end

Robert Schilling's avatar
Robert Schilling committed
80 81 82
      desc 'Get a todo list' do
        success Entities::Todo
      end
83
      params do
84
        use :pagination, :todo_filters
85
      end
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
86
      get do
87
        todos = paginate(find_todos.with_entity_associations)
88 89 90 91
        options = { with: Entities::Todo, current_user: current_user }
        batch_load_issuable_metadata(todos, options)

        present todos, options
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
92 93
      end

Robert Schilling's avatar
Robert Schilling committed
94 95 96 97 98 99
      desc 'Mark a todo as done' do
        success Entities::Todo
      end
      params do
        requires :id, type: Integer, desc: 'The ID of the todo being marked as done'
      end
100
      post ':id/mark_as_done' do
101
        todo = current_user.todos.find(params[:id])
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
102

103 104
        TodoService.new.resolve_todo(todo, current_user, resolved_by_action: :api_done)

105
        present todo, with: Entities::Todo, current_user: current_user
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
106 107
      end

Robert Schilling's avatar
Robert Schilling committed
108
      desc 'Mark all todos as done'
109
      post '/mark_as_done' do
110
        todos = find_todos
111 112

        TodoService.new.resolve_todos(todos, current_user, resolved_by_action: :api_all_done)
113 114

        no_content!
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
115 116 117 118
      end
    end
  end
end
119 120

API::Todos.prepend_if_ee('EE::API::Todos')