summaryrefslogtreecommitdiff
path: root/lib/api/branches.rb
blob: 13cfba728fa55b157f48c0d23b3ac9e97b87b35c (plain)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
require 'mime/types'

module API
  class Branches < Grape::API
    include PaginationParams

    BRANCH_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(branch: API::NO_SLASH_URL_PART_REGEX)

    before { authorize! :download_code, user_project }

    helpers do
      def find_branch!(branch_name)
        begin
          user_project.repository.find_branch(branch_name) || not_found!('Branch')
        rescue Gitlab::Git::CommandError
          render_api_error!('The branch refname is invalid', 400)
        end
      end

      params :filter_params do
        optional :search, type: String, desc: 'Return list of branches matching the search criteria'
      end
    end

    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
    resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
      desc 'Get a project repository branches' do
        success Entities::Branch
      end
      params do
        use :pagination
        use :filter_params
      end
      get ':id/repository/branches' do
        Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42329')

        repository = user_project.repository

        branches = BranchesFinder.new(repository, declared_params(include_missing: false)).execute

        merged_branch_names = repository.merged_branch_names(branches.map(&:name))

        present(
          paginate(::Kaminari.paginate_array(branches)),
          with: Entities::Branch,
          project: user_project,
          merged_branch_names: merged_branch_names
        )
      end

      resource ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
        desc 'Get a single branch' do
          success Entities::Branch
        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
          branch = find_branch!(params[:branch])

          present branch, with: Entities::Branch, project: user_project
        end
      end

      # Note: This API will be deprecated in favor of the protected branches API.
      # 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`.
      desc 'Protect a single branch' do
        success Entities::Branch
      end
      params do
        requires :branch, type: String, desc: 'The name of the branch'
        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
      put ':id/repository/branches/:branch/protect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
        authorize_admin_project

        branch = find_branch!(params[:branch])

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

        protected_branch_params = {
          name: branch.name,
          developers_can_push: params[:developers_can_push],
          developers_can_merge: params[:developers_can_merge]
        }

        service_args = [user_project, current_user, protected_branch_params]

        protected_branch = if protected_branch
                             ::ProtectedBranches::LegacyApiUpdateService.new(*service_args).execute(protected_branch)
                           else
                             ::ProtectedBranches::LegacyApiCreateService.new(*service_args).execute
                           end

        if protected_branch.valid?
          present branch, with: Entities::Branch, project: user_project
        else
          render_api_error!(protected_branch.errors.full_messages, 422)
        end
      end

      # Note: This API will be deprecated in favor of the protected branches API.
      desc 'Unprotect a single branch' do
        success Entities::Branch
      end
      params do
        requires :branch, type: String, desc: 'The name of the branch'
      end
      put ':id/repository/branches/:branch/unprotect', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
        authorize_admin_project

        branch = find_branch!(params[:branch])
        protected_branch = user_project.protected_branches.find_by(name: branch.name)
        protected_branch&.destroy

        present branch, with: Entities::Branch, project: user_project
      end

      desc 'Create branch' do
        success Entities::Branch
      end
      params do
        requires :branch, type: String, desc: 'The name of the branch'
        requires :ref, type: String, desc: 'Create branch from commit sha or existing branch'
      end
      post ':id/repository/branches' do
        authorize_push_project

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

        if result[:status] == :success
          present result[:branch],
                  with: Entities::Branch,
                  project: user_project
        else
          render_api_error!(result[:message], 400)
        end
      end

      desc 'Delete a branch'
      params do
        requires :branch, type: String, desc: 'The name of the branch'
      end
      delete ':id/repository/branches/:branch', requirements: BRANCH_ENDPOINT_REQUIREMENTS do
        authorize_push_project

        branch = find_branch!(params[:branch])

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

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

          if result[:status] != :success
            render_api_error!(result[:message], result[:return_code])
          end
        end
      end

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

        accepted!
      end
    end
  end
end