projects.rb 18.0 KB
Newer Older
N
Nihad Abbasov 已提交
1 2 3 4 5 6
module Gitlab
  # Projects API
  class Projects < Grape::API
    before { authenticate! }

    resource :projects do
7 8 9 10 11 12 13 14 15
      helpers do
        def handle_project_member_errors(errors)
          if errors[:project_access].any?
            error!(errors[:project_access], 422)
          end
          not_found!
        end
      end

N
Nihad Abbasov 已提交
16 17 18 19 20
      # Get a projects list for authenticated user
      #
      # Example Request:
      #   GET /projects
      get do
21
        @projects = paginate current_user.authorized_projects
22
        present @projects, with: Entities::Project
N
Nihad Abbasov 已提交
23 24 25 26 27
      end

      # Get a single project
      #
      # Parameters:
28
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
29 30 31
      # Example Request:
      #   GET /projects/:id
      get ":id" do
32
        present user_project, with: Entities::Project
N
Nihad Abbasov 已提交
33 34
      end

35 36 37 38
      # Create new project
      #
      # Parameters:
      #   name (required) - name for new project
39 40 41 42 43 44
      #   description (optional) - short project description
      #   default_branch (optional) - 'master' by default
      #   issues_enabled (optional) - enabled by default
      #   wall_enabled (optional) - enabled by default
      #   merge_requests_enabled (optional) - enabled by default
      #   wiki_enabled (optional) - enabled by default
45 46 47
      # Example Request
      #   POST /projects
      post do
48
        required_attributes! [:name]
49
        attrs = attributes_for_keys [:name,
50 51 52 53 54
                                    :description,
                                    :default_branch,
                                    :issues_enabled,
                                    :wall_enabled,
                                    :merge_requests_enabled,
S
Sebastian Ziebell 已提交
55
                                    :wiki_enabled]
56
        @project = ::Projects::CreateContext.new(current_user, attrs).execute
57 58 59
        if @project.saved?
          present @project, with: Entities::Project
        else
60 61 62
          if @project.errors[:limit_reached].present?
            error!(@project.errors[:limit_reached], 403)
          end
63
          not_found!
64 65 66
        end
      end

A
Angus MacArthur 已提交
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
      # Create new project for a specified user.  Only available to admin users.
      #
      # Parameters:
      #   user_id (required) - The ID of a user
      #   name (required) - name for new project
      #   description (optional) - short project description
      #   default_branch (optional) - 'master' by default
      #   issues_enabled (optional) - enabled by default
      #   wall_enabled (optional) - enabled by default
      #   merge_requests_enabled (optional) - enabled by default
      #   wiki_enabled (optional) - enabled by default
      # Example Request
      #   POST /projects/user/:user_id
      post "user/:user_id" do
        authenticated_as_admin!
        user = User.find(params[:user_id])
        attrs = attributes_for_keys [:name,
                                    :description,
                                    :default_branch,
                                    :issues_enabled,
                                    :wall_enabled,
                                    :merge_requests_enabled,
                                    :wiki_enabled]
        @project = ::Projects::CreateContext.new(user, attrs).execute
        if @project.saved?
          present @project, with: Entities::Project
        else
          not_found!
        end
      end


N
Nihad Abbasov 已提交
99
      # Get a project team members
M
miks 已提交
100 101
      #
      # Parameters:
102
      #   id (required) - The ID of a project
V
Valeriy Sizov 已提交
103
      #   query         - Query string
M
miks 已提交
104
      # Example Request:
N
Nihad Abbasov 已提交
105 106
      #   GET /projects/:id/members
      get ":id/members" do
V
Valeriy Sizov 已提交
107 108 109 110 111
        if params[:query].present?
          @members = paginate user_project.users.where("username LIKE ?", "%#{params[:query]}%")
        else
          @members = paginate user_project.users
        end
N
Nihad Abbasov 已提交
112
        present @members, with: Entities::ProjectMember, project: user_project
M
miks 已提交
113 114
      end

N
Nihad Abbasov 已提交
115
      # Get a project team members
116 117
      #
      # Parameters:
118
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
119
      #   user_id (required) - The ID of a user
120
      # Example Request:
N
Nihad Abbasov 已提交
121 122 123 124 125 126 127 128 129
      #   GET /projects/:id/members/:user_id
      get ":id/members/:user_id" do
        @member = user_project.users.find params[:user_id]
        present @member, with: Entities::ProjectMember, project: user_project
      end

      # Add a new project team member
      #
      # Parameters:
130
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
131 132 133 134 135
      #   user_id (required) - The ID of a user
      #   access_level (required) - Project access level
      # Example Request:
      #   POST /projects/:id/members
      post ":id/members" do
R
randx 已提交
136
        authorize! :admin_project, user_project
137
        required_attributes! [:user_id, :access_level]
138 139 140 141 142 143 144 145 146 147 148 149

        # either the user is already a team member or a new one
        team_member = user_project.team_member_by_id(params[:user_id])
        if team_member.nil?
          team_member = user_project.users_projects.new(
            user_id: params[:user_id],
            project_access: params[:access_level]
          )
        end

        if team_member.save
          @member = team_member.user
N
Nihad Abbasov 已提交
150 151
          present @member, with: Entities::ProjectMember, project: user_project
        else
152
          handle_project_member_errors team_member.errors
N
Nihad Abbasov 已提交
153
        end
154 155
      end

N
Nihad Abbasov 已提交
156
      # Update project team member
M
miks 已提交
157 158
      #
      # Parameters:
159
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
160 161
      #   user_id (required) - The ID of a team member
      #   access_level (required) - Project access level
M
miks 已提交
162
      # Example Request:
N
Nihad Abbasov 已提交
163 164
      #   PUT /projects/:id/members/:user_id
      put ":id/members/:user_id" do
R
randx 已提交
165
        authorize! :admin_project, user_project
166
        required_attributes! [:access_level]
N
Nihad Abbasov 已提交
167

168
        team_member = user_project.users_projects.find_by_user_id(params[:user_id])
169
        not_found!("User can not be found") if team_member.nil?
170 171 172

        if team_member.update_attributes(project_access: params[:access_level])
          @member = team_member.user
N
Nihad Abbasov 已提交
173 174
          present @member, with: Entities::ProjectMember, project: user_project
        else
175
          handle_project_member_errors team_member.errors
N
Nihad Abbasov 已提交
176
        end
M
miks 已提交
177 178
      end

N
Nihad Abbasov 已提交
179
      # Remove a team member from project
M
miks 已提交
180 181
      #
      # Parameters:
182
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
183
      #   user_id (required) - The ID of a team member
M
miks 已提交
184
      # Example Request:
N
Nihad Abbasov 已提交
185 186
      #   DELETE /projects/:id/members/:user_id
      delete ":id/members/:user_id" do
R
randx 已提交
187
        authorize! :admin_project, user_project
188 189 190
        team_member = user_project.users_projects.find_by_user_id(params[:user_id])
        unless team_member.nil?
          team_member.destroy
191 192 193
        else
          {:message => "Access revoked", :id => params[:user_id].to_i}
        end
M
miks 已提交
194 195
      end

M
miks 已提交
196 197 198
      # Get project hooks
      #
      # Parameters:
199
      #   id (required) - The ID of a project
M
miks 已提交
200 201 202
      # Example Request:
      #   GET /projects/:id/hooks
      get ":id/hooks" do
M
miks 已提交
203
        authorize! :admin_project, user_project
M
miks 已提交
204 205 206
        @hooks = paginate user_project.hooks
        present @hooks, with: Entities::Hook
      end
S
Saito 已提交
207

J
jozefvaclavik 已提交
208 209 210
      # Get a project hook
      #
      # Parameters:
211
      #   id (required) - The ID of a project
J
jozefvaclavik 已提交
212 213 214 215
      #   hook_id (required) - The ID of a project hook
      # Example Request:
      #   GET /projects/:id/hooks/:hook_id
      get ":id/hooks/:hook_id" do
216
        authorize! :admin_project, user_project
J
jozefvaclavik 已提交
217 218 219
        @hook = user_project.hooks.find(params[:hook_id])
        present @hook, with: Entities::Hook
      end
S
Saito 已提交
220

M
miks 已提交
221 222 223 224

      # Add hook to project
      #
      # Parameters:
225
      #   id (required) - The ID of a project
M
miks 已提交
226 227 228 229
      #   url (required) - The hook URL
      # Example Request:
      #   POST /projects/:id/hooks
      post ":id/hooks" do
M
miks 已提交
230
        authorize! :admin_project, user_project
231
        required_attributes! [:url]
232

M
miks 已提交
233 234 235 236
        @hook = user_project.hooks.new({"url" => params[:url]})
        if @hook.save
          present @hook, with: Entities::Hook
        else
237 238 239
          if @hook.errors[:url].present?
            error!("Invalid url given", 422)
          end
240
          not_found!
M
miks 已提交
241 242
        end
      end
S
Saito 已提交
243

J
jozefvaclavik 已提交
244 245 246
      # Update an existing project hook
      #
      # Parameters:
247
      #   id (required) - The ID of a project
J
jozefvaclavik 已提交
248 249 250 251 252 253 254
      #   hook_id (required) - The ID of a project hook
      #   url (required) - The hook URL
      # Example Request:
      #   PUT /projects/:id/hooks/:hook_id
      put ":id/hooks/:hook_id" do
        @hook = user_project.hooks.find(params[:hook_id])
        authorize! :admin_project, user_project
255
        required_attributes! [:url]
J
jozefvaclavik 已提交
256

257
        attrs = attributes_for_keys [:url]
J
jozefvaclavik 已提交
258 259 260
        if @hook.update_attributes attrs
          present @hook, with: Entities::Hook
        else
261 262 263
          if @hook.errors[:url].present?
            error!("Invalid url given", 422)
          end
J
jozefvaclavik 已提交
264 265 266
          not_found!
        end
      end
M
miks 已提交
267

268
      # Deletes project hook. This is an idempotent function.
M
miks 已提交
269 270
      #
      # Parameters:
271
      #   id (required) - The ID of a project
M
miks 已提交
272 273
      #   hook_id (required) - The ID of hook to delete
      # Example Request:
274
      #   DELETE /projects/:id/hooks/:hook_id
275
      delete ":id/hooks" do
M
miks 已提交
276
        authorize! :admin_project, user_project
277
        required_attributes! [:hook_id]
278 279 280 281 282

        begin
          @hook = ProjectHook.find(params[:hook_id])
          @hook.destroy
        rescue
283
          # ProjectHook can raise Error if hook_id not found
284
        end
M
miks 已提交
285 286
      end

N
Nihad Abbasov 已提交
287 288 289
      # Get a project repository branches
      #
      # Parameters:
290
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
291 292 293
      # Example Request:
      #   GET /projects/:id/repository/branches
      get ":id/repository/branches" do
294
        present user_project.repo.heads.sort_by(&:name), with: Entities::RepoObject, project: user_project
N
Nihad Abbasov 已提交
295 296
      end

297 298 299
      # Get a single branch
      #
      # Parameters:
300
      #   id (required) - The ID of a project
301
      #   branch (required) - The name of the branch
302
      # Example Request:
303 304 305
      #   GET /projects/:id/repository/branches/:branch
      get ":id/repository/branches/:branch" do
        @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
306
        not_found!("Branch does not exist") if @branch.nil?
307 308 309 310 311 312 313 314 315 316 317 318
        present @branch, with: Entities::RepoObject, project: user_project
      end

      # Protect a single branch
      #
      # Parameters:
      #   id (required) - The ID of a project
      #   branch (required) - The name of the branch
      # Example Request:
      #   PUT /projects/:id/repository/branches/:branch/protect
      put ":id/repository/branches/:branch/protect" do
        @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
319
        not_found! unless @branch
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
        protected = user_project.protected_branches.find_by_name(@branch.name)

        unless protected
          user_project.protected_branches.create(:name => @branch.name)
        end

        present @branch, with: Entities::RepoObject, project: user_project
      end

      # Unprotect a single branch
      #
      # Parameters:
      #   id (required) - The ID of a project
      #   branch (required) - The name of the branch
      # Example Request:
      #   PUT /projects/:id/repository/branches/:branch/unprotect
      put ":id/repository/branches/:branch/unprotect" do
        @branch = user_project.repo.heads.find { |item| item.name == params[:branch] }
