projects.rb 15.5 KB
Newer Older
1
module API
N
Nihad Abbasov 已提交
2 3
  # Projects API
  class Projects < Grape::API
R
Robert Schilling 已提交
4 5
    include PaginationParams

6
    before { authenticate_non_get! }
N
Nihad Abbasov 已提交
7

R
Robert Schilling 已提交
8
    helpers do
9
      params :optional_params_ce do
R
Robert Schilling 已提交
10 11 12 13
        optional :description, type: String, desc: 'The description of the project'
        optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled'
        optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled'
        optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled'
14
        optional :jobs_enabled, type: Boolean, desc: 'Flag indication if jobs are enabled'
R
Robert Schilling 已提交
15 16 17 18
        optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled'
        optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project'
        optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project'
        optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project'
19
        optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the project.'
R
Robert Schilling 已提交
20 21
        optional :public_builds, type: Boolean, desc: 'Perform public builds'
        optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
J
James Lopez 已提交
22
        optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
R
Robert Schilling 已提交
23 24
        optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
      end
25 26 27 28

      params :optional_params do
        use :optional_params_ce
      end
29 30 31 32

      params :statistics_params do
        optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
      end
R
Robert Schilling 已提交
33 34 35
    end

    resource :projects do
36
      helpers do
M
Markus Koller 已提交
37 38 39 40 41 42 43 44 45
        params :collection_params do
          use :sort_params
          use :filter_params
          use :pagination

          optional :simple, type: Boolean, default: false,
                            desc: 'Return only the ID, URL, name, and path of each project'
        end

R
Robert Schilling 已提交
46 47 48 49 50 51 52 53 54
        params :sort_params do
          optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
                              default: 'created_at', desc: 'Return projects ordered by field'
          optional :sort, type: String, values: %w[asc desc], default: 'desc',
                          desc: 'Return projects sorted in ascending and descending order'
        end

        params :filter_params do
          optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
55
          optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values,
R
Robert Schilling 已提交
56
                                desc: 'Limit by visibility'
57
          optional :search, type: String, desc: 'Return list of projects matching the search criteria'
T
Toon Claes 已提交
58 59
          optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user'
          optional :starred, type: Boolean, default: false, desc: 'Limit by starred status'
60
          optional :membership, type: Boolean, default: false, desc: 'Limit by projects that the current user is a member of'
61 62
          optional :with_issues_enabled, type: Boolean, default: false, desc: 'Limit by enabled issues feature'
          optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
M
Markus Koller 已提交
63 64
        end

R
Robert Schilling 已提交
65 66 67
        params :create_params do
          optional :namespace_id, type: Integer, desc: 'Namespace ID for the new project. Default to the user namespace.'
          optional :import_url, type: String, desc: 'URL from which the project is imported'
68
        end
M
Markus Koller 已提交
69

70
        def present_projects(options = {})
71 72 73 74
          finder_params = {}
          finder_params[:non_public] = true if params[:membership].present?

          projects = ProjectsFinder.new(current_user: current_user, params: finder_params).execute
M
Markus Koller 已提交
75
          projects = filter_projects(projects)
76 77 78
          projects = projects.with_statistics if params[:statistics]
          projects = projects.with_issues_enabled if params[:with_issues_enabled]
          projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
79 80 81 82 83 84

          options = options.reverse_merge(
            with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails,
            statistics: params[:statistics],
            current_user: current_user
          )
85
          options[:with] = Entities::BasicProjectDetails if params[:simple]
M
Markus Koller 已提交
86 87 88

          present paginate(projects), options
        end
89 90
      end

91
      desc 'Get a list of visible projects for authenticated user' do
R
Robert Schilling 已提交
92 93 94
        success Entities::BasicProjectDetails
      end
      params do
M
Markus Koller 已提交
95
        use :collection_params
96
        use :statistics_params
R
Robert Schilling 已提交
97
      end
98
      get do
99
        present_projects
100 101
      end

R
Robert Schilling 已提交
102 103 104 105
      desc 'Create new project' do
        success Entities::Project
      end
      params do
106
        optional :name, type: String, desc: 'The name of the project'
R
Robert Schilling 已提交
107
        optional :path, type: String, desc: 'The path of the repository'
108
        at_least_one_of :name, :path
R
Robert Schilling 已提交
109 110 111
        use :optional_params
        use :create_params
      end
112
      post do
113
        attrs = declared_params(include_missing: false)
114
        attrs[:builds_enabled] = attrs.delete(:jobs_enabled) if attrs.has_key?(:jobs_enabled)
R
Robert Schilling 已提交
115 116 117 118 119
        project = ::Projects::CreateService.new(current_user, attrs).execute

        if project.saved?
          present project, with: Entities::Project,
                           user_can_admin_project: can?(current_user, :admin_project, project)
120
        else
R
Robert Schilling 已提交
121 122
          if project.errors[:limit_reached].present?
            error!(project.errors[:limit_reached], 403)
123
          end
R
Robert Schilling 已提交
124
          render_validation_error!(project)
125 126 127
        end
      end

R
Robert Schilling 已提交
128 129 130 131 132 133 134 135 136 137
      desc 'Create new project for a specified user. Only available to admin users.' do
        success Entities::Project
      end
      params do
        requires :name, type: String, desc: 'The name of the project'
        requires :user_id, type: Integer, desc: 'The ID of a user'
        optional :default_branch, type: String, desc: 'The default branch of the project'
        use :optional_params
        use :create_params
      end
A
Angus MacArthur 已提交
138 139
      post "user/:user_id" do
        authenticated_as_admin!
R
Robert Schilling 已提交
140 141 142
        user = User.find_by(id: params.delete(:user_id))
        not_found!('User') unless user

143
        attrs = declared_params(include_missing: false)
R
Robert Schilling 已提交
144 145 146 147 148
        project = ::Projects::CreateService.new(user, attrs).execute

        if project.saved?
          present project, with: Entities::Project,
                           user_can_admin_project: can?(current_user, :admin_project, project)
A
Angus MacArthur 已提交
149
        else
R
Robert Schilling 已提交
150
          render_validation_error!(project)
A
Angus MacArthur 已提交
151 152
        end
      end
R
Robert Schilling 已提交
153 154 155 156 157
    end

    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
158
    resource :projects, requirements: { id: %r{[^/]+} } do
R
Robert Schilling 已提交
159 160 161
      desc 'Get a single project' do
        success Entities::ProjectWithAccess
      end
162 163 164
      params do
        use :statistics_params
      end
R
Robert Schilling 已提交
165
      get ":id" do
166
        entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails
167
        present user_project, with: entity, current_user: current_user,
168
                              user_can_admin_project: can?(current_user, :admin_project, user_project), statistics: params[:statistics]
R
Robert Schilling 已提交
169 170 171 172 173 174 175 176 177 178 179
      end

      desc 'Get events for a single project' do
        success Entities::Event
      end
      params do
        use :pagination
      end
      get ":id/events" do
        present paginate(user_project.events.recent), with: Entities::Event
      end
A
Angus MacArthur 已提交
180

R
Robert Schilling 已提交
181 182 183 184 185 186
      desc 'Fork new project for the current user or provided namespace.' do
        success Entities::Project
      end
      params do
        optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
      end
187
      post ':id/fork' do
R
Robert Schilling 已提交
188 189
        fork_params = declared_params(include_missing: false)
        namespace_id = fork_params[:namespace]
190

191
        if namespace_id.present?
R
Robert Schilling 已提交
192 193 194 195 196
          fork_params[:namespace] = if namespace_id =~ /^\d+$/
                                      Namespace.find_by(id: namespace_id)
                                    else
                                      Namespace.find_by_path_or_name(namespace_id)
                                    end
197

R
Robert Schilling 已提交
198
          unless fork_params[:namespace] && can?(current_user, :create_projects, fork_params[:namespace])
199 200
            not_found!('Target Namespace')
          end
201
        end
202

R
Robert Schilling 已提交
203
        forked_project = ::Projects::ForkService.new(user_project, current_user, fork_params).execute
204

R
Robert Schilling 已提交
205 206
        if forked_project.errors.any?
          conflict!(forked_project.errors.messages)
207
        else
R
Robert Schilling 已提交
208 209
          present forked_project, with: Entities::Project,
                                  user_can_admin_project: can?(current_user, :admin_project, forked_project)
210
        end
211 212
      end

R
Robert Schilling 已提交
213 214 215 216
      desc 'Update an existing project' do
        success Entities::Project
      end
      params do
217 218 219
        # CE
        at_least_one_of_ce =
          [
220
            :jobs_enabled,
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
            :container_registry_enabled,
            :default_branch,
            :description,
            :issues_enabled,
            :lfs_enabled,
            :merge_requests_enabled,
            :name,
            :only_allow_merge_if_all_discussions_are_resolved,
            :only_allow_merge_if_pipeline_succeeds,
            :path,
            :public_builds,
            :request_access_enabled,
            :shared_runners_enabled,
            :snippets_enabled,
            :visibility,
236
            :wiki_enabled
237
          ]
R
Robert Schilling 已提交
238 239 240
        optional :name, type: String, desc: 'The name of the project'
        optional :default_branch, type: String, desc: 'The default branch of the project'
        optional :path, type: String, desc: 'The path of the repository'
241

R
Robert Schilling 已提交
242
        use :optional_params
243
        at_least_one_of(*at_least_one_of_ce)
R
Robert Schilling 已提交
244
      end
245 246
      put ':id' do
        authorize_admin_project
247
        attrs = declared_params(include_missing: false)
248
        authorize! :rename_project, user_project if attrs[:name].present?
249
        authorize! :change_visibility_level, user_project if attrs[:visibility].present?
250

251 252
        attrs[:builds_enabled] = attrs.delete(:jobs_enabled) if attrs.has_key?(:jobs_enabled)

253
        result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
254

255
        if result[:status] == :success
256 257
          present user_project, with: Entities::Project,
                                user_can_admin_project: can?(current_user, :admin_project, user_project)
258 259
        else
          render_validation_error!(user_project)
260 261 262
        end
      end

R
Robert Schilling 已提交
263 264 265
      desc 'Archive a project' do
        success Entities::Project
      end
266
      post ':id/archive' do
267 268 269 270
        authorize!(:archive_project, user_project)

        user_project.archive!

271
        present user_project, with: Entities::Project
272 273
      end

R
Robert Schilling 已提交
274 275 276
      desc 'Unarchive a project' do
        success Entities::Project
      end
277
      post ':id/unarchive' do
278 279 280 281
        authorize!(:archive_project, user_project)

        user_project.unarchive!

282
        present user_project, with: Entities::Project
283 284
      end

R
Robert Schilling 已提交
285 286 287
      desc 'Star a project' do
        success Entities::Project
      end
288
      post ':id/star' do
R
Robert Schilling 已提交
289 290 291
        if current_user.starred?(user_project)
          not_modified!
        else
292 293
          current_user.toggle_star(user_project)
          user_project.reload
294

295 296 297 298
          present user_project, with: Entities::Project
        end
      end

R
Robert Schilling 已提交
299 300 301
      desc 'Unstar a project' do
        success Entities::Project
      end
302
      post ':id/unstar' do
303 304 305
        if current_user.starred?(user_project)
          current_user.toggle_star(user_project)
          user_project.reload
306

307 308 309 310 311 312
          present user_project, with: Entities::Project
        else
          not_modified!
        end
      end

R
Robert Schilling 已提交
313
      desc 'Remove a project'
314 315
      delete ":id" do
        authorize! :remove_project, user_project
S
Stan Hu 已提交
316
        ::Projects::DestroyService.new(user_project, current_user, {}).async_execute
317 318

        accepted!
319
      end
A
Angus MacArthur 已提交
320

R
Robert Schilling 已提交
321 322 323 324
      desc 'Mark this project as forked from another'
      params do
        requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from'
      end
325 326
      post ":id/fork/:forked_from_id" do
        authenticated_as_admin!
R
Robert Schilling 已提交
327

328
        forked_from_project = find_project!(params[:forked_from_id])
R
Robert Schilling 已提交
329 330 331 332
        not_found!("Source Project") unless forked_from_project

        if user_project.forked_from_project.nil?
          user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id)
333
        else
R
Robert Schilling 已提交
334
          render_api_error!("Project already forked", 409)
335 336 337
        end
      end

R
Robert Schilling 已提交
338
      desc 'Remove a forked_from relationship'
339
      delete ":id/fork" do
340
        authorize! :remove_fork_project, user_project
R
Robert Schilling 已提交
341

342
        if user_project.forked?
343
          user_project.forked_project_link.destroy
R
Robert Schilling 已提交
344 345
        else
          not_modified!
346 347
        end
      end
D
Douwe Maan 已提交
348

R
Robert Schilling 已提交
349 350 351 352 353 354 355 356
      desc 'Share the project with a group' do
        success Entities::ProjectGroupLink
      end
      params do
        requires :group_id, type: Integer, desc: 'The ID of a group'
        requires :group_access, type: Integer, values: Gitlab::Access.values, desc: 'The group access level'
        optional :expires_at, type: Date, desc: 'Share expiration date'
      end
357 358
      post ":id/share" do
        authorize! :admin_project, user_project
R
Robert Schilling 已提交
359
        group = Group.find_by_id(params[:group_id])
360 361 362 363 364

        unless group && can?(current_user, :read_group, group)
          not_found!('Group')
        end

365 366 367 368
        unless user_project.allowed_to_share_with_group?
          return render_api_error!("The project sharing with group is disabled", 400)
        end

R
Robert Schilling 已提交
369
        link = user_project.project_group_links.new(declared_params(include_missing: false))
370

371 372 373 374 375 376 377
        if link.save
          present link, with: Entities::ProjectGroupLink
        else
          render_api_error!(link.errors.full_messages.first, 409)
        end
      end

378 379 380 381 382 383 384 385 386 387 388 389
      params do
        requires :group_id, type: Integer, desc: 'The ID of the group'
      end
      delete ":id/share/:group_id" do
        authorize! :admin_project, user_project

        link = user_project.project_group_links.find_by(group_id: params[:group_id])
        not_found!('Group Link') unless link

        link.destroy
      end

R
Robert Schilling 已提交
390 391 392 393
      desc 'Upload a file'
      params do
        requires :file, type: File, desc: 'The file to be uploaded'
      end
D
Douwe Maan 已提交
394
      post ":id/uploads" do
395
        UploadService.new(user_project, params[:file]).execute
D
Douwe Maan 已提交
396 397
      end

R
Robert Schilling 已提交
398 399 400 401 402 403
      desc 'Get the users list of a project' do
        success Entities::UserBasic
      end
      params do
        optional :search, type: String, desc: 'Return list of users matching the search criteria'
        use :pagination
404
      end
405
      get ':id/users' do
406
        users = user_project.team.users
R
Robert Schilling 已提交
407 408 409
        users = users.search(params[:search]) if params[:search].present?

        present paginate(users), with: Entities::UserBasic
410
      end
411 412 413 414 415 416 417 418 419 420 421 422 423

      desc 'Start the housekeeping task for a project' do
        detail 'This feature was introduced in GitLab 9.0.'
      end
      post ':id/housekeeping' do
        authorize_admin_project

        begin
          ::Projects::HousekeepingService.new(user_project).execute
        rescue ::Projects::HousekeepingService::LeaseTaken => error
          conflict!(error.message)
        end
      end
N
Nihad Abbasov 已提交
424 425 426
    end
  end
end