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:
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"snippets_enabled": true,
"created_at": "2016-04-05T21:40:50.169Z",
"last_activity_at": "2016-04-06T16:52:08.432Z",
......@@ -100,7 +100,7 @@ Example response:
"star_count": 1,
"forks_count": 0,
"open_issues_count": 3,
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"request_access_enabled": false
}
......@@ -158,7 +158,7 @@ Example response:
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": true,
"created_at": "2016-06-17T07:47:25.578Z",
......@@ -175,7 +175,7 @@ Example response:
"star_count": 0,
"forks_count": 0,
"open_issues_count": 3,
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"request_access_enabled": false
},
......@@ -196,7 +196,7 @@ Example response:
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": true,
"created_at": "2016-06-17T07:47:24.661Z",
......@@ -213,7 +213,7 @@ Example response:
"star_count": 0,
"forks_count": 0,
"open_issues_count": 8,
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"request_access_enabled": false
}
......@@ -236,7 +236,7 @@ Example response:
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": true,
"created_at": "2016-06-17T07:47:27.089Z",
......@@ -253,7 +253,7 @@ Example response:
"star_count": 0,
"forks_count": 0,
"open_issues_count": 4,
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [
{
"group_id": 4,
......@@ -359,7 +359,7 @@ Example response:
"issues_enabled": true,
"merge_requests_enabled": true,
"wiki_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"snippets_enabled": true,
"created_at": "2016-04-05T21:40:50.169Z",
"last_activity_at": "2016-04-06T16:52:08.432Z",
......@@ -375,7 +375,7 @@ Example response:
"star_count": 1,
"forks_count": 0,
"open_issues_count": 3,
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"request_access_enabled": false
}
......
# Builds API
# Jobs API
## List project builds
## List project jobs
Get a list of builds in a project.
Get a list of jobs in a project.
```
GET /projects/:id/builds
GET /projects/:id/jobs
```
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided |
| `scope` | string **or** array of strings | no | The scope of jobs to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all jobs if none provided |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/1/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/1/jobs?scope%5B0%5D=pending&scope%5B1%5D=running'
```
Example of response
......@@ -115,125 +115,21 @@ Example of response
]
```
## List commit builds
## Get a single job
Get a list of builds for specific commit in a project.
This endpoint will return all builds, from all pipelines for a given commit.
If the commit SHA is not found, it will respond with 404, otherwise it will
return an array of builds (an empty array if there are no builds for this
particular commit).
```
GET /projects/:id/repository/commits/:sha/builds
```
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `sha` | string | yes | The SHA id of a commit |
| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v4/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds?scope%5B0%5D=pending&scope%5B1%5D=running'
```
Example of response
```json
[
{
"commit": {
"author_email": "admin@example.com",
"author_name": "Administrator",
"created_at": "2015-12-24T16:51:14.000+01:00",
"id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"message": "Test the CI integration.",
"short_id": "0ff3ae19",
"title": "Test the CI integration."
},
"coverage": null,
"created_at": "2016-01-11T10:13:33.506Z",
"artifacts_file": null,
"finished_at": "2016-01-11T10:14:09.526Z",
"id": 69,
"name": "rubocop",
"pipeline": {
"id": 6,
"ref": "master",
"sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"status": "pending"
},
"ref": "master",
"runner": null,
"stage": "test",
"started_at": null,
"status": "canceled",
"tag": false,
"user": null
},
{
"commit": {
"author_email": "admin@example.com",
"author_name": "Administrator",
"created_at": "2015-12-24T16:51:14.000+01:00",
"id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"message": "Test the CI integration.",
"short_id": "0ff3ae19",
"title": "Test the CI integration."
},
"coverage": null,
"created_at": "2015-12-24T15:51:21.957Z",
"artifacts_file": null,
"finished_at": "2015-12-24T17:54:33.913Z",
"id": 9,
"name": "brakeman",
"pipeline": {
"id": 6,
"ref": "master",
"sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"status": "pending"
},
"ref": "master",
"runner": null,
"stage": "test",
"started_at": "2015-12-24T17:54:33.727Z",
"status": "failed",
"tag": false,
"user": {
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"bio": null,
"created_at": "2015-12-21T13:14:24.077Z",
"id": 1,
"is_admin": true,
"linkedin": "",
"name": "Administrator",
"skype": "",
"state": "active",
"twitter": "",
"username": "root",
"web_url": "http://gitlab.dev/root",
"website_url": ""
}
}
]
```
## Get a single build
Get a single build of a project
Get a single job of a project
```
GET /projects/:id/builds/:build_id
GET /projects/:id/jobs/:job_id
```
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `build_id` | integer | yes | The ID of a build |
| `job_id` | integer | yes | The ID of a job |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/8"
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/jobs/8"
```
Example of response
......@@ -285,23 +181,23 @@ Example of response
}
```
## Get build artifacts
## Get job artifacts
> [Introduced][ce-2893] in GitLab 8.5
Get build artifacts of a project
Get job artifacts of a project
```
GET /projects/:id/builds/:build_id/artifacts
GET /projects/:id/jobs/:job_id/artifacts
```
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `build_id` | integer | yes | The ID of a build |
| `job_id` | integer | yes | The ID of a job |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/8/artifacts"
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/jobs/8/artifacts"
```
Response:
......@@ -318,10 +214,10 @@ Response:
> [Introduced][ce-5347] in GitLab 8.10.
Download the artifacts file from the given reference name and job provided the
build finished successfully.
job finished successfully.
```
GET /projects/:id/builds/artifacts/:ref_name/download?job=name
GET /projects/:id/jobs/artifacts/:ref_name/download?job=name
```
Parameters
......@@ -335,7 +231,7 @@ Parameters
Example request:
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/artifacts/master/download?job=test"
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/jobs/artifacts/master/download?job=test"
```
Example response:
......@@ -349,19 +245,19 @@ Example response:
## Get a trace file
Get a trace of a specific build of a project
Get a trace of a specific job of a project
```
GET /projects/:id/builds/:build_id/trace
GET /projects/:id/jobs/:job_id/trace
```
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
| id | integer | yes | The ID of a project |
| build_id | integer | yes | The ID of a build |
| job_id | integer | yes | The ID of a job |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/8/trace"
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/jobs/8/trace"
```
Response:
......@@ -371,21 +267,21 @@ Response:
| 200 | Serves the trace file |
| 404 | Build not found or no trace file |
## Cancel a build
## Cancel a job
Cancel a single build of a project
Cancel a single job of a project
```
POST /projects/:id/builds/:build_id/cancel
POST /projects/:id/jobs/:job_id/cancel
```
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `build_id` | integer | yes | The ID of a build |
| `job_id` | integer | yes | The ID of a job |
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/cancel"
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/jobs/1/cancel"
```
Example of response
......@@ -417,21 +313,21 @@ Example of response
}
```
## Retry a build
## Retry a job
Retry a single build of a project
Retry a single job of a project
```
POST /projects/:id/builds/:build_id/retry
POST /projects/:id/jobs/:job_id/retry
```
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `build_id` | integer | yes | The ID of a build |
| `job_id` | integer | yes | The ID of a job |
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/retry"
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/jobs/1/retry"
```
Example of response
......@@ -463,12 +359,12 @@ Example of response
}
```
## Erase a build
## Erase a job
Erase a single build of a project (remove build artifacts and a build trace)
Erase a single job of a project (remove job artifacts and a job trace)
```
POST /projects/:id/builds/:build_id/erase
POST /projects/:id/jobs/:job_id/erase
```
Parameters
......@@ -476,12 +372,12 @@ Parameters
| Attribute | Type | Required | Description |
|-------------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `build_id` | integer | yes | The ID of a build |
| `job_id` | integer | yes | The ID of a job |
Example of request
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/erase"
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/jobs/1/erase"
```
Example of response
......@@ -518,7 +414,7 @@ Example of response
Prevents artifacts from being deleted when expiration is set.
```
POST /projects/:id/builds/:build_id/artifacts/keep
POST /projects/:id/jobs/:job_id/artifacts/keep
```
Parameters
......@@ -526,12 +422,12 @@ Parameters
| Attribute | Type | Required | Description |
|-------------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `build_id` | integer | yes | The ID of a build |
| `job_id` | integer | yes | The ID of a job |
Example request:
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/artifacts/keep"
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/jobs/1/artifacts/keep"
```
Example response:
......@@ -563,21 +459,21 @@ Example response:
}
```
## Play a build
## Play a job
Triggers a manual action to start a build.
Triggers a manual action to start a job.
```
POST /projects/:id/builds/:build_id/play
POST /projects/:id/jobs/:job_id/play
```
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `build_id` | integer | yes | The ID of a build |
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
| `job_id` | integer | yes | The ID of a job |
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/builds/1/play"
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/jobs/1/play"
```
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
......@@ -173,7 +173,7 @@ Response:
}
```
## Cancel a pipelines builds
## Cancel a pipelines jobs
> [Introduced][ce-5837] in GitLab 8.11
......
......@@ -66,7 +66,7 @@ Parameters:
"issues_enabled": true,
"open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"wiki_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": false,
......@@ -86,7 +86,7 @@ Parameters:
"forks_count": 0,
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
......@@ -116,7 +116,7 @@ Parameters:
"issues_enabled": true,
"open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"wiki_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": false,
......@@ -146,7 +146,7 @@ Parameters:
"forks_count": 0,
"star_count": 0,
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
......@@ -196,7 +196,7 @@ Parameters:
"issues_enabled": true,
"open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"wiki_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": false,
......@@ -226,7 +226,7 @@ Parameters:
"forks_count": 0,
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [
{
"group_id": 4,
......@@ -439,15 +439,15 @@ Parameters:
| `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
| `builds_enabled` | boolean | no | Enable builds for this project |
| `jobs_enabled` | boolean | no | Enable jobs for this project |
| `wiki_enabled` | boolean | no | Enable wiki for this project |
| `snippets_enabled` | boolean | no | Enable snippets for this project |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
| `visibility` | String | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `public_jobs` | boolean | no | If `true`, jobs can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
......@@ -472,15 +472,15 @@ Parameters:
| `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
| `builds_enabled` | boolean | no | Enable builds for this project |
| `jobs_enabled` | boolean | no | Enable jobs for this project |
| `wiki_enabled` | boolean | no | Enable wiki for this project |
| `snippets_enabled` | boolean | no | Enable snippets for this project |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `public_jobs` | boolean | no | If `true`, jobs can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
......@@ -504,15 +504,15 @@ Parameters:
| `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
| `builds_enabled` | boolean | no | Enable builds for this project |
| `jobs_enabled` | boolean | no | Enable jobs for this project |
| `wiki_enabled` | boolean | no | Enable wiki for this project |
| `snippets_enabled` | boolean | no | Enable snippets for this project |
| `container_registry_enabled` | boolean | no | Enable container registry for this project |
| `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
| `visibility` | string | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `public_jobs` | boolean | no | If `true`, jobs can be viewed by non-project-members |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
......@@ -572,7 +572,7 @@ Example response:
"issues_enabled": true,
"open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"wiki_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": false,
......@@ -591,7 +591,7 @@ Example response:
"shared_runners_enabled": true,
"forks_count": 0,
"star_count": 1,
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
......@@ -637,7 +637,7 @@ Example response:
"issues_enabled": true,
"open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"wiki_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": false,
......@@ -656,7 +656,7 @@ Example response:
"shared_runners_enabled": true,
"forks_count": 0,
"star_count": 0,
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
......@@ -708,7 +708,7 @@ Example response:
"issues_enabled": true,
"open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"wiki_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": false,
......@@ -738,7 +738,7 @@ Example response:
"forks_count": 0,
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
......@@ -790,7 +790,7 @@ Example response:
"issues_enabled": true,
"open_issues_count": 1,
"merge_requests_enabled": true,
"builds_enabled": true,
"jobs_enabled": true,
"wiki_enabled": true,
"snippets_enabled": false,
"container_registry_enabled": false,
......@@ -820,7 +820,7 @@ Example response:
"forks_count": 0,
"star_count": 0,
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"public_jobs": true,
"shared_with_groups": [],
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
......@@ -955,7 +955,7 @@ Parameters:
"merge_requests_events": true,
"tag_push_events": true,
"note_events": true,
"build_events": true,
"job_events": true,
"pipeline_events": true,
"wiki_page_events": true,
"enable_ssl_verification": true,
......@@ -982,7 +982,7 @@ Parameters:
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
| `tag_push_events` | boolean | no | Trigger hook on tag push events |
| `note_events` | boolean | no | Trigger hook on note events |
| `build_events` | boolean | no | Trigger hook on build events |
| `job_events` | boolean | no | Trigger hook on job events |
| `pipeline_events` | boolean | no | Trigger hook on pipeline events |
| `wiki_events` | boolean | no | Trigger hook on wiki events |
| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
......@@ -1008,7 +1008,7 @@ Parameters:
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
| `tag_push_events` | boolean | no | Trigger hook on tag push events |
| `note_events` | boolean | no | Trigger hook on note events |
| `build_events` | boolean | no | Trigger hook on build events |
| `job_events` | boolean | no | Trigger hook on job events |
| `pipeline_events` | boolean | no | Trigger hook on pipeline events |
| `wiki_events` | boolean | no | Trigger hook on wiki events |
| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
......
......@@ -148,7 +148,7 @@ Get emails for GitLab CI builds.
Set Build-Emails service for a project.
```
PUT /projects/:id/services/builds-email
PUT /projects/:id/services/jobs-email
```
Parameters:
......@@ -157,23 +157,23 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `recipients` | string | yes | Comma-separated list of recipient email addresses |
| `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 /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 /projects/:id/services/builds-email
GET /projects/:id/services/jobs-email
```
## Campfire
......@@ -580,7 +580,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `recipients` | string | yes | Comma-separated list of recipient email addresses |
| `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
......
......@@ -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)
- `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)
- 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)
- `POST /projects/:id/trigger/builds` to `POST /projects/:id/trigger/pipeline`
- Require description when creating a new trigger `POST /projects/:id/triggers`
......@@ -9,6 +9,7 @@ module API
mount ::API::V3::Boards
mount ::API::V3::Branches
mount ::API::V3::BroadcastMessages
mount ::API::V3::Builds
mount ::API::V3::Commits
mount ::API::V3::DeployKeys
mount ::API::V3::Environments
......@@ -81,7 +82,6 @@ module API
mount ::API::Boards
mount ::API::Branches
mount ::API::BroadcastMessages
mount ::API::Builds
mount ::API::Commits
mount ::API::CommitStatuses
mount ::API::DeployKeys
......@@ -91,6 +91,7 @@ module API
mount ::API::Groups
mount ::API::Internal
mount ::API::Issues
mount ::API::Jobs
mount ::API::Keys
mount ::API::Labels
mount ::API::Lint
......
......@@ -49,7 +49,8 @@ module API
class ProjectHook < Hook
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
class BasicProjectDetails < Grape::Entity
......@@ -80,7 +81,7 @@ module API
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(: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 :created_at, :last_activity_at
......@@ -93,7 +94,7 @@ module API
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 :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|
SharedGroup.represent(project.project_group_links.all, options)
end
......@@ -109,7 +110,7 @@ module API
expose :storage_size
expose :repository_size
expose :lfs_objects_size
expose :build_artifacts_size
expose :build_artifacts_size, as: :job_artifacts_size
end
class Member < UserBasic
......@@ -144,7 +145,7 @@ module API
expose :storage_size
expose :repository_size
expose :lfs_objects_size
expose :build_artifacts_size
expose :build_artifacts_size, as: :job_artifacts_size
end
end
end
......@@ -454,7 +455,8 @@ module API
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 :tag_push_events, :note_events, :pipeline_events
expose :build_events, as: :job_events
# Expose serialized properties
expose :properties do |service, options|
field_names = service.fields.
......@@ -626,7 +628,7 @@ module API
expose :id, :token
end
class BuildArtifactFile < Grape::Entity
class JobArtifactFile < Grape::Entity
expose :filename, :size
end
......@@ -634,11 +636,11 @@ module API
expose :id, :sha, :ref, :status
end
class Build < Grape::Entity
class Job < Grape::Entity
expose :id, :status, :stage, :name, :ref, :tag, :coverage
expose :created_at, :started_at, :finished_at
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 :runner, with: Runner
expose :pipeline, with: PipelineBasic
......@@ -676,7 +678,7 @@ module API
expose :id, :iid, :ref, :sha, :created_at
expose :user, using: Entities::UserBasic
expose :environment, using: Entities::EnvironmentBasic
expose :deployable, using: Entities::Build
expose :deployable, using: Entities::Job
end
class RepoLicense < Grape::Entity
......
module API
class Builds < Grape::API
class Jobs < Grape::API
include PaginationParams
before { authenticate! }
......@@ -13,9 +13,10 @@ module API
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
values: ::CommitStatus::AVAILABLE_STATUSES,
coerce_with: ->(scope) {
if scope.is_a?(String)
case scope
when String
[scope]
elsif scope.is_a?(Hashie::Mash)
when Hashie::Mash
scope.values
else
['unknown']
......@@ -24,79 +25,58 @@ module API
end
end
desc 'Get a project builds' do
success Entities::Build
desc 'Get a projects jobs' do
success Entities::Job
end
params do
use :optional_scope
use :pagination
end
get ':id/builds' do
get ':id/jobs' do
builds = user_project.builds.order('id DESC')
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)
end
desc 'Get builds for a specific commit of a project' do
success Entities::Build
desc 'Get a specific job of a project' do
success Entities::Job
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: 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'
requires :job_id, type: Integer, desc: 'The ID of a job'
end
get ':id/builds/:build_id' do
get ':id/jobs/:job_id' do
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)
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'
end
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
get ':id/builds/:build_id/artifacts' do
get ':id/jobs/:job_id/artifacts' do
authorize_read_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
present_artifacts!(build.artifacts_file)
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'
end
params do
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
get ':id/builds/artifacts/:ref_name/download',
get ':id/jobs/artifacts/:ref_name/download',
requirements: { ref_name: /.+/ } do
authorize_read_builds!
......@@ -109,14 +89,14 @@ module API
# 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'
desc 'Get a trace of a specific job of a project'
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
get ':id/builds/:build_id/trace' do
get ':id/jobs/:job_id/trace' do
authorize_read_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
content_type 'text/plain'
......@@ -126,95 +106,95 @@ module API
body trace
end
desc 'Cancel a specific build of a project' do
success Entities::Build
desc 'Cancel a specific job of a project' do
success Entities::Job
end
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
post ':id/builds/:build_id/cancel' do
post ':id/jobs/:job_id/cancel' do
authorize_update_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
build.cancel
present build, with: Entities::Build,
present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Retry a specific build of a project' do
success Entities::Build
success Entities::Job
end
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
post ':id/builds/:build_id/retry' do
post ':id/jobs/:job_id/retry' do
authorize_update_builds!
build = get_build!(params[:build_id])
return forbidden!('Build is not retryable') unless build.retryable?
build = get_build!(params[:job_id])
return forbidden!('Job is not retryable') unless build.retryable?
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)
end
desc 'Erase build (remove artifacts and build trace)' do
success Entities::Build
desc 'Erase job (remove artifacts and the trace)' do
success Entities::Job
end
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
post ':id/builds/:build_id/erase' do
post ':id/jobs/:job_id/erase' do
authorize_update_builds!
build = get_build!(params[:build_id])
return forbidden!('Build is not erasable!') unless build.erasable?
build = get_build!(params[:job_id])
return forbidden!('Job is not erasable!') unless build.erasable?
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)
end
desc 'Keep the artifacts to prevent them from being deleted' do
success Entities::Build
success Entities::Job
end
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
post ':id/builds/:build_id/artifacts/keep' do
post ':id/jobs/:job_id/artifacts/keep' do
authorize_update_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
return not_found!(build) unless build.artifacts?
build.keep_artifacts!
status 200
present build, with: Entities::Build,
present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
desc 'Trigger a manual build' do
success Entities::Build
desc 'Trigger a manual job' do
success Entities::Job
detail 'This feature was added in GitLab 8.11'
end
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
post ":id/builds/:build_id/play" do
post ":id/jobs/:job_id/play" do
authorize_read_builds!
build = get_build!(params[:build_id])
build = get_build!(params[:job_id])
bad_request!("Unplayable Job") unless build.playable?
build.play(current_user)
status 200
present build, with: Entities::Build,
present build, with: Entities::Job,
user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
end
......
......@@ -122,9 +122,9 @@ module API
},
{
required: false,
name: :notify_only_broken_builds,
name: :notify_only_broken_jobs,
type: Boolean,
desc: 'Notify only broken builds'
desc: 'Notify only broken jobs'
}
],
'campfire' => [
......@@ -403,9 +403,9 @@ module API
},
{
required: false,
name: :notify_only_broken_builds,
name: :notify_only_broken_jobs,
type: Boolean,
desc: 'Notify only broken builds'
desc: 'Notify only broken jobs'
}
],
'pivotaltracker' => [
......@@ -611,7 +611,7 @@ module API
desc "Set #{service_slug} service for project"
params do
service_classes.each do |service|
event_names = service.try(:event_names) || []
event_names = service.try(:event_names) || next
event_names.each do |event_name|
services[service.to_param.tr("_", "-")] << {
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
expose :request_access_enabled
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
class ProjectWithAccess < Project
......@@ -195,6 +195,59 @@ module API
class TriggerRequest < Grape::Entity
expose :id, :variables
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
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
]
}
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
before { authenticate! }
before { authorize_admin_project }
......@@ -567,6 +584,57 @@ module API
render_api_error!('400 Bad Request', 400)
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
......
......@@ -76,6 +76,8 @@ describe API::Groups, api: true do
lfs_objects_size: 234,
build_artifacts_size: 345,
}.stringify_keys
exposed_attributes = attributes.dup
exposed_attributes['job_artifacts_size'] = exposed_attributes.delete('build_artifacts_size')
project1.statistics.update!(attributes)
......@@ -85,7 +87,7 @@ describe API::Groups, api: true do
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response)
.to satisfy_one { |group| group['statistics'] == attributes }
.to satisfy_one { |group| group['statistics'] == exposed_attributes }
end
end
......
require 'spec_helper'
describe API::Jobs, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:api_user) { user }
let!(:project) { create(:project, :repository, creator: user, public_builds: false) }
let!(:developer) { create(:project_member, :developer, user: user, project: project) }
let(:reporter) { create(:project_member, :reporter, project: project) }
let(:guest) { create(:project_member, :guest, project: project) }
let!(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) }
let!(:build) { create(:ci_build, pipeline: pipeline) }
describe 'GET /projects/:id/jobs' do
let(:query) { Hash.new }
before do
get api("/projects/#{project.id}/jobs", api_user), query
end
context 'authorized user' do
it 'returns project jobs' do
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
end
it 'returns correct values' do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
end
it 'returns pipeline data' do
json_build = json_response.first
expect(json_build['pipeline']).not_to be_empty
expect(json_build['pipeline']['id']).to eq build.pipeline.id
expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
expect(json_build['pipeline']['status']).to eq build.pipeline.status
end
context 'filter project with one scope element' do
let(:query) { { 'scope' => 'pending' } }
it do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
end
end
context 'filter project with array of scope elements' do
let(:query) { { 'scope[0]' => 'pending', 'scope[1]' => 'running' } }
it do
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
end
end
context 'respond 400 when scope contains invalid state' do
let(:query) { { 'scope[0]' => 'unknown', 'scope[1]' => 'running' } }
it { expect(response).to have_http_status(400) }
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not return project builds' do
expect(response).to have_http_status(401)
end
end
end
describe 'GET /projects/:id/jobs/:job_id' do
before do
get api("/projects/#{project.id}/jobs/#{build.id}", api_user)
end
context 'authorized user' do
it 'returns specific job data' do
expect(response).to have_http_status(200)
expect(json_response['name']).to eq('test')
end
it 'returns pipeline data' do
json_build = json_response
expect(json_build['pipeline']).not_to be_empty
expect(json_build['pipeline']['id']).to eq build.pipeline.id
expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
expect(json_build['pipeline']['status']).to eq build.pipeline.status
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not return specific job data' do
expect(response).to have_http_status(401)
end
end
end
describe 'GET /projects/:id/jobs/:job_id/artifacts' do
before do
get api("/projects/#{project.id}/jobs/#{build.id}/artifacts", api_user)
end
context 'job with artifacts' do
let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
context 'authorized user' do
let(:download_headers) do
{ 'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
end
it 'returns specific job artifacts' do
expect(response).to have_http_status(200)
expect(response.headers).to include(download_headers)
expect(response.body).to match_file(build.artifacts_file.file.file)
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not return specific job artifacts' do
expect(response).to have_http_status(401)
end
end
end
it 'does not return job artifacts if not uploaded' do
expect(response).to have_http_status(404)
end
end
describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do
let(:api_user) { reporter.user }
let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
before do
build.success
end
def get_for_ref(ref = pipeline.ref, job = build.name)
get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job
end
context 'when not logged in' do
let(:api_user) { nil }
before do
get_for_ref
end
it 'gives 401' do
expect(response).to have_http_status(401)
end
end
context 'when logging as guest' do
let(:api_user) { guest.user }
before do
get_for_ref
end
it 'gives 403' do
expect(response).to have_http_status(403)
end
end
context 'non-existing job' do
shared_examples 'not found' do
it { expect(response).to have_http_status(:not_found) }
end
context 'has no such ref' do
before do
get_for_ref('TAIL')
end
it_behaves_like 'not found'
end
context 'has no such job' do
before do
get_for_ref(pipeline.ref, 'NOBUILD')
end
it_behaves_like 'not found'
end
end
context 'find proper job' do
shared_examples 'a valid file' do
let(:download_headers) do
{ 'Content-Transfer-Encoding' => 'binary',
'Content-Disposition' =>
"attachment; filename=#{build.artifacts_file.filename}" }
end
it { expect(response).to have_http_status(200) }
it { expect(response.headers).to include(download_headers) }
end
context 'with regular branch' do
before do
pipeline.reload
pipeline.update(ref: 'master',
sha: project.commit('master').sha)
get_for_ref('master')
end
it_behaves_like 'a valid file'
end
context 'with branch name containing slash' do
before do
pipeline.reload
pipeline.update(ref: 'improve/awesome',
sha: project.commit('improve/awesome').sha)
end
before do
get_for_ref('improve/awesome')
end
it_behaves_like 'a valid file'
end
end
end
describe 'GET /projects/:id/jobs/:job_id/trace' do
let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
before do
get api("/projects/#{project.id}/jobs/#{build.id}/trace", api_user)
end
context 'authorized user' do
it 'returns specific job trace' do
expect(response).to have_http_status(200)
expect(response.body).to eq(build.trace)
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not return specific job trace' do
expect(response).to have_http_status(401)
end
end
end
describe 'POST /projects/:id/jobs/:job_id/cancel' do
before do
post api("/projects/#{project.id}/jobs/#{build.id}/cancel", api_user)
end
context 'authorized user' do
context 'user with :update_build persmission' do
it 'cancels running or pending job' do
expect(response).to have_http_status(201)
expect(project.builds.first.status).to eq('canceled')
end
end
context 'user without :update_build permission' do
let(:api_user) { reporter.user }
it 'does not cancel job' do
expect(response).to have_http_status(403)
end
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not cancel job' do
expect(response).to have_http_status(401)
end
end
end
describe 'POST /projects/:id/jobs/:job_id/retry' do
let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
before do
post api("/projects/#{project.id}/jobs/#{build.id}/retry", api_user)
end
context 'authorized user' do
context 'user with :update_build permission' do
it 'retries non-running job' do
expect(response).to have_http_status(201)
expect(project.builds.first.status).to eq('canceled')
expect(json_response['status']).to eq('pending')
end
end
context 'user without :update_build permission' do
let(:api_user) { reporter.user }
it 'does not retry job' do
expect(response).to have_http_status(403)
end
end
end
context 'unauthorized user' do
let(:api_user) { nil }
it 'does not retry job' do
expect(response).to have_http_status(401)
end
end
end
describe 'POST /projects/:id/jobs/:job_id/erase' do
before do
post api("/projects/#{project.id}/jobs/#{build.id}/erase", user)
end
context 'job is erasable' do
let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
it 'erases job content' do
expect(response).to have_http_status(201)
expect(build.trace).to be_empty
expect(build.artifacts_file.exists?).to be_falsy
expect(build.artifacts_metadata.exists?).to be_falsy
end
it 'updates job' do
build.reload
expect(build.erased_at).to be_truthy
expect(build.erased_by).to eq(user)
end
end
context 'job is not erasable' do
let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
it 'responds with forbidden' do
expect(response).to have_http_status(403)
end
end
end
describe 'POST /projects/:id/jobs/:build_id/artifacts/keep' do
before do
post api("/projects/#{project.id}/jobs/#{build.id}/artifacts/keep", user)
end
context 'artifacts did not expire' do
let(:build) do
create(:ci_build, :trace, :artifacts, :success,
project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days)
end
it 'keeps artifacts' do
expect(response).to have_http_status(200)
expect(build.reload.artifacts_expire_at).to be_nil
end
end
context 'no artifacts' do
let(:build) { create(:ci_build, project: project, pipeline: pipeline) }
it 'responds with not found' do
expect(response).to have_http_status(404)
end
end
end
describe 'POST /projects/:id/jobs/:job_id/play' do
before do
post api("/projects/#{project.id}/jobs/#{build.id}/play", user)
end
context 'on an playable job' do
let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
it 'plays the job' do
expect(response).to have_http_status(200)
expect(json_response['user']['id']).to eq(user.id)
expect(json_response['id']).to eq(build.id)
end
end
context 'on a non-playable job' do
it 'returns a status code 400, Bad Request' do
expect(response).to have_http_status 400
expect(response.body).to match("Unplayable Job")
end
end
end
end
......@@ -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['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['job_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)
......@@ -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['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['job_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)
......@@ -98,7 +98,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
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['job_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)
......@@ -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['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['job_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)
......
......@@ -594,7 +594,7 @@ describe API::Projects, api: true do
expect(json_response['issues_enabled']).to be_present
expect(json_response['merge_requests_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['container_registry_enabled']).to be_present
expect(json_response['created_at']).to be_present
......@@ -605,7 +605,7 @@ describe API::Projects, api: true do
expect(json_response['avatar_url']).to be_nil
expect(json_response['star_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'].length).to eq(1)
expect(json_response['shared_with_groups'][0]['group_id']).to eq(group.id)
......
require 'spec_helper'
describe API::Builds, api: true do
describe API::V3::Builds, api: true do
include ApiHelpers
let(:user) { create(:user) }
......@@ -18,7 +18,7 @@ describe API::Builds, api: true do
before do
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
context 'authorized user' do
......@@ -91,7 +91,7 @@ describe API::Builds, api: true do
describe 'GET /projects/:id/repository/commits/:sha/builds' do
context 'when commit does not exist in repository' 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
it 'responds with 404' do
......@@ -107,7 +107,7 @@ describe API::Builds, api: true do
create(:ci_build, pipeline: pipeline)
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
it 'returns project jobs for specific commit' do
......@@ -130,7 +130,7 @@ describe API::Builds, api: true do
context 'when pipeline has no jobs' do
before do
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
it 'returns an empty array' do
......@@ -146,7 +146,7 @@ describe API::Builds, api: true do
create(:ci_pipeline, project: project, sha: project.commit.id)
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
it 'does not return project jobs' do
......@@ -159,7 +159,7 @@ describe API::Builds, api: true do
describe 'GET /projects/:id/builds/:build_id' 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
context 'authorized user' do
......@@ -189,7 +189,7 @@ describe API::Builds, api: true do
describe 'GET /projects/:id/builds/:build_id/artifacts' 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
context 'job with artifacts' do
......@@ -231,7 +231,7 @@ describe API::Builds, api: true do
end
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
context 'when not logged in' do
......@@ -324,7 +324,7 @@ describe API::Builds, api: true do
let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
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
context 'authorized user' do
......@@ -345,7 +345,7 @@ describe API::Builds, api: true do
describe 'POST /projects/:id/builds/:build_id/cancel' 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
context 'authorized user' do
......@@ -378,7 +378,7 @@ describe API::Builds, api: true do
let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
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
context 'authorized user' do
......@@ -410,7 +410,7 @@ describe API::Builds, api: true do
describe 'POST /projects/:id/builds/:build_id/erase' 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
context 'job is erasable' do
......@@ -440,7 +440,7 @@ describe API::Builds, api: true do
describe 'POST /projects/:id/builds/:build_id/artifacts/keep' 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
context 'artifacts did not expire' do
......@@ -466,7 +466,7 @@ describe API::Builds, api: true do
describe 'POST /projects/:id/builds/:build_id/play' 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
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