branches.rb 6.55 KB
Newer Older
1
2
# frozen_string_literal: true

3
4
5
6
require 'mime/types'

module API
  class Branches < Grape::API
7
8
    include PaginationParams

9
    BRANCH_ENDPOINT_REQUIREMENTS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX)
10

11
12
    before { authorize! :download_code, user_project }

13
    helpers do
14
15
      params :filter_params do
        optional :search, type: String, desc: 'Return list of branches matching the search criteria'
16
        optional :sort, type: String, desc: 'Return list of branches sorted by the given field'
17
      end
18
19
    end

20
21
22
    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
23
    resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
24
      desc 'Get a project repository branches' do
25
        success Entities::Branch
26
      end
27
28
      params do
        use :pagination
29
        use :filter_params
30
      end
31
      get ':id/repository/branches' do
32
33
        Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42329')

34
        repository = user_project.repository
35
36

        branches = BranchesFinder.new(repository, declared_params(include_missing: false)).execute
37
        branches = paginate(::Kaminari.paginate_array(branches))
38
        merged_branch_names = repository.merged_branch_names(branches.map(&:name))
39

40
        present(
41
          branches,
42
          with: Entities::Branch,
43
          current_user: current_user,
44
45
46
          project: user_project,
          merged_branch_names: merged_branch_names
        )
47
48
      end

49
50
      resource ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
        desc 'Get a single branch' do
51
          success Entities::Branch
52
53
54
55
56
57
58
59
        end
        params do
          requires :branch, type: String, desc: 'The name of the branch'
        end
        head do
          user_project.repository.branch_exists?(params[:branch]) ? status(204) : status(404)
        end
        get do
60
          branch = find_branch!(params[:branch])
61

62
          present branch, with: Entities::Branch, current_user: current_user, project: user_project
63
        end
64
65
      end

Eric's avatar
Eric committed
66
      # Note: This API will be deprecated in favor of the protected branches API.
67
68
69
      # Note: The internal data model moved from `developers_can_{merge,push}` to `allowed_to_{merge,push}`
      # in `gitlab-org/gitlab-ce!5081`. The API interface has not been changed (to maintain compatibility),
      # but it works with the changed data model to infer `developers_can_merge` and `developers_can_push`.
70
      desc 'Protect a single branch' do
71
        success Entities::Branch
72
73
      end
      params do
74
        requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
75
76
77
        optional :developers_can_push, type: Boolean, desc: 'Flag if developers can push to that branch'
        optional :developers_can_merge, type: Boolean, desc: 'Flag if developers can merge to that branch'
      end
78
      # rubocop: disable CodeReuse/ActiveRecord
79
      put ':id/repository/branches/:branch/protect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
80
81
        authorize_admin_project

82
        branch = find_branch!(params[:branch])
83
84

        protected_branch = user_project.protected_branches.find_by(name: branch.name)
85

86
        protected_branch_params = {
87
88
89
          name: branch.name,
          developers_can_push: params[:developers_can_push],
          developers_can_merge: params[:developers_can_merge]
90
91
        }

92
        service_args = [user_project, current_user, protected_branch_params]
93

Timothy Andrew's avatar
Timothy Andrew committed
94
        protected_branch = if protected_branch
95
                             ::ProtectedBranches::LegacyApiUpdateService.new(*service_args).execute(protected_branch)
Timothy Andrew's avatar
Timothy Andrew committed
96
                           else
97
                             ::ProtectedBranches::LegacyApiCreateService.new(*service_args).execute
Timothy Andrew's avatar
Timothy Andrew committed
98
                           end
99

Timothy Andrew's avatar
Timothy Andrew committed
100
        if protected_branch.valid?
101
          present branch, with: Entities::Branch, current_user: current_user, project: user_project
102
        else
Timothy Andrew's avatar
Timothy Andrew committed
103
          render_api_error!(protected_branch.errors.full_messages, 422)
104
        end
105
      end
106
      # rubocop: enable CodeReuse/ActiveRecord
107

Eric's avatar
Eric committed
108
      # Note: This API will be deprecated in favor of the protected branches API.
109
      desc 'Unprotect a single branch' do
110
        success Entities::Branch
111
112
      end
      params do
113
        requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
114
      end
115
      # rubocop: disable CodeReuse/ActiveRecord
116
      put ':id/repository/branches/:branch/unprotect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
117
118
        authorize_admin_project

119
        branch = find_branch!(params[:branch])
120
        protected_branch = user_project.protected_branches.find_by(name: branch.name)
Z.J. van de Weg's avatar
Z.J. van de Weg committed
121
        protected_branch&.destroy
122

123
        present branch, with: Entities::Branch, current_user: current_user, project: user_project
124
      end
125
      # rubocop: enable CodeReuse/ActiveRecord
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
126

127
      desc 'Create branch' do
128
        success Entities::Branch
129
130
      end
      params do
131
132
        requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
        requires :ref, type: String, desc: 'Create branch from commit sha or existing branch', allow_blank: false
133
      end
134
      post ':id/repository/branches' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
135
        authorize_push_project
136

137
138
        result = CreateBranchService.new(user_project, current_user)
                 .execute(params[:branch], params[:ref])
139

140
141
        if result[:status] == :success
          present result[:branch],
142
                  with: Entities::Branch,
143
                  current_user: current_user,
144
145
146
147
                  project: user_project
        else
          render_api_error!(result[:message], 400)
        end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
148
      end
149

150
151
      desc 'Delete a branch'
      params do
152
        requires :branch, type: String, desc: 'The name of the branch', allow_blank: false
153
      end
154
      delete ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
155
        authorize_push_project
156

157
        branch = find_branch!(params[:branch])
158
159
160

        commit = user_project.repository.commit(branch.dereferenced_target)

161
        destroy_conditionally!(commit, last_updated: commit.authored_date) do
162
163
          result = DeleteBranchService.new(user_project, current_user)
                    .execute(params[:branch])
164

165
166
167
          if result[:status] != :success
            render_api_error!(result[:message], result[:return_code])
          end
168
        end
169
      end
170

171
      desc 'Delete all merged branches'
172
      delete ':id/repository/merged_branches' do
173
174
        DeleteMergedBranchesService.new(user_project, current_user).async_execute

175
        accepted!
176
      end
177
178
179
    end
  end
end