338
        not_found! unless @branch
339 340 341 342 343 344 345
        protected = user_project.protected_branches.find_by_name(@branch.name)

        if protected
          protected.destroy
        end

        present @branch, with: Entities::RepoObject, project: user_project
346 347
      end

N
Nihad Abbasov 已提交
348 349 350
      # Get a project repository tags
      #
      # Parameters:
351
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
352 353 354
      # Example Request:
      #   GET /projects/:id/repository/tags
      get ":id/repository/tags" do
355
        present user_project.repo.tags.sort_by(&:name).reverse, with: Entities::RepoObject
N
Nihad Abbasov 已提交
356
      end
N
Nihad Abbasov 已提交
357

358 359 360
      # Get a project repository commits
      #
      # Parameters:
361
      #   id (required) - The ID of a project
362 363 364
      #   ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
      #   page (optional) - The page number of the commit pagination
      #   per_page (optional) - The number of elements per page used in pagination
365 366 367 368 369 370
      # Example Request:
      #   GET /projects/:id/repository/commits
      get ":id/repository/commits" do
        authorize! :download_code, user_project

        page = params[:page] || 0
N
Nihad Abbasov 已提交
371
        per_page = (params[:per_page] || 20).to_i
372 373
        ref = params[:ref_name] || user_project.try(:default_branch) || 'master'

D
Dmitriy Zaporozhets 已提交
374
        commits = user_project.repository.commits(ref, nil, per_page, page * per_page)
375 376 377
        present CommitDecorator.decorate(commits), with: Entities::RepoCommit
      end

N
Nihad Abbasov 已提交
378 379 380
      # Get a project snippets
      #
      # Parameters:
381
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
382 383 384 385 386 387
      # Example Request:
      #   GET /projects/:id/snippets
      get ":id/snippets" do
        present paginate(user_project.snippets), with: Entities::ProjectSnippet
      end

N
Nihad Abbasov 已提交
388 389 390
      # Get a project snippet
      #
      # Parameters:
391
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
392 393 394 395
      #   snippet_id (required) - The ID of a project snippet
      # Example Request:
      #   GET /projects/:id/snippets/:snippet_id
      get ":id/snippets/:snippet_id" do
N
Nihad Abbasov 已提交
396
        @snippet = user_project.snippets.find(params[:snippet_id])
397
        present @snippet, with: Entities::ProjectSnippet
N
Nihad Abbasov 已提交
398 399 400 401 402
      end

      # Create a new project snippet
      #
      # Parameters:
403
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
404 405 406 407 408 409 410
      #   title (required) - The title of a snippet
      #   file_name (required) - The name of a snippet file
      #   lifetime (optional) - The expiration date of a snippet
      #   code (required) - The content of a snippet
      # Example Request:
      #   POST /projects/:id/snippets
      post ":id/snippets" do
411
        authorize! :write_snippet, user_project
412
        required_attributes! [:title, :file_name, :code]
413

A
Alex Denisov 已提交
414
        attrs = attributes_for_keys [:title, :file_name]
A
Alex Denisov 已提交
415 416 417
        attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
        attrs[:content] = params[:code] if params[:code].present?
        @snippet = user_project.snippets.new attrs
N
Nihad Abbasov 已提交
418 419 420
        @snippet.author = current_user

        if @snippet.save
421
          present @snippet, with: Entities::ProjectSnippet
N
Nihad Abbasov 已提交
422
        else
423
          not_found!
N
Nihad Abbasov 已提交
424 425 426
        end
      end

427 428 429
      # Update an existing project snippet
      #
      # Parameters:
430
      #   id (required) - The ID of a project
431 432 433 434 435 436 437 438
      #   snippet_id (required) - The ID of a project snippet
      #   title (optional) - The title of a snippet
      #   file_name (optional) - The name of a snippet file
      #   lifetime (optional) - The expiration date of a snippet
      #   code (optional) - The content of a snippet
      # Example Request:
      #   PUT /projects/:id/snippets/:snippet_id
      put ":id/snippets/:snippet_id" do
N
Nihad Abbasov 已提交
439
        @snippet = user_project.snippets.find(params[:snippet_id])
R
randx 已提交
440 441
        authorize! :modify_snippet, @snippet

A
Alex Denisov 已提交
442
        attrs = attributes_for_keys [:title, :file_name]
A
Alex Denisov 已提交
443 444
        attrs[:expires_at] = params[:lifetime] if params[:lifetime].present?
        attrs[:content] = params[:code] if params[:code].present?
445

A
Alex Denisov 已提交
446
        if @snippet.update_attributes attrs
447
          present @snippet, with: Entities::ProjectSnippet
448
        else
449
          not_found!
450 451 452
        end
      end

N
Nihad Abbasov 已提交
453 454 455
      # Delete a project snippet
      #
      # Parameters:
456
      #   id (required) - The ID of a project
N
Nihad Abbasov 已提交
457 458 459 460
      #   snippet_id (required) - The ID of a project snippet
      # Example Request:
      #   DELETE /projects/:id/snippets/:snippet_id
      delete ":id/snippets/:snippet_id" do
461 462 463 464 465 466
        begin
          @snippet = user_project.snippets.find(params[:snippet_id])
          authorize! :modify_snippet, user_project
          @snippet.destroy
        rescue
        end
N
Nihad Abbasov 已提交
467
      end
468 469 470 471

      # Get a raw project snippet
      #
      # Parameters:
472
      #   id (required) - The ID of a project
473 474 475 476
      #   snippet_id (required) - The ID of a project snippet
      # Example Request:
      #   GET /projects/:id/snippets/:snippet_id/raw
      get ":id/snippets/:snippet_id/raw" do
N
Nihad Abbasov 已提交
477
        @snippet = user_project.snippets.find(params[:snippet_id])
478
        content_type 'text/plain'
479 480
        present @snippet.content
      end
481 482 483 484

      # Get a raw file contents
      #
      # Parameters:
485
      #   id (required) - The ID of a project
486
      #   sha (required) - The commit or branch name
487 488 489 490
      #   filepath (required) - The path to the file to display
      # Example Request:
      #   GET /projects/:id/repository/commits/:sha/blob
      get ":id/repository/commits/:sha/blob" do
491
        authorize! :download_code, user_project
492
        required_attributes! [:filepath]
493

494 495
        ref = params[:sha]

D
Dmitriy Zaporozhets 已提交
496
        commit = user_project.repository.commit ref
497
        not_found! "Commit" unless commit
498

D
Dmitriy Zaporozhets 已提交
499
        tree = Tree.new commit.tree, ref, params[:filepath]
500
        not_found! "File" unless tree.try(:tree)
501

S
Saito 已提交
502
        content_type tree.mime_type
503 504 505
        present tree.data
      end

M
Matt Humphrey 已提交
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
      # Get a specific project's keys
      #
      # Example Request:
      #   GET /projects/:id/keys
      get ":id/keys" do
        present user_project.deploy_keys, with: Entities::SSHKey
      end

      # Get single key owned by currently authenticated user
      #
      # Example Request:
      #   GET /projects/:id/keys/:id
      get ":id/keys/:key_id" do
        key = user_project.deploy_keys.find params[:key_id]
        present key, with: Entities::SSHKey
      end

      # Add new ssh key to currently authenticated user
      #
      # Parameters:
      #   key (required) - New SSH Key
      #   title (required) - New SSH Key's title
      # Example Request:
      #   POST /projects/:id/keys
      post ":id/keys" do
        attrs = attributes_for_keys [:title, :key]
        key = user_project.deploy_keys.new attrs
        if key.save
          present key, with: Entities::SSHKey
        else
          not_found!
        end
      end

      # Delete existed ssh key of currently authenticated user
      #
      # Example Request:
      #   DELETE /projects/:id/keys/:id
      delete ":id/keys/:key_id" do
        key = user_project.deploy_keys.find params[:key_id]
        key.delete
      end

N
Nihad Abbasov 已提交
549 550 551
    end
  end
end