Commit b63c41e1 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'zj-builds-to-jobs-api' into 'master'

Rename builds to jobs in the API

Closes #28515

See merge request !9463
parents a4dd5792 699e9553
---
title: Rename builds to job for the v4 API
merge_request: 9463
author:
...@@ -84,7 +84,7 @@ Example response: ...@@ -84,7 +84,7 @@ Example response:
"issues_enabled": true, "issues_enabled": true,
"merge_requests_enabled": true, "merge_requests_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"builds_enabled": true, "jobs_enabled": true,
"snippets_enabled": true, "snippets_enabled": true,
"created_at": "2016-04-05T21:40:50.169Z", "created_at": "2016-04-05T21:40:50.169Z",
"last_activity_at": "2016-04-06T16:52:08.432Z", "last_activity_at": "2016-04-06T16:52:08.432Z",
...@@ -100,7 +100,7 @@ Example response: ...@@ -100,7 +100,7 @@ Example response:
"star_count": 1, "star_count": 1,
"forks_count": 0, "forks_count": 0,
"open_issues_count": 3, "open_issues_count": 3,
"public_builds": true, "public_jobs": true,
"shared_with_groups": [], "shared_with_groups": [],
"request_access_enabled": false "request_access_enabled": false
} }
...@@ -158,7 +158,7 @@ Example response: ...@@ -158,7 +158,7 @@ Example response:
"issues_enabled": true, "issues_enabled": true,
"merge_requests_enabled": true, "merge_requests_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"builds_enabled": true, "jobs_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"container_registry_enabled": true, "container_registry_enabled": true,
"created_at": "2016-06-17T07:47:25.578Z", "created_at": "2016-06-17T07:47:25.578Z",
...@@ -175,7 +175,7 @@ Example response: ...@@ -175,7 +175,7 @@ Example response:
"star_count": 0, "star_count": 0,
"forks_count": 0, "forks_count": 0,
"open_issues_count": 3, "open_issues_count": 3,
"public_builds": true, "public_jobs": true,
"shared_with_groups": [], "shared_with_groups": [],
"request_access_enabled": false "request_access_enabled": false
}, },
...@@ -196,7 +196,7 @@ Example response: ...@@ -196,7 +196,7 @@ Example response:
"issues_enabled": true, "issues_enabled": true,
"merge_requests_enabled": true, "merge_requests_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"builds_enabled": true, "jobs_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"container_registry_enabled": true, "container_registry_enabled": true,
"created_at": "2016-06-17T07:47:24.661Z", "created_at": "2016-06-17T07:47:24.661Z",
...@@ -213,7 +213,7 @@ Example response: ...@@ -213,7 +213,7 @@ Example response:
"star_count": 0, "star_count": 0,
"forks_count": 0, "forks_count": 0,
"open_issues_count": 8, "open_issues_count": 8,
"public_builds": true, "public_jobs": true,
"shared_with_groups": [], "shared_with_groups": [],
"request_access_enabled": false "request_access_enabled": false
} }
...@@ -236,7 +236,7 @@ Example response: ...@@ -236,7 +236,7 @@ Example response:
"issues_enabled": true, "issues_enabled": true,
"merge_requests_enabled": true, "merge_requests_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"builds_enabled": true, "jobs_enabled": true,
"snippets_enabled": false, "snippets_enabled": false,
"container_registry_enabled": true, "container_registry_enabled": true,
"created_at": "2016-06-17T07:47:27.089Z", "created_at": "2016-06-17T07:47:27.089Z",
...@@ -253,7 +253,7 @@ Example response: ...@@ -253,7 +253,7 @@ Example response:
"star_count": 0, "star_count": 0,
"forks_count": 0, "forks_count": 0,
"open_issues_count": 4, "open_issues_count": 4,
"public_builds": true, "public_jobs": true,
"shared_with_groups": [ "shared_with_groups": [
{ {
"group_id": 4, "group_id": 4,
...@@ -359,7 +359,7 @@ Example response: ...@@ -359,7 +359,7 @@ Example response:
"issues_enabled": true, "issues_enabled": true,
"merge_requests_enabled": true, "merge_requests_enabled": true,
"wiki_enabled": true, "wiki_enabled": true,
"builds_enabled": true, "jobs_enabled": true,
"snippets_enabled": true, "snippets_enabled": true,
"created_at": "2016-04-05T21:40:50.169Z", "created_at": "2016-04-05T21:40:50.169Z",
"last_activity_at": "2016-04-06T16:52:08.432Z", "last_activity_at": "2016-04-06T16:52:08.432Z",
...@@ -375,7 +375,7 @@ Example response: ...@@ -375,7 +375,7 @@ Example response:
"star_count": 1, "star_count": 1,
"forks_count": 0, "forks_count": 0,
"open_issues_count": 3, "open_issues_count": 3,
"public_builds": true, "public_jobs": true,
"shared_with_groups": [], "shared_with_groups": [],
"request_access_enabled": false "request_access_enabled": false
} }
......
This diff is collapsed.
...@@ -127,7 +127,7 @@ Example of response ...@@ -127,7 +127,7 @@ Example of response
} }
``` ```
## Retry builds in a pipeline ## Retry jobs in a pipeline
> [Introduced][ce-5837] in GitLab 8.11 > [Introduced][ce-5837] in GitLab 8.11
...@@ -173,7 +173,7 @@ Response: ...@@ -173,7 +173,7 @@ Response:
} }
``` ```
## Cancel a pipelines builds ## Cancel a pipelines jobs
> [Introduced][ce-5837] in GitLab 8.11 > [Introduced][ce-5837] in GitLab 8.11
......
This diff is collapsed.
...@@ -148,7 +148,7 @@ Get emails for GitLab CI builds. ...@@ -148,7 +148,7 @@ Get emails for GitLab CI builds.
Set Build-Emails service for a project. Set Build-Emails service for a project.
``` ```
PUT /projects/:id/services/builds-email PUT /projects/:id/services/jobs-email
``` ```
Parameters: Parameters:
...@@ -157,23 +157,23 @@ Parameters: ...@@ -157,23 +157,23 @@ Parameters:
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `recipients` | string | yes | Comma-separated list of recipient email addresses | | `recipients` | string | yes | Comma-separated list of recipient email addresses |
| `add_pusher` | boolean | no | Add pusher to recipients list | | `add_pusher` | boolean | no | Add pusher to recipients list |
| `notify_only_broken_builds` | boolean | no | Notify only broken builds | | `notify_only_broken_jobs` | boolean | no | Notify only broken jobs |
### Delete Build-Emails service ### Delete Job-Emails service
Delete Build-Emails service for a project. Delete Build-Emails service for a project.
``` ```
DELETE /projects/:id/services/builds-email DELETE /projects/:id/services/jobs-email
``` ```
### Get Build-Emails service settings ### Get Job-Emails service settings
Get Build-Emails service settings for a project. Get Build-Emails service settings for a project.
``` ```
GET /projects/:id/services/builds-email GET /projects/:id/services/jobs-email
``` ```
## Campfire ## Campfire
...@@ -580,7 +580,7 @@ Parameters: ...@@ -580,7 +580,7 @@ Parameters:
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `recipients` | string | yes | Comma-separated list of recipient email addresses | | `recipients` | string | yes | Comma-separated list of recipient email addresses |
| `add_pusher` | boolean | no | Add pusher to recipients list | | `add_pusher` | boolean | no | Add pusher to recipients list |
| `notify_only_broken_builds` | boolean | no | Notify only broken pipelines | | `notify_only_broken_jobs` | boolean | no | Notify only broken pipelines |
### Delete Pipeline-Emails service ### Delete Pipeline-Emails service
......
...@@ -63,6 +63,8 @@ changes are in V4: ...@@ -63,6 +63,8 @@ changes are in V4:
- Return 202 with JSON body on async removals on V4 API (DELETE `/projects/:id/repository/merged_branches` and DELETE `/projects/:id`) [!9449](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9449) - Return 202 with JSON body on async removals on V4 API (DELETE `/projects/:id/repository/merged_branches` and DELETE `/projects/:id`) [!9449](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9449)
- `projects/:id/milestones?iid[]=x&iid[]=y` array filter has been renamed to `iids` [!9096](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9096) - `projects/:id/milestones?iid[]=x&iid[]=y` array filter has been renamed to `iids` [!9096](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9096)
- Return basic info about pipeline in `GET /projects/:id/pipelines` [!8875](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8875) - Return basic info about pipeline in `GET /projects/:id/pipelines` [!8875](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8875)
- Renamed all `build` references to `job` [!9463](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9463)
- Drop GET '/projects/:id/repository/commits/:sha/jobs' [!9463](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9463)
- Rename Build Triggers to be Pipeline Triggers API [!9713](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9713) - Rename Build Triggers to be Pipeline Triggers API [!9713](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9713)
- `POST /projects/:id/trigger/builds` to `POST /projects/:id/trigger/pipeline` - `POST /projects/:id/trigger/builds` to `POST /projects/:id/trigger/pipeline`
- Require description when creating a new trigger `POST /projects/:id/triggers` - Require description when creating a new trigger `POST /projects/:id/triggers`
...@@ -9,6 +9,7 @@ module API ...@@ -9,6 +9,7 @@ module API
mount ::API::V3::Boards mount ::API::V3::Boards
mount ::API::V3::Branches mount ::API::V3::Branches
mount ::API::V3::BroadcastMessages mount ::API::V3::BroadcastMessages
mount ::API::V3::Builds
mount ::API::V3::Commits mount ::API::V3::Commits
mount ::API::V3::DeployKeys mount ::API::V3::DeployKeys
mount ::API::V3::Environments mount ::API::V3::Environments
...@@ -81,7 +82,6 @@ module API ...@@ -81,7 +82,6 @@ module API
mount ::API::Boards mount ::API::Boards
mount ::API::Branches mount ::API::Branches
mount ::API::BroadcastMessages mount ::API::BroadcastMessages
mount ::API::Builds
mount ::API::Commits mount ::API::Commits
mount ::API::CommitStatuses mount ::API::CommitStatuses
mount ::API::DeployKeys mount ::API::DeployKeys
...@@ -91,6 +91,7 @@ module API ...@@ -91,6 +91,7 @@ module API
mount ::API::Groups mount ::API::Groups
mount ::API::Internal mount ::API::Internal
mount ::API::Issues mount ::API::Issues
mount ::API::Jobs
mount ::API::Keys mount ::API::Keys
mount ::API::Labels mount ::API::Labels
mount ::API::Lint mount ::API::Lint
......
...@@ -49,7 +49,8 @@ module API ...@@ -49,7 +49,8 @@ module API
class ProjectHook < Hook class ProjectHook < Hook
expose :project_id, :issues_events, :merge_requests_events expose :project_id, :issues_events, :merge_requests_events
expose :note_events, :build_events, :pipeline_events, :wiki_page_events expose :note_events, :pipeline_events, :wiki_page_events
expose :build_events, as: :job_events
end end
class BasicProjectDetails < Grape::Entity class BasicProjectDetails < Grape::Entity
...@@ -80,7 +81,7 @@ module API ...@@ -80,7 +81,7 @@ module API
expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) } expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) } expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) } expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
expose :created_at, :last_activity_at expose :created_at, :last_activity_at
...@@ -93,7 +94,7 @@ module API ...@@ -93,7 +94,7 @@ module API
expose :star_count, :forks_count expose :star_count, :forks_count
expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? } expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :public_builds expose :public_builds, as: :public_jobs
expose :shared_with_groups do |project, options| expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links.all, options) SharedGroup.represent(project.project_group_links.all, options)
end end
...@@ -109,7 +110,7 @@ module API ...@@ -109,7 +110,7 @@ module API
expose :storage_size expose :storage_size
expose :repository_size expose :repository_size
expose :lfs_objects_size expose :lfs_objects_size
expose :build_artifacts_size expose :build_artifacts_size, as: :job_artifacts_size
end end
class Member < UserBasic class Member < UserBasic
...@@ -144,7 +145,7 @@ module API ...@@ -144,7 +145,7 @@ module API
expose :storage_size expose :storage_size
expose :repository_size expose :repository_size
expose :lfs_objects_size expose :lfs_objects_size
expose :build_artifacts_size expose :build_artifacts_size, as: :job_artifacts_size
end end
end end
end end
...@@ -454,7 +455,8 @@ module API ...@@ -454,7 +455,8 @@ module API
class ProjectService < Grape::Entity class ProjectService < Grape::Entity
expose :id, :title, :created_at, :updated_at, :active expose :id, :title, :created_at, :updated_at, :active
expose :push_events, :issues_events, :merge_requests_events expose :push_events, :issues_events, :merge_requests_events
expose :tag_push_events, :note_events, :build_events, :pipeline_events expose :tag_push_events, :note_events, :pipeline_events
expose :build_events, as: :job_events
# Expose serialized properties # Expose serialized properties
expose :properties do |service, options| expose :properties do |service, options|
field_names = service.fields. field_names = service.fields.
...@@ -626,7 +628,7 @@ module API ...@@ -626,7 +628,7 @@ module API
expose :id, :token expose :id, :token
end end
class BuildArtifactFile < Grape::Entity class JobArtifactFile < Grape::Entity
expose :filename, :size expose :filename, :size
end end
...@@ -634,11 +636,11 @@ module API ...@@ -634,11 +636,11 @@ module API
expose :id, :sha, :ref, :status expose :id, :sha, :ref, :status
end end
class Build < Grape::Entity class Job < Grape::Entity
expose :id, :status, :stage, :name, :ref, :tag, :coverage expose :id, :status, :stage, :name, :ref, :tag, :coverage
expose :created_at, :started_at, :finished_at expose :created_at, :started_at, :finished_at
expose :user, with: User expose :user, with: User
expose :artifacts_file, using: BuildArtifactFile, if: -> (build, opts) { build.artifacts? } expose :artifacts_file, using: JobArtifactFile, if: -> (job, opts) { job.artifacts? }
expose :commit, with: RepoCommit expose :commit, with: RepoCommit
expose :runner, with: Runner expose :runner, with: Runner
expose :pipeline, with: PipelineBasic expose :pipeline, with: PipelineBasic
...@@ -676,7 +678,7 @@ module API ...@@ -676,7 +678,7 @@ module API
expose :id, :iid, :ref, :sha, :created_at expose :id, :iid, :ref, :sha, :created_at
expose :user, using: Entities::UserBasic expose :user, using: Entities::UserBasic
expose :environment, using: Entities::EnvironmentBasic expose :environment, using: Entities::EnvironmentBasic
expose :deployable, using: Entities::Build expose :deployable, using: Entities::Job
end end
class RepoLicense < Grape::Entity class RepoLicense < Grape::Entity
......
module API module API
class Builds < Grape::API class Jobs < Grape::API
include PaginationParams include PaginationParams
before { authenticate! } before { authenticate! }
...@@ -13,9 +13,10 @@ module API ...@@ -13,9 +13,10 @@ module API
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
values: ::CommitStatus::AVAILABLE_STATUSES, values: ::CommitStatus::AVAILABLE_STATUSES,
coerce_with: ->(scope) { coerce_with: ->(scope) {
if scope.is_a?(String) case scope
when String
[scope] [scope]
elsif scope.is_a?(Hashie::Mash) when Hashie::Mash
scope.values scope.values
else else
['unknown'] ['unknown']
...@@ -24,79 +25,58 @@ module API ...@@ -24,79 +25,58 @@ module API
end end
end end
desc 'Get a project builds' do desc 'Get a projects jobs' do
success Entities::Build success Entities::Job
end end
params do params do
use :optional_scope use :optional_scope
use :pagination use :pagination
end end
get ':id/builds' do get ':id/jobs' do
builds = user_project.builds.order('id DESC') builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope]) builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build, present paginate(builds), with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
desc 'Get builds for a specific commit of a project' do desc 'Get a specific job of a project' do
success Entities::Build success Entities::Job
end end
params do params do
requires :sha, type: String, desc: 'The SHA id of a commit' requires :job_id, type: Integer, desc: 'The ID of a job'
use :optional_scope
use :pagination
end
get ':id/repository/commits/:sha/builds' do
authorize_read_builds!
return not_found! unless user_project.commit(params[:sha])
pipelines = user_project.pipelines.where(sha: params[:sha])
builds = user_project.builds.where(pipeline: pipelines).order('id DESC')
builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Get a specific build of a project' do
success Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end end
get ':id/builds/:build_id' do get ':id/jobs/:job_id' do
authorize_read_builds! authorize_read_builds!
build = get_build!(params[:build_id]) build = get_build!(params[:job_id])
present build, with: Entities::Build, present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
desc 'Download the artifacts file from build' do desc 'Download the artifacts file from a job' do
detail 'This feature was introduced in GitLab 8.5' detail 'This feature was introduced in GitLab 8.5'
end end
params do params do
requires :build_id, type: Integer, desc: 'The ID of a build' requires :job_id, type: Integer, desc: 'The ID of a job'
end end
get ':id/builds/:build_id/artifacts' do get ':id/jobs/:job_id/artifacts' do
authorize_read_builds! authorize_read_builds!
build = get_build!(params[:build_id]) build = get_build!(params[:job_id])
present_artifacts!(build.artifacts_file) present_artifacts!(build.artifacts_file)
end end
desc 'Download the artifacts file from build' do desc 'Download the artifacts file from a job' do
detail 'This feature was introduced in GitLab 8.10' detail 'This feature was introduced in GitLab 8.10'
end end
params do params do
requires :ref_name, type: String, desc: 'The ref from repository' requires :ref_name, type: String, desc: 'The ref from repository'
requires :job, type: String, desc: 'The name for the build' requires :job, type: String, desc: 'The name for the job'
end end
get ':id/builds/artifacts/:ref_name/download', get ':id/jobs/artifacts/:ref_name/download',
requirements: { ref_name: /.+/ } do requirements: { ref_name: /.+/ } do
authorize_read_builds! authorize_read_builds!
...@@ -109,14 +89,14 @@ module API ...@@ -109,14 +89,14 @@ module API
# TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace # TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace
# is saved in the DB instead of file). But before that, we need to consider how to replace the value of # is saved in the DB instead of file). But before that, we need to consider how to replace the value of
# `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse. # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
desc 'Get a trace of a specific build of a project' desc 'Get a trace of a specific job of a project'
params do params do
requires :build_id, type: Integer, desc: 'The ID of a build' requires :job_id, type: Integer, desc: 'The ID of a job'
end end
get ':id/builds/:build_id/trace' do get ':id/jobs/:job_id/trace' do
authorize_read_builds! authorize_read_builds!
build = get_build!(params[:build_id]) build = get_build!(params[:job_id])
header 'Content-Disposition', "infile; filename=\"#{build.id}.log\"" header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
content_type 'text/plain' content_type 'text/plain'
...@@ -126,95 +106,95 @@ module API ...@@ -126,95 +106,95 @@ module API
body trace body trace
end end
desc 'Cancel a specific build of a project' do desc 'Cancel a specific job of a project' do
success Entities::Build success Entities::Job
end end
params do params do
requires :build_id, type: Integer, desc: 'The ID of a build' requires :job_id, type: Integer, desc: 'The ID of a job'
end end
post ':id/builds/:build_id/cancel' do post ':id/jobs/:job_id/cancel' do
authorize_update_builds! authorize_update_builds!
build = get_build!(params[:build_id]) build = get_build!(params[:job_id])
build.cancel build.cancel
present build, with: Entities::Build, present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
desc 'Retry a specific build of a project' do desc 'Retry a specific build of a project' do
success Entities::Build success Entities::Job
end end
params do params do
requires :build_id, type: Integer, desc: 'The ID of a build' requires :job_id, type: Integer, desc: 'The ID of a build'
end end
post ':id/builds/:build_id/retry' do post ':id/jobs/:job_id/retry' do
authorize_update_builds! authorize_update_builds!
build = get_build!(params[:build_id]) build = get_build!(params[:job_id])
return forbidden!('Build is not retryable') unless build.retryable? return forbidden!('Job is not retryable') unless build.retryable?
build = Ci::Build.retry(build, current_user) build = Ci::Build.retry(build, current_user)
present build, with: Entities::Build, present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
desc 'Erase build (remove artifacts and build trace)' do desc 'Erase job (remove artifacts and the trace)' do
success Entities::Build success Entities::Job
end end
params do params do
requires :build_id, type: Integer, desc: 'The ID of a build' requires :job_id, type: Integer, desc: 'The ID of a build'
end end
post ':id/builds/:build_id/erase' do post ':id/jobs/:job_id/erase' do
authorize_update_builds! authorize_update_builds!
build = get_build!(params[:build_id]) build = get_build!(params[:job_id])
return forbidden!('Build is not erasable!') unless build.erasable? return forbidden!('Job is not erasable!') unless build.erasable?
build.erase(erased_by: current_user) build.erase(erased_by: current_user)
present build, with: Entities::Build, present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
end end
desc 'Keep the artifacts to prevent them from being deleted' do desc 'Keep the artifacts to prevent them from being deleted' do
success Entities::Build success Entities::Job
end end
params do params do
requires :build_id, type: Integer, desc: 'The ID of a build' requires :job_id, type: Integer, desc: 'The ID of a job'
end end
post ':id/builds/:build_id/artifacts/keep' do post ':id/jobs/:job_id/artifacts/keep' do
authorize_update_builds! authorize_update_builds!
build = get_build!(params[:build_id]) build = get_build!(params[:job_id])
return not_found!(build) unless build.artifacts? return not_found!(build) unless build.artifacts?
build.keep_artifacts! build.keep_artifacts!
status 200 status 200
present build, with: Entities::Build, present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
desc 'Trigger a manual build' do desc 'Trigger a manual job' do
success Entities::Build success Entities::Job
detail 'This feature was added in GitLab 8.11' detail 'This feature was added in GitLab 8.11'
end end
params do params do
requires :build_id, type: Integer, desc: 'The ID of a Build' requires :job_id, type: Integer, desc: 'The ID of a Job'
end end
post ":id/builds/:build_id/play" do post ":id/jobs/:job_id/play" do
authorize_read_builds! authorize_read_builds!
build = get_build!(params[:build_id]) build = get_build!(params[:job_id])
bad_request!("Unplayable Job") unless build.playable? bad_request!("Unplayable Job") unless build.playable?
build.play(current_user) build.play(current_user)
status 200 status 200
present build, with: Entities::Build, present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project) user_can_download_artifacts: can?(current_user, :read_build, user_project)
end end
end end
......
...@@ -122,9 +122,9 @@ module API ...@@ -122,9 +122,9 @@ module API
}, },
{ {
required: false, required: false,
name: :notify_only_broken_builds, name: :notify_only_broken_jobs,
type: Boolean, type: Boolean,
desc: 'Notify only broken builds' desc: 'Notify only broken jobs'
} }
], ],
'campfire' => [ 'campfire' => [
...@@ -403,9 +403,9 @@ module API ...@@ -403,9 +403,9 @@ module API
}, },
{ {
required: false, required: false,
name: :notify_only_broken_builds, name: :notify_only_broken_jobs,
type: Boolean, type: Boolean,
desc: 'Notify only broken builds' desc: 'Notify only broken jobs'
} }
], ],
'pivotaltracker' => [ 'pivotaltracker' => [
...@@ -611,7 +611,7 @@ module API ...@@ -611,7 +611,7 @@ module API
desc "Set #{service_slug} service for project" desc "Set #{service_slug} service for project"
params do params do
service_classes.each do |service| service_classes.each do |service|
event_names = service.try(:event_names) || [] event_names = service.try(:event_names) || next
event_names.each do |event_name| event_names.each do |event_name|
services[service.to_param.tr("_", "-")] << { services[service.to_param.tr("_", "-")] << {
required: false, required: false,
......
module API
module V3
class Builds < Grape::API
include PaginationParams
before { authenticate! }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
helpers do
params :optional_scope do
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
values: %w(pending running failed success canceled skipped),
coerce_with: ->(scope) {
if scope.is_a?(String)
[scope]
elsif scope.is_a?(Hashie::Mash)
scope.values
else
['unknown']
end
}
end
end
desc 'Get a project builds' do
success ::API::V3::Entities::Build
end
params do
use :optional_scope
use :pagination
end
get ':id/builds' do
builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope])
present paginate(builds), with: ::API::V3::Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Get builds for a specific commit of a project' do
success ::API::V3::Entities::Build
end
params do
requires :sha, type: String, desc: 'The SHA id of a commit'
use :optional_scope
use :pagination
end
get ':id/repository/commits/:sha/builds' do
authorize_read_builds!
return not_found! unless user_project.commit(params[:sha])
pipelines = user_project.pipelines.where(sha: params[:sha])
builds = user_project.builds.where(pipeline: pipelines).order('id DESC')
builds = filter_builds(builds, params[:scope])
present paginate(builds), with: ::API::V3::Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Get a specific build of a project' do
success ::API::V3::Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
get ':id/builds/:build_id' do
authorize_read_builds!
build = get_build!(params[:build_id])
present build, with: ::API::V3::Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Download the artifacts file from build' do
detail 'This feature was introduced in GitLab 8.5'
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
get ':id/builds/:build_id/artifacts' do
authorize_read_builds!
build = get_build!(params[:build_id])
present_artifacts!(build.artifacts_file)
end
desc 'Download the artifacts file from build' do
detail 'This feature was introduced in GitLab 8.10'
end
params do
requires :ref_name, type: String, desc: 'The ref from repository'
requires :job, type: String, desc: 'The name for the build'
end
get ':id/builds/artifacts/:ref_name/download',
requirements: { ref_name: /.+/ } do
authorize_read_builds!
builds = user_project.latest_successful_builds_for(params[:ref_name])
latest_build = builds.find_by!(name: params[:job])
present_artifacts!(latest_build.artifacts_file)
end
# TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace
# is saved in the DB instead of file). But before that, we need to consider how to replace the value of
# `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
desc 'Get a trace of a specific build of a project'
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
get ':id/builds/:build_id/trace' do
authorize_read_builds!
build = get_build!(params[:build_id])
header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
content_type 'text/plain'
env['api.format'] = :binary
trace = build.trace
body trace
end
desc 'Cancel a specific build of a project' do
success ::API::V3::Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/cancel' do
authorize_update_builds!
build = get_build!(params[:build_id])
build.cancel
present build, with: ::API::V3::Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Retry a specific build of a project' do
success ::API::V3::Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/retry' do
authorize_update_builds!
build = get_build!(params[:build_id])
return forbidden!('Build is not retryable') unless build.retryable?
build = Ci::Build.retry(build, current_user)
present build, with: ::API::V3::Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Erase build (remove artifacts and build trace)' do
success ::API::V3::Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/erase' do
authorize_update_builds!
build = get_build!(params[:build_id])
return forbidden!('Build is not erasable!') unless build.erasable?
build.erase(erased_by: current_user)
present build, with: ::API::V3::Entities::Build,
user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
end
desc 'Keep the artifacts to prevent them from being deleted' do
success ::API::V3::Entities::Build
end
params do
requires :build_id, type: Integer, desc: 'The ID of a build'
end
post ':id/builds/:build_id/artifacts/keep' do
authorize_update_builds!
build = get_build!(params[:build_id])
return not_found!(build) unless build.artifacts?
build.keep_artifacts!
status 200
present build, with: ::API::V3::Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Trigger a manual build' do
success ::API::V3::Entities::Build
detail 'This feature was added in GitLab 8.11'
end
params do
requires :build_id, type: Integer, desc: 'The ID of a Build'
end
post ":id/builds/:build_id/play" do
authorize_read_builds!
build = get_build!(params[:build_id])
bad_request!("Unplayable Job") unless build.playable?
build.play(current_user)
status 200
present build, with: ::API::V3::Entities::Build,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
end
helpers do
def get_build(id)
user_project.builds.find_by(id: id.to_i)
end
def get_build!(id)
get_build(id) || not_found!
end
def present_artifacts!(artifacts_file)
if !artifacts_file.file_storage?
redirect_to(build.artifacts_file.url)
elsif artifacts_file.exists?
present_file!(artifacts_file.path, artifacts_file.filename)
else
not_found!
end
end
def filter_builds(builds, scope)
return builds if scope.nil? || scope.empty?
available_statuses = ::CommitStatus::AVAILABLE_STATUSES
unknown = scope - available_statuses
render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty?
builds.where(status: available_statuses && scope)
end
def authorize_read_builds!
authorize! :read_build, user_project
end
def authorize_update_builds!
authorize! :update_build, user_project
end
end
end
end
end
module API
# Deployments RESTfull API endpoints
class Deployments < Grape::API
include PaginationParams
before { authenticate! }
params do
requires :id, type: String, desc: 'The project ID'
end
resource :projects do
desc 'Get all deployments of the project' do
detail 'This feature was introduced in GitLab 8.11.'
success ::API::V3::Deployments
end
params do
use :pagination
end
get ':id/deployments' do
authorize! :read_deployment, user_project
present paginate(user_project.deployments), with: ::API::V3::Deployments
end
desc 'Gets a specific deployment' do
detail 'This feature was introduced in GitLab 8.11.'
success ::API::V3::Deployments
end
params do
requires :deployment_id, type: Integer, desc: 'The deployment ID'
end
get ':id/deployments/:deployment_id' do
authorize! :read_deployment, user_project
deployment = user_project.deployments.find(params[:deployment_id])
present deployment, with: ::API::V3::Deployments
end
end
end
end
...@@ -81,7 +81,7 @@ module API ...@@ -81,7 +81,7 @@ module API
expose :request_access_enabled expose :request_access_enabled
expose :only_allow_merge_if_all_discussions_are_resolved expose :only_allow_merge_if_all_discussions_are_resolved
expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics expose :statistics, using: '::API::V3::Entities::ProjectStatistics', if: :statistics
end end
class ProjectWithAccess < Project class ProjectWithAccess < Project
...@@ -195,6 +195,59 @@ module API ...@@ -195,6 +195,59 @@ module API
class TriggerRequest < Grape::Entity class TriggerRequest < Grape::Entity
expose :id, :variables expose :id, :variables
end end
class Build < Grape::Entity
expose :id, :status, :stage, :name, :ref, :tag, :coverage
expose :created_at, :started_at, :finished_at
expose :user, with: ::API::Entities::User
expose :artifacts_file, using: ::API::Entities::JobArtifactFile, if: -> (build, opts) { build.artifacts? }
expose :commit, with: ::API::Entities::RepoCommit
expose :runner, with: ::API::Entities::Runner
expose :pipeline, with: ::API::Entities::PipelineBasic
end
class BuildArtifactFile < Grape::Entity
expose :filename, :size
end
class Deployment < Grape::Entity
expose :id, :iid, :ref, :sha, :created_at
expose :user, using: ::API::Entities::UserBasic
expose :environment, using: ::API::Entities::EnvironmentBasic
expose :deployable, using: Entities::Build
end
class MergeRequestChanges < MergeRequest
expose :diffs, as: :changes, using: ::API::Entities::RepoDiff do |compare, _|
compare.raw_diffs(all_diffs: true).to_a
end
end
class ProjectStatistics < Grape::Entity
expose :commit_count
expose :storage_size
expose :repository_size
expose :lfs_objects_size
expose :build_artifacts_size
end
class ProjectService < Grape::Entity
expose :id, :title, :created_at, :updated_at, :active
expose :push_events, :issues_events, :merge_requests_events
expose :tag_push_events, :note_events, :build_events, :pipeline_events
# Expose serialized properties
expose :properties do |service, options|
field_names = service.fields.
select { |field| options[:include_passwords] || field[:type] != 'password' }.
map { |field| field[:name] }
service.properties.slice(*field_names)
end
end
class ProjectHook < ::API::Entities::Hook
expose :project_id, :issues_events, :merge_requests_events
expose :note_events, :build_events, :pipeline_events, :wiki_page_events
end
end end
end end
end end
module API
module V3
# MergeRequestDiff API
class MergeRequestDiffs < Grape::API
before { authenticate! }
resource :projects do
desc 'Get a list of merge request diff versions' do
detail 'This feature was introduced in GitLab 8.12.'
success ::API::Entities::MergeRequestDiff
end
params do
requires :id, type: String, desc: 'The ID of a project'
requires :merge_request_id, type: Integer, desc: 'The ID of a merge request'
end
get ":id/merge_requests/:merge_request_id/versions" do
merge_request = find_merge_request_with_access(params[:merge_request_id])
present merge_request.merge_request_diffs, with: ::API::Entities::MergeRequestDiff
end
desc 'Get a single merge request diff version' do
detail 'This feature was introduced in GitLab 8.12.'
success ::API::Entities::MergeRequestDiffFull
end
params do
requires :id, type: String, desc: 'The ID of a project'
requires :merge_request_id, type: Integer, desc: 'The ID of a merge request'
requires :version_id, type: Integer, desc: 'The ID of a merge request diff version'
end
get ":id/merge_requests/:merge_request_id/versions/:version_id" do
merge_request = find_merge_request_with_access(params[:merge_request_id])
present merge_request.merge_request_diffs.find(params[:version_id]), with: ::API::Entities::MergeRequestDiffFull
end
end
end
end
end
module API
module V3
class ProjectHooks < Grape::API
include PaginationParams
before { authenticate! }
before { authorize_admin_project }
helpers do
params :project_hook_properties do
requires :url, type: String, desc: "The URL to send the request to"
optional :push_events, type: Boolean, desc: "Trigger hook on push events"
optional :issues_events, type: Boolean, desc: "Trigger hook on issues events"
optional :merge_requests_events, type: Boolean, desc: "Trigger hook on merge request events"
optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events"
optional :note_events, type: Boolean, desc: "Trigger hook on note(comment) events"
optional :build_events, type: Boolean, desc: "Trigger hook on build events"
optional :pipeline_events, type: Boolean, desc: "Trigger hook on pipeline events"
optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events"
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response"
end
end
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
desc 'Get project hooks' do
success ::API::V3::Entities::ProjectHook
end
params do
use :pagination
end
get ":id/hooks" do
hooks = paginate user_project.hooks
present hooks, with: ::API::V3::Entities::ProjectHook
end
desc 'Get a project hook' do
success ::API::V3::Entities::ProjectHook
end
params do
requires :hook_id, type: Integer, desc: 'The ID of a project hook'
end
get ":id/hooks/:hook_id" do
hook = user_project.hooks.find(params[:hook_id])
present hook, with: ::API::V3::Entities::ProjectHook
end
desc 'Add hook to project' do
success ::API::V3::Entities::ProjectHook
end
params do
use :project_hook_properties
end
post ":id/hooks" do
hook = user_project.hooks.new(declared_params(include_missing: false))
if hook.save
present hook, with: ::API::V3::Entities::ProjectHook
else
error!("Invalid url given", 422) if hook.errors[:url].present?
not_found!("Project hook #{hook.errors.messages}")
end
end
desc 'Update an existing project hook' do
success ::API::V3::Entities::ProjectHook
end
params do
requires :hook_id, type: Integer, desc: "The ID of the hook to update"
use :project_hook_properties
end
put ":id/hooks/:hook_id" do
hook = user_project.hooks.find(params.delete(:hook_id))
if hook.update_attributes(declared_params(include_missing: false))
present hook, with: ::API::V3::Entities::ProjectHook
else
error!("Invalid url given", 422) if hook.errors[:url].present?
not_found!("Project hook #{hook.errors.messages}")
end
end
desc 'Deletes project hook' do
success ::API::V3::Entities::ProjectHook
end
params do
requires :hook_id, type: Integer, desc: 'The ID of the hook to delete'
end
delete ":id/hooks/:hook_id" do
begin
present user_project.hooks.destroy(params[:hook_id]), with: ::API::V3::Entities::ProjectHook
rescue
# ProjectHook can raise Error if hook_id not found
not_found!("Error deleting hook #{params[:hook_id]}")
end
end
end
end
end
end
...@@ -537,6 +537,23 @@ module API ...@@ -537,6 +537,23 @@ module API
] ]
} }
trigger_services = {
'mattermost-slash-commands' => [
{
name: :token,
type: String,
desc: 'The Mattermost token'
}
],
'slack-slash-commands' => [
{
name: :token,
type: String,
desc: 'The Slack token'
}
]
}.freeze
resource :projects do resource :projects do
before { authenticate! } before { authenticate! }
before { authorize_admin_project } before { authorize_admin_project }
...@@ -567,6 +584,57 @@ module API ...@@ -567,6 +584,57 @@ module API
render_api_error!('400 Bad Request', 400) render_api_error!('400 Bad Request', 400)
end end
end end
desc 'Get the service settings for project' do
success Entities::ProjectService
end
params do
requires :service_slug, type: String, values: services.keys, desc: 'The name of the service'
end
get ":id/services/:service_slug" do
service = user_project.find_or_initialize_service(params[:service_slug].underscore)
present service, with: Entities::ProjectService, include_passwords: current_user.is_admin?
end
end
trigger_services.each do |service_slug, settings|
helpers do
def chat_command_service(project, service_slug, params)
project.services.active.where(template: false).find do |service|
service.try(:token) == params[:token] && service.to_param == service_slug.underscore
end
end
end
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
desc "Trigger a slash command for #{service_slug}" do
detail 'Added in GitLab 8.13'
end
params do
settings.each do |setting|
requires setting[:name], type: setting[:type], desc: setting[:desc]
end
end
post ":id/services/#{service_slug.underscore}/trigger" do
project = find_project(params[:id])
# This is not accurate, but done to prevent leakage of the project names
not_found!('Service') unless project
service = chat_command_service(project, service_slug, params)
result = service.try(:trigger, params)
if result
status result[:status] || 200
present result
else
not_found!('Service')
end
end
end
end end
end end
end end
......
...@@ -76,6 +76,8 @@ describe API::Groups, api: true do ...@@ -76,6 +76,8 @@ describe API::Groups, api: true do
lfs_objects_size: 234, lfs_objects_size: 234,
build_artifacts_size: 345, build_artifacts_size: 345,
}.stringify_keys }.stringify_keys
exposed_attributes = attributes.dup
exposed_attributes['job_artifacts_size'] = exposed_attributes.delete('build_artifacts_size')
project1.statistics.update!(attributes) project1.statistics.update!(attributes)
...@@ -85,7 +87,7 @@ describe API::Groups, api: true do ...@@ -85,7 +87,7 @@ describe API::Groups, api: true do
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response) expect(json_response)
.to satisfy_one { |group| group['statistics'] == attributes } .to satisfy_one { |group| group['statistics'] == exposed_attributes }
end end
end end
......
This diff is collapsed.
...@@ -33,7 +33,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do ...@@ -33,7 +33,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
expect(json_response.first['merge_requests_events']).to eq(true) expect(json_response.first['merge_requests_events']).to eq(true)
expect(json_response.first['tag_push_events']).to eq(true) expect(json_response.first['tag_push_events']).to eq(true)
expect(json_response.first['note_events']).to eq(true) expect(json_response.first['note_events']).to eq(true)
expect(json_response.first['build_events']).to eq(true) expect(json_response.first['job_events']).to eq(true)
expect(json_response.first['pipeline_events']).to eq(true) expect(json_response.first['pipeline_events']).to eq(true)
expect(json_response.first['wiki_page_events']).to eq(true) expect(json_response.first['wiki_page_events']).to eq(true)
expect(json_response.first['enable_ssl_verification']).to eq(true) expect(json_response.first['enable_ssl_verification']).to eq(true)
...@@ -59,7 +59,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do ...@@ -59,7 +59,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events)
expect(json_response['tag_push_events']).to eq(hook.tag_push_events) expect(json_response['tag_push_events']).to eq(hook.tag_push_events)
expect(json_response['note_events']).to eq(hook.note_events) expect(json_response['note_events']).to eq(hook.note_events)
expect(json_response['build_events']).to eq(hook.build_events) expect(json_response['job_events']).to eq(hook.build_events)
expect(json_response['pipeline_events']).to eq(hook.pipeline_events) expect(json_response['pipeline_events']).to eq(hook.pipeline_events)
expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events) expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events)
expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification) expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification)
...@@ -98,7 +98,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do ...@@ -98,7 +98,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
expect(json_response['merge_requests_events']).to eq(false) expect(json_response['merge_requests_events']).to eq(false)
expect(json_response['tag_push_events']).to eq(false) expect(json_response['tag_push_events']).to eq(false)
expect(json_response['note_events']).to eq(false) expect(json_response['note_events']).to eq(false)
expect(json_response['build_events']).to eq(false) expect(json_response['job_events']).to eq(false)
expect(json_response['pipeline_events']).to eq(false) expect(json_response['pipeline_events']).to eq(false)
expect(json_response['wiki_page_events']).to eq(true) expect(json_response['wiki_page_events']).to eq(true)
expect(json_response['enable_ssl_verification']).to eq(true) expect(json_response['enable_ssl_verification']).to eq(true)
...@@ -144,7 +144,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do ...@@ -144,7 +144,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events) expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events)
expect(json_response['tag_push_events']).to eq(hook.tag_push_events) expect(json_response['tag_push_events']).to eq(hook.tag_push_events)
expect(json_response['note_events']).to eq(hook.note_events) expect(json_response['note_events']).to eq(hook.note_events)
expect(json_response['build_events']).to eq(hook.build_events) expect(json_response['job_events']).to eq(hook.build_events)
expect(json_response['pipeline_events']).to eq(hook.pipeline_events) expect(json_response['pipeline_events']).to eq(hook.pipeline_events)
expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events) expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events)
expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification) expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification)
......
...@@ -594,7 +594,7 @@ describe API::Projects, api: true do ...@@ -594,7 +594,7 @@ describe API::Projects, api: true do
expect(json_response['issues_enabled']).to be_present expect(json_response['issues_enabled']).to be_present
expect(json_response['merge_requests_enabled']).to be_present expect(json_response['merge_requests_enabled']).to be_present
expect(json_response['wiki_enabled']).to be_present expect(json_response['wiki_enabled']).to be_present
expect(json_response['builds_enabled']).to be_present expect(json_response['jobs_enabled']).to be_present
expect(json_response['snippets_enabled']).to be_present expect(json_response['snippets_enabled']).to be_present
expect(json_response['container_registry_enabled']).to be_present expect(json_response['container_registry_enabled']).to be_present
expect(json_response['created_at']).to be_present expect(json_response['created_at']).to be_present
...@@ -605,7 +605,7 @@ describe API::Projects, api: true do ...@@ -605,7 +605,7 @@ describe API::Projects, api: true do
expect(json_response['avatar_url']).to be_nil expect(json_response['avatar_url']).to be_nil
expect(json_response['star_count']).to be_present expect(json_response['star_count']).to be_present
expect(json_response['forks_count']).to be_present expect(json_response['forks_count']).to be_present
expect(json_response['public_builds']).to be_present expect(json_response['public_jobs']).to be_present
expect(json_response['shared_with_groups']).to be_an Array expect(json_response['shared_with_groups']).to be_an Array
expect(json_response['shared_with_groups'].length).to eq(1) expect(json_response['shared_with_groups'].length).to eq(1)
expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id) expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
......
require 'spec_helper' require 'spec_helper'
describe API::Builds, api: true do describe API::V3::Builds, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -18,7 +18,7 @@ describe API::Builds, api: true do ...@@ -18,7 +18,7 @@ describe API::Builds, api: true do
before do before do
create(:ci_build, :skipped, pipeline: pipeline) create(:ci_build, :skipped, pipeline: pipeline)
get api("/projects/#{project.id}/builds?#{query}", api_user) get v3_api("/projects/#{project.id}/builds?#{query}", api_user)
end end
context 'authorized user' do context 'authorized user' do
...@@ -91,7 +91,7 @@ describe API::Builds, api: true do ...@@ -91,7 +91,7 @@ describe API::Builds, api: true do
describe 'GET /projects/:id/repository/commits/:sha/builds' do describe 'GET /projects/:id/repository/commits/:sha/builds' do
context 'when commit does not exist in repository' do context 'when commit does not exist in repository' do
before do before do
get api("/projects/#{project.id}/repository/commits/1a271fd1/builds", api_user) get v3_api("/projects/#{project.id}/repository/commits/1a271fd1/builds", api_user)
end end
it 'responds with 404' do it 'responds with 404' do
...@@ -107,7 +107,7 @@ describe API::Builds, api: true do ...@@ -107,7 +107,7 @@ describe API::Builds, api: true do
create(:ci_build, pipeline: pipeline) create(:ci_build, pipeline: pipeline)
create(:ci_build) create(:ci_build)
get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user) get v3_api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user)
end end
it 'returns project jobs for specific commit' do it 'returns project jobs for specific commit' do
...@@ -130,7 +130,7 @@ describe API::Builds, api: true do ...@@ -130,7 +130,7 @@ describe API::Builds, api: true do
context 'when pipeline has no jobs' do context 'when pipeline has no jobs' do
before do before do
branch_head = project.commit('feature').id branch_head = project.commit('feature').id
get api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user) get v3_api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user)
end end
it 'returns an empty array' do it 'returns an empty array' do
...@@ -146,7 +146,7 @@ describe API::Builds, api: true do ...@@ -146,7 +146,7 @@ describe API::Builds, api: true do
create(:ci_pipeline, project: project, sha: project.commit.id) create(:ci_pipeline, project: project, sha: project.commit.id)
create(:ci_build, pipeline: pipeline) create(:ci_build, pipeline: pipeline)
get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil) get v3_api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil)
end end
it 'does not return project jobs' do it 'does not return project jobs' do
...@@ -159,7 +159,7 @@ describe API::Builds, api: true do ...@@ -159,7 +159,7 @@ describe API::Builds, api: true do
describe 'GET /projects/:id/builds/:build_id' do describe 'GET /projects/:id/builds/:build_id' do
before do before do
get api("/projects/#{project.id}/builds/#{build.id}", api_user) get v3_api("/projects/#{project.id}/builds/#{build.id}", api_user)
end end
context 'authorized user' do context 'authorized user' do
...@@ -189,7 +189,7 @@ describe API::Builds, api: true do ...@@ -189,7 +189,7 @@ describe API::Builds, api: true do
describe 'GET /projects/:id/builds/:build_id/artifacts' do describe 'GET /projects/:id/builds/:build_id/artifacts' do
before do before do
get api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user) get v3_api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user)
end end
context 'job with artifacts' do context 'job with artifacts' do
...@@ -231,7 +231,7 @@ describe API::Builds, api: true do ...@@ -231,7 +231,7 @@ describe API::Builds, api: true do
end end
def path_for_ref(ref = pipeline.ref, job = build.name) def path_for_ref(ref = pipeline.ref, job = build.name)
api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", api_user) v3_api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", api_user)
end end
context 'when not logged in' do context 'when not logged in' do
...@@ -324,7 +324,7 @@ describe API::Builds, api: true do ...@@ -324,7 +324,7 @@ describe API::Builds, api: true do
let(:build) { create(:ci_build, :trace, pipeline: pipeline) } let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
before do before do
get api("/projects/#{project.id}/builds/#{build.id}/trace", api_user) get v3_api("/projects/#{project.id}/builds/#{build.id}/trace", api_user)
end end
context 'authorized user' do context 'authorized user' do
...@@ -345,7 +345,7 @@ describe API::Builds, api: true do ...@@ -345,7 +345,7 @@ describe API::Builds, api: true do
describe 'POST /projects/:id/builds/:build_id/cancel' do describe 'POST /projects/:id/builds/:build_id/cancel' do
before do before do
post api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user) post v3_api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user)
end end
context 'authorized user' do context 'authorized user' do
...@@ -378,7 +378,7 @@ describe API::Builds, api: true do ...@@ -378,7 +378,7 @@ describe API::Builds, api: true do
let(:build) { create(:ci_build, :canceled, pipeline: pipeline) } let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
before do before do
post api("/projects/#{project.id}/builds/#{build.id}/retry", api_user) post v3_api("/projects/#{project.id}/builds/#{build.id}/retry", api_user)
end end
context 'authorized user' do context 'authorized user' do
...@@ -410,7 +410,7 @@ describe API::Builds, api: true do ...@@ -410,7 +410,7 @@ describe API::Builds, api: true do
describe 'POST /projects/:id/builds/:build_id/erase' do describe 'POST /projects/:id/builds/:build_id/erase' do
before do before do
post api("/projects/#{project.id}/builds/#{build.id}/erase", user) post v3_api("/projects/#{project.id}/builds/#{build.id}/erase", user)
end end
context 'job is erasable' do context 'job is erasable' do
...@@ -440,7 +440,7 @@ describe API::Builds, api: true do ...@@ -440,7 +440,7 @@ describe API::Builds, api: true do
describe 'POST /projects/:id/builds/:build_id/artifacts/keep' do describe 'POST /projects/:id/builds/:build_id/artifacts/keep' do
before do before do
post api("/projects/#{project.id}/builds/#{build.id}/artifacts/keep", user) post v3_api("/projects/#{project.id}/builds/#{build.id}/artifacts/keep", user)
end end
context 'artifacts did not expire' do context 'artifacts did not expire' do
...@@ -466,7 +466,7 @@ describe API::Builds, api: true do ...@@ -466,7 +466,7 @@ describe API::Builds, api: true do
describe 'POST /projects/:id/builds/:build_id/play' do describe 'POST /projects/:id/builds/:build_id/play' do
before do before do
post api("/projects/#{project.id}/builds/#{build.id}/play", user) post v3_api("/projects/#{project.id}/builds/#{build.id}/play", user)
end end
context 'on an playable job' do context 'on an playable job' do
......
require 'spec_helper'
describe API::Deployments, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:non_member) { create(:user) }
let(:project) { deployment.environment.project }
let!(:deployment) { create(:deployment) }
before do
project.team << [user, :master]
end
shared_examples 'a paginated resources' do
before do
# Fires the request
request
end
it 'has pagination headers' do
expect(response).to include_pagination_headers
end
end
describe 'GET /projects/:id/deployments' do
context 'as member of the project' do
it_behaves_like 'a paginated resources' do
let(:request) { get api("/projects/#{project.id}/deployments", user) }
end
it 'returns projects deployments' do
get api("/projects/#{project.id}/deployments", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
expect(json_response.first['iid']).to eq(deployment.iid)
expect(json_response.first['sha']).to match /\A\h{40}\z/
end
end
context 'as non member' do
it 'returns a 404 status code' do
get api("/projects/#{project.id}/deployments", non_member)
expect(response).to have_http_status(404)
end
end
end
describe 'GET /projects/:id/deployments/:deployment_id' do
context 'as a member of the project' do
it 'returns the projects deployment' do
get api("/projects/#{project.id}/deployments/#{deployment.id}", user)
expect(response).to have_http_status(200)
expect(json_response['sha']).to match /\A\h{40}\z/
expect(json_response['id']).to eq(deployment.id)
end
end
context 'as non member' do
it 'returns a 404 status code' do
get api("/projects/#{project.id}/deployments/#{deployment.id}", non_member)
expect(response).to have_http_status(404)
end
end
end
end
require "spec_helper"
describe API::MergeRequestDiffs, 'MergeRequestDiffs', api: true do
include ApiHelpers
let!(:user) { create(:user) }
let!(:merge_request) { create(:merge_request, importing: true) }
let!(:project) { merge_request.target_project }
before do
merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e')
project.team << [user, :master]
end
describe 'GET /projects/:id/merge_requests/:merge_request_id/versions' do
it 'returns 200 for a valid merge request' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions", user)
merge_request_diff = merge_request.merge_request_diffs.first
expect(response.status).to eq 200
expect(json_response.size).to eq(merge_request.merge_request_diffs.size)
expect(json_response.first['id']).to eq(merge_request_diff.id)
expect(json_response.first['head_commit_sha']).to eq(merge_request_diff.head_commit_sha)
end
it 'returns a 404 when merge_request_id not found' do
get api("/projects/#{project.id}/merge_requests/999/versions", user)
expect(response).to have_http_status(404)
end
end
describe 'GET /projects/:id/merge_requests/:merge_request_id/versions/:version_id' do
it 'returns a 200 for a valid merge request' do
merge_request_diff = merge_request.merge_request_diffs.first
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/#{merge_request_diff.id}", user)
expect(response.status).to eq 200
expect(json_response['id']).to eq(merge_request_diff.id)
expect(json_response['head_commit_sha']).to eq(merge_request_diff.head_commit_sha)
expect(json_response['diffs'].size).to eq(merge_request_diff.diffs.size)
end
it 'returns a 404 when merge_request_id not found' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/versions/999", user)
expect(response).to have_http_status(404)
end
end
end
require 'spec_helper'
describe API::ProjectHooks, 'ProjectHooks', api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:user3) { create(:user) }
let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:hook) do
create(:project_hook,
:all_events_enabled,
project: project,
url: 'http://example.com',
enable_ssl_verification: true)
end
before do
project.team << [user, :master]
project.team << [user3, :developer]
end
describe "GET /projects/:id/hooks" do
context "authorized user" do
it "returns project hooks" do
get v3_api("/projects/#{project.id}/hooks", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.count).to eq(1)
expect(json_response.first['url']).to eq("http://example.com")
expect(json_response.first['issues_events']).to eq(true)
expect(json_response.first['push_events']).to eq(true)
expect(json_response.first['merge_requests_events']).to eq(true)
expect(json_response.first['tag_push_events']).to eq(true)
expect(json_response.first['note_events']).to eq(true)
expect(json_response.first['build_events']).to eq(true)
expect(json_response.first['pipeline_events']).to eq(true)
expect(json_response.first['wiki_page_events']).to eq(true)
expect(json_response.first['enable_ssl_verification']).to eq(true)
end
end
context "unauthorized user" do
it "does not access project hooks" do
get v3_api("/projects/#{project.id}/hooks", user3)
expect(response).to have_http_status(403)
end
end
end
describe "GET /projects/:id/hooks/:hook_id" do
context "authorized user" do
it "returns a project hook" do
get v3_api("/projects/#{project.id}/hooks/#{hook.id}", user)
expect(response).to have_http_status(200)
expect(json_response['url']).to eq(hook.url)
expect(json_response['issues_events']).to eq(hook.issues_events)
expect(json_response['push_events']).to eq(hook.push_events)
expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events)
expect(json_response['tag_push_events']).to eq(hook.tag_push_events)
expect(json_response['note_events']).to eq(hook.note_events)
expect(json_response['build_events']).to eq(hook.build_events)
expect(json_response['pipeline_events']).to eq(hook.pipeline_events)
expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events)
expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification)
end
it "returns a 404 error if hook id is not available" do
get v3_api("/projects/#{project.id}/hooks/1234", user)
expect(response).to have_http_status(404)
end
end
context "unauthorized user" do
it "does not access an existing hook" do
get v3_api("/projects/#{project.id}/hooks/#{hook.id}", user3)
expect(response).to have_http_status(403)
end
end
it "returns a 404 error if hook id is not available" do
get v3_api("/projects/#{project.id}/hooks/1234", user)
expect(response).to have_http_status(404)
end
end
describe "POST /projects/:id/hooks" do
it "adds hook to project" do
expect do
post v3_api("/projects/#{project.id}/hooks", user),
url: "http://example.com", issues_events: true, wiki_page_events: true
end.to change {project.hooks.count}.by(1)
expect(response).to have_http_status(201)
expect(json_response['url']).to eq('http://example.com')
expect(json_response['issues_events']).to eq(true)
expect(json_response['push_events']).to eq(true)
expect(json_response['merge_requests_events']).to eq(false)
expect(json_response['tag_push_events']).to eq(false)
expect(json_response['note_events']).to eq(false)
expect(json_response['build_events']).to eq(false)
expect(json_response['pipeline_events']).to eq(false)
expect(json_response['wiki_page_events']).to eq(true)
expect(json_response['enable_ssl_verification']).to eq(true)
expect(json_response).not_to include('token')
end
it "adds the token without including it in the response" do
token = "secret token"
expect do
post v3_api("/projects/#{project.id}/hooks", user), url: "http://example.com", token: token
end.to change {project.hooks.count}.by(1)
expect(response).to have_http_status(201)
expect(json_response["url"]).to eq("http://example.com")
expect(json_response).not_to include("token")
hook = project.hooks.find(json_response["id"])
expect(hook.url).to eq("http://example.com")
expect(hook.token).to eq(token)
end
it "returns a 400 error if url not given" do
post v3_api("/projects/#{project.id}/hooks", user)
expect(response).to have_http_status(400)
end
it "returns a 422 error if url not valid" do
post v3_api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com"
expect(response).to have_http_status(422)
end
end
describe "PUT /projects/:id/hooks/:hook_id" do
it "updates an existing project hook" do
put v3_api("/projects/#{project.id}/hooks/#{hook.id}", user),
url: 'http://example.org', push_events: false
expect(response).to have_http_status(200)
expect(json_response['url']).to eq('http://example.org')
expect(json_response['issues_events']).to eq(hook.issues_events)
expect(json_response['push_events']).to eq(false)
expect(json_response['merge_requests_events']).to eq(hook.merge_requests_events)
expect(json_response['tag_push_events']).to eq(hook.tag_push_events)
expect(json_response['note_events']).to eq(hook.note_events)
expect(json_response['build_events']).to eq(hook.build_events)
expect(json_response['pipeline_events']).to eq(hook.pipeline_events)
expect(json_response['wiki_page_events']).to eq(hook.wiki_page_events)
expect(json_response['enable_ssl_verification']).to eq(hook.enable_ssl_verification)
end
it "adds the token without including it in the response" do
token = "secret token"
put v3_api("/projects/#{project.id}/hooks/#{hook.id}", user), url: "http://example.org", token: token
expect(response).to have_http_status(200)
expect(json_response["url"]).to eq("http://example.org")
expect(json_response).not_to include("token")
expect(hook.reload.url).to eq("http://example.org")
expect(hook.reload.token).to eq(token)
end
it "returns 404 error if hook id not found" do
put v3_api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org'
expect(response).to have_http_status(404)
end
it "returns 400 error if url is not given" do
put v3_api("/projects/#{project.id}/hooks/#{hook.id}", user)
expect(response).to have_http_status(400)
end
it "returns a 422 error if url is not valid" do
put v3_api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com'
expect(response).to have_http_status(422)
end
end
describe "DELETE /projects/:id/hooks/:hook_id" do
it "deletes hook from project" do
expect do
delete v3_api("/projects/#{project.id}/hooks/#{hook.id}", user)
end.to change {project.hooks.count}.by(-1)
expect(response).to have_http_status(200)
end
it "returns success when deleting hook" do
delete v3_api("/projects/#{project.id}/hooks/#{hook.id}", user)
expect(response).to have_http_status(200)
end
it "returns a 404 error when deleting non existent hook" do
delete v3_api("/projects/#{project.id}/hooks/42", user)
expect(response).to have_http_status(404)
end
it "returns a 404 error if hook id not given" do
delete v3_api("/projects/#{project.id}/hooks", user)
expect(response).to have_http_status(404)
end
it "returns a 404 if a user attempts to delete project hooks he/she does not own" do
test_user = create(:user)
other_project = create(:project)
other_project.team << [test_user, :master]
delete v3_api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
expect(response).to have_http_status(404)
expect(WebHook.exists?(hook.id)).to be_truthy
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment