Merge branch '30892-add-api-support-for-pipeline-schedule' into 'master'
Add API support for pipeline schedule Closes #30892 See merge request !11307
This commit is contained in:
commit
19ee16a0f8
10 changed files with 767 additions and 0 deletions
|
@ -24,6 +24,10 @@ module Ci
|
||||||
owner == current_user
|
owner == current_user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def own!(user)
|
||||||
|
update(owner: user)
|
||||||
|
end
|
||||||
|
|
||||||
def inactive?
|
def inactive?
|
||||||
!active?
|
!active?
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: Add API support for pipeline schedule
|
||||||
|
merge_request: 11307
|
||||||
|
author: dosuken123
|
|
@ -33,6 +33,7 @@ following locations:
|
||||||
- [Notification settings](notification_settings.md)
|
- [Notification settings](notification_settings.md)
|
||||||
- [Pipelines](pipelines.md)
|
- [Pipelines](pipelines.md)
|
||||||
- [Pipeline Triggers](pipeline_triggers.md)
|
- [Pipeline Triggers](pipeline_triggers.md)
|
||||||
|
- [Pipeline Schedules](pipeline_schedules.md)
|
||||||
- [Projects](projects.md) including setting Webhooks
|
- [Projects](projects.md) including setting Webhooks
|
||||||
- [Project Access Requests](access_requests.md)
|
- [Project Access Requests](access_requests.md)
|
||||||
- [Project Members](members.md)
|
- [Project Members](members.md)
|
||||||
|
|
273
doc/api/pipeline_schedules.md
Normal file
273
doc/api/pipeline_schedules.md
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
# Pipeline schedules
|
||||||
|
|
||||||
|
You can read more about [pipeline schedules](../user/project/pipelines/schedules.md).
|
||||||
|
|
||||||
|
## Get all pipeline schedules
|
||||||
|
|
||||||
|
Get a list of the pipeline schedules of a project.
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /projects/:id/pipeline_schedules
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | required | Description |
|
||||||
|
|-----------|---------|----------|---------------------|
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `scope` | string | no | The scope of pipeline schedules, one of: `active`, `inactive` |
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"description": "Test schedule pipeline",
|
||||||
|
"ref": "master",
|
||||||
|
"cron": "* * * * *",
|
||||||
|
"cron_timezone": "Asia/Tokyo",
|
||||||
|
"next_run_at": "2017-05-19T13:41:00.000Z",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2017-05-19T13:31:08.849Z",
|
||||||
|
"updated_at": "2017-05-19T13:40:17.727Z",
|
||||||
|
"owner": {
|
||||||
|
"name": "Administrator",
|
||||||
|
"username": "root",
|
||||||
|
"id": 1,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
|
||||||
|
"web_url": "https://gitlab.example.com/root"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get a single pipeline schedule
|
||||||
|
|
||||||
|
Get the pipeline schedule of a project.
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /projects/:id/pipeline_schedules/:pipeline_schedule_id
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | required | Description |
|
||||||
|
|--------------|---------|----------|--------------------------|
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `pipeline_schedule_id` | integer | yes | The pipeline schedule id |
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules/13"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"description": "Test schedule pipeline",
|
||||||
|
"ref": "master",
|
||||||
|
"cron": "* * * * *",
|
||||||
|
"cron_timezone": "Asia/Tokyo",
|
||||||
|
"next_run_at": "2017-05-19T13:41:00.000Z",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2017-05-19T13:31:08.849Z",
|
||||||
|
"updated_at": "2017-05-19T13:40:17.727Z",
|
||||||
|
"last_pipeline": {
|
||||||
|
"id": 332,
|
||||||
|
"sha": "0e788619d0b5ec17388dffb973ecd505946156db",
|
||||||
|
"ref": "master",
|
||||||
|
"status": "pending"
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"name": "Administrator",
|
||||||
|
"username": "root",
|
||||||
|
"id": 1,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
|
||||||
|
"web_url": "https://gitlab.example.com/root"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create a new pipeline schedule
|
||||||
|
|
||||||
|
Create a new pipeline schedule of a project.
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /projects/:id/pipeline_schedules
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | required | Description |
|
||||||
|
|---------------|---------|----------|--------------------------|
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `description` | string | yes | The description of pipeline schedule |
|
||||||
|
| `ref` | string | yes | The branch/tag name will be triggered |
|
||||||
|
| `cron ` | string | yes | The cron (e.g. `0 1 * * *`) ([Cron syntax](https://en.wikipedia.org/wiki/Cron)) |
|
||||||
|
| `cron_timezone ` | string | no | The timezone supproted by `ActiveSupport::TimeZone` (e.g. `Pacific Time (US & Canada)`) (default: `'UTC'`) |
|
||||||
|
| `active ` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially (default: `true`) |
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --request POST --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" --form description="Build packages" --form ref="master" --form cron="0 1 * * 5" --form cron_timezone="UTC" --form active="true" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"description": "Build packages",
|
||||||
|
"ref": "master",
|
||||||
|
"cron": "0 1 * * 5",
|
||||||
|
"cron_timezone": "UTC",
|
||||||
|
"next_run_at": "2017-05-26T01:00:00.000Z",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2017-05-19T13:43:08.169Z",
|
||||||
|
"updated_at": "2017-05-19T13:43:08.169Z",
|
||||||
|
"last_pipeline": null,
|
||||||
|
"owner": {
|
||||||
|
"name": "Administrator",
|
||||||
|
"username": "root",
|
||||||
|
"id": 1,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
|
||||||
|
"web_url": "https://gitlab.example.com/root"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Edit a pipeline schedule
|
||||||
|
|
||||||
|
Updates the pipeline schedule of a project. Once the update is done, it will be rescheduled automatically.
|
||||||
|
|
||||||
|
```
|
||||||
|
PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | required | Description |
|
||||||
|
|---------------|---------|----------|--------------------------|
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `pipeline_schedule_id` | integer | yes | The pipeline schedule id |
|
||||||
|
| `description` | string | no | The description of pipeline schedule |
|
||||||
|
| `ref` | string | no | The branch/tag name will be triggered |
|
||||||
|
| `cron ` | string | no | The cron (e.g. `0 1 * * *`) ([Cron syntax](https://en.wikipedia.org/wiki/Cron)) |
|
||||||
|
| `cron_timezone ` | string | no | The timezone supproted by `ActiveSupport::TimeZone` (e.g. `Pacific Time (US & Canada)`) or `TZInfo::Timezone` (e.g. `America/Los_Angeles`) |
|
||||||
|
| `active ` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially. |
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --request PUT --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" --form cron="0 2 * * *" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules/13"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"description": "Test schedule pipeline",
|
||||||
|
"ref": "master",
|
||||||
|
"cron": "0 2 * * *",
|
||||||
|
"cron_timezone": "Asia/Tokyo",
|
||||||
|
"next_run_at": "2017-05-19T17:00:00.000Z",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2017-05-19T13:31:08.849Z",
|
||||||
|
"updated_at": "2017-05-19T13:44:16.135Z",
|
||||||
|
"last_pipeline": {
|
||||||
|
"id": 332,
|
||||||
|
"sha": "0e788619d0b5ec17388dffb973ecd505946156db",
|
||||||
|
"ref": "master",
|
||||||
|
"status": "pending"
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"name": "Administrator",
|
||||||
|
"username": "root",
|
||||||
|
"id": 1,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
|
||||||
|
"web_url": "https://gitlab.example.com/root"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Take ownership of a pipeline schedule
|
||||||
|
|
||||||
|
Update the owner of the pipeline schedule of a project.
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /projects/:id/pipeline_schedules/:pipeline_schedule_id/take_ownership
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | required | Description |
|
||||||
|
|---------------|---------|----------|--------------------------|
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `pipeline_schedule_id` | integer | yes | The pipeline schedule id |
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --request POST --header "PRIVATE-TOKEN: hf2CvZXB9w8Uc5pZKpSB" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules/13/take_ownership"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"description": "Test schedule pipeline",
|
||||||
|
"ref": "master",
|
||||||
|
"cron": "0 2 * * *",
|
||||||
|
"cron_timezone": "Asia/Tokyo",
|
||||||
|
"next_run_at": "2017-05-19T17:00:00.000Z",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2017-05-19T13:31:08.849Z",
|
||||||
|
"updated_at": "2017-05-19T13:46:37.468Z",
|
||||||
|
"last_pipeline": {
|
||||||
|
"id": 332,
|
||||||
|
"sha": "0e788619d0b5ec17388dffb973ecd505946156db",
|
||||||
|
"ref": "master",
|
||||||
|
"status": "pending"
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"name": "shinya",
|
||||||
|
"username": "maeda",
|
||||||
|
"id": 50,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://www.gravatar.com/avatar/8ca0a796a679c292e3a11da50f99e801?s=80&d=identicon",
|
||||||
|
"web_url": "https://gitlab.example.com/maeda"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Delete a pipeline schedule
|
||||||
|
|
||||||
|
Delete the pipeline schedule of a project.
|
||||||
|
|
||||||
|
```
|
||||||
|
DELETE /projects/:id/pipeline_schedules/:pipeline_schedule_id
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Type | required | Description |
|
||||||
|
|----------------|---------|----------|--------------------------|
|
||||||
|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||||
|
| `pipeline_schedule_id` | integer | yes | The pipeline schedule id |
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --request DELETE --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" "https://gitlab.example.com/api/v4/projects/29/pipeline_schedules/13"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"description": "Test schedule pipeline",
|
||||||
|
"ref": "master",
|
||||||
|
"cron": "0 2 * * *",
|
||||||
|
"cron_timezone": "Asia/Tokyo",
|
||||||
|
"next_run_at": "2017-05-19T17:00:00.000Z",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2017-05-19T13:31:08.849Z",
|
||||||
|
"updated_at": "2017-05-19T13:46:37.468Z",
|
||||||
|
"last_pipeline": {
|
||||||
|
"id": 332,
|
||||||
|
"sha": "0e788619d0b5ec17388dffb973ecd505946156db",
|
||||||
|
"ref": "master",
|
||||||
|
"status": "pending"
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"name": "shinya",
|
||||||
|
"username": "maeda",
|
||||||
|
"id": 50,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://www.gravatar.com/avatar/8ca0a796a679c292e3a11da50f99e801?s=80&d=identicon",
|
||||||
|
"web_url": "https://gitlab.example.com/maeda"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -110,6 +110,7 @@ module API
|
||||||
mount ::API::Notes
|
mount ::API::Notes
|
||||||
mount ::API::NotificationSettings
|
mount ::API::NotificationSettings
|
||||||
mount ::API::Pipelines
|
mount ::API::Pipelines
|
||||||
|
mount ::API::PipelineSchedules
|
||||||
mount ::API::ProjectHooks
|
mount ::API::ProjectHooks
|
||||||
mount ::API::Projects
|
mount ::API::Projects
|
||||||
mount ::API::ProjectSnippets
|
mount ::API::ProjectSnippets
|
||||||
|
|
|
@ -686,6 +686,17 @@ module API
|
||||||
expose :coverage
|
expose :coverage
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class PipelineSchedule < Grape::Entity
|
||||||
|
expose :id
|
||||||
|
expose :description, :ref, :cron, :cron_timezone, :next_run_at, :active
|
||||||
|
expose :created_at, :updated_at
|
||||||
|
expose :owner, using: Entities::UserBasic
|
||||||
|
end
|
||||||
|
|
||||||
|
class PipelineScheduleDetails < PipelineSchedule
|
||||||
|
expose :last_pipeline, using: Entities::PipelineBasic
|
||||||
|
end
|
||||||
|
|
||||||
class EnvironmentBasic < Grape::Entity
|
class EnvironmentBasic < Grape::Entity
|
||||||
expose :id, :name, :slug, :external_url
|
expose :id, :name, :slug, :external_url
|
||||||
end
|
end
|
||||||
|
|
131
lib/api/pipeline_schedules.rb
Normal file
131
lib/api/pipeline_schedules.rb
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
module API
|
||||||
|
class PipelineSchedules < Grape::API
|
||||||
|
include PaginationParams
|
||||||
|
|
||||||
|
before { authenticate! }
|
||||||
|
|
||||||
|
params do
|
||||||
|
requires :id, type: String, desc: 'The ID of a project'
|
||||||
|
end
|
||||||
|
resource :projects, requirements: { id: %r{[^/]+} } do
|
||||||
|
desc 'Get all pipeline schedules' do
|
||||||
|
success Entities::PipelineSchedule
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
use :pagination
|
||||||
|
optional :scope, type: String, values: %w[active inactive],
|
||||||
|
desc: 'The scope of pipeline schedules'
|
||||||
|
end
|
||||||
|
get ':id/pipeline_schedules' do
|
||||||
|
authorize! :read_pipeline_schedule, user_project
|
||||||
|
|
||||||
|
schedules = PipelineSchedulesFinder.new(user_project).execute(scope: params[:scope])
|
||||||
|
.preload([:owner, :last_pipeline])
|
||||||
|
present paginate(schedules), with: Entities::PipelineSchedule
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Get a single pipeline schedule' do
|
||||||
|
success Entities::PipelineScheduleDetails
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
|
||||||
|
end
|
||||||
|
get ':id/pipeline_schedules/:pipeline_schedule_id' do
|
||||||
|
authorize! :read_pipeline_schedule, user_project
|
||||||
|
|
||||||
|
not_found!('PipelineSchedule') unless pipeline_schedule
|
||||||
|
|
||||||
|
present pipeline_schedule, with: Entities::PipelineScheduleDetails
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Create a new pipeline schedule' do
|
||||||
|
success Entities::PipelineScheduleDetails
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :description, type: String, desc: 'The description of pipeline schedule'
|
||||||
|
requires :ref, type: String, desc: 'The branch/tag name will be triggered'
|
||||||
|
requires :cron, type: String, desc: 'The cron'
|
||||||
|
optional :cron_timezone, type: String, default: 'UTC', desc: 'The timezone'
|
||||||
|
optional :active, type: Boolean, default: true, desc: 'The activation of pipeline schedule'
|
||||||
|
end
|
||||||
|
post ':id/pipeline_schedules' do
|
||||||
|
authorize! :create_pipeline_schedule, user_project
|
||||||
|
|
||||||
|
pipeline_schedule = Ci::CreatePipelineScheduleService
|
||||||
|
.new(user_project, current_user, declared_params(include_missing: false))
|
||||||
|
.execute
|
||||||
|
|
||||||
|
if pipeline_schedule.persisted?
|
||||||
|
present pipeline_schedule, with: Entities::PipelineScheduleDetails
|
||||||
|
else
|
||||||
|
render_validation_error!(pipeline_schedule)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Edit a pipeline schedule' do
|
||||||
|
success Entities::PipelineScheduleDetails
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
|
||||||
|
optional :description, type: String, desc: 'The description of pipeline schedule'
|
||||||
|
optional :ref, type: String, desc: 'The branch/tag name will be triggered'
|
||||||
|
optional :cron, type: String, desc: 'The cron'
|
||||||
|
optional :cron_timezone, type: String, desc: 'The timezone'
|
||||||
|
optional :active, type: Boolean, desc: 'The activation of pipeline schedule'
|
||||||
|
end
|
||||||
|
put ':id/pipeline_schedules/:pipeline_schedule_id' do
|
||||||
|
authorize! :update_pipeline_schedule, user_project
|
||||||
|
|
||||||
|
not_found!('PipelineSchedule') unless pipeline_schedule
|
||||||
|
|
||||||
|
if pipeline_schedule.update(declared_params(include_missing: false))
|
||||||
|
present pipeline_schedule, with: Entities::PipelineScheduleDetails
|
||||||
|
else
|
||||||
|
render_validation_error!(pipeline_schedule)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Take ownership of a pipeline schedule' do
|
||||||
|
success Entities::PipelineScheduleDetails
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
|
||||||
|
end
|
||||||
|
post ':id/pipeline_schedules/:pipeline_schedule_id/take_ownership' do
|
||||||
|
authorize! :update_pipeline_schedule, user_project
|
||||||
|
|
||||||
|
not_found!('PipelineSchedule') unless pipeline_schedule
|
||||||
|
|
||||||
|
if pipeline_schedule.own!(current_user)
|
||||||
|
present pipeline_schedule, with: Entities::PipelineScheduleDetails
|
||||||
|
else
|
||||||
|
render_validation_error!(pipeline_schedule)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Delete a pipeline schedule' do
|
||||||
|
success Entities::PipelineScheduleDetails
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :pipeline_schedule_id, type: Integer, desc: 'The pipeline schedule id'
|
||||||
|
end
|
||||||
|
delete ':id/pipeline_schedules/:pipeline_schedule_id' do
|
||||||
|
authorize! :admin_pipeline_schedule, user_project
|
||||||
|
|
||||||
|
not_found!('PipelineSchedule') unless pipeline_schedule
|
||||||
|
|
||||||
|
status :accepted
|
||||||
|
present pipeline_schedule.destroy, with: Entities::PipelineScheduleDetails
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
helpers do
|
||||||
|
def pipeline_schedule
|
||||||
|
@pipeline_schedule ||=
|
||||||
|
user_project.pipeline_schedules
|
||||||
|
.preload(:owner, :last_pipeline)
|
||||||
|
.find_by(id: params.delete(:pipeline_schedule_id))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
spec/fixtures/api/schemas/pipeline_schedule.json
vendored
Normal file
41
spec/fixtures/api/schemas/pipeline_schedule.json
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties" : {
|
||||||
|
"id": { "type": "integer" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"ref": { "type": "string" },
|
||||||
|
"cron": { "type": "string" },
|
||||||
|
"cron_timezone": { "type": "string" },
|
||||||
|
"next_run_at": { "type": "date" },
|
||||||
|
"active": { "type": "boolean" },
|
||||||
|
"created_at": { "type": "date" },
|
||||||
|
"updated_at": { "type": "date" },
|
||||||
|
"last_pipeline": {
|
||||||
|
"type": ["object", "null"],
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "integer" },
|
||||||
|
"sha": { "type": "string" },
|
||||||
|
"ref": { "type": "string" },
|
||||||
|
"status": { "type": "string" }
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": { "type": "string" },
|
||||||
|
"username": { "type": "string" },
|
||||||
|
"id": { "type": "integer" },
|
||||||
|
"state": { "type": "string" },
|
||||||
|
"avatar_url": { "type": "uri" },
|
||||||
|
"web_url": { "type": "uri" }
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"id", "description", "ref", "cron", "cron_timezone", "next_run_at",
|
||||||
|
"active", "created_at", "updated_at", "owner"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
4
spec/fixtures/api/schemas/pipeline_schedules.json
vendored
Normal file
4
spec/fixtures/api/schemas/pipeline_schedules.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "pipeline_schedule.json" }
|
||||||
|
}
|
297
spec/requests/api/pipeline_schedules_spec.rb
Normal file
297
spec/requests/api/pipeline_schedules_spec.rb
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe API::PipelineSchedules do
|
||||||
|
set(:developer) { create(:user) }
|
||||||
|
set(:user) { create(:user) }
|
||||||
|
set(:project) { create(:project) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_developer(developer)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /projects/:id/pipeline_schedules' do
|
||||||
|
context 'authenticated user with valid permissions' do
|
||||||
|
let(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: developer) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
pipeline_schedule.pipelines << build(:ci_pipeline, project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns list of pipeline_schedules' do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules", developer)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(response).to include_pagination_headers
|
||||||
|
expect(response).to match_response_schema('pipeline_schedules')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'avoids N + 1 queries' do
|
||||||
|
control_count = ActiveRecord::QueryRecorder.new do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules", developer)
|
||||||
|
end.count
|
||||||
|
|
||||||
|
create_list(:ci_pipeline_schedule, 10, project: project)
|
||||||
|
.each do |pipeline_schedule|
|
||||||
|
create(:user).tap do |user|
|
||||||
|
project.add_developer(user)
|
||||||
|
pipeline_schedule.update_attributes(owner: user)
|
||||||
|
end
|
||||||
|
pipeline_schedule.pipelines << build(:ci_pipeline, project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules", developer)
|
||||||
|
end.not_to exceed_query_limit(control_count)
|
||||||
|
end
|
||||||
|
|
||||||
|
%w[active inactive].each do |target|
|
||||||
|
context "when scope is #{target}" do
|
||||||
|
before do
|
||||||
|
create(:ci_pipeline_schedule, project: project, active: active?(target))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns matched pipeline schedules' do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules", developer), scope: target
|
||||||
|
|
||||||
|
expect(json_response.map{ |r| r['active'] }).to all(eq(active?(target)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def active?(str)
|
||||||
|
(str == 'active') ? true : false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with invalid permissions' do
|
||||||
|
it 'does not return pipeline_schedules list' do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules", user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'unauthenticated user' do
|
||||||
|
it 'does not return pipeline_schedules list' do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules")
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
|
||||||
|
let(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: developer) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
pipeline_schedule.pipelines << build(:ci_pipeline, project: project)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with valid permissions' do
|
||||||
|
it 'returns pipeline_schedule details' do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(response).to match_response_schema('pipeline_schedule')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'responds with 404 Not Found if requesting non-existing pipeline_schedule' do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules/-5", developer)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with invalid permissions' do
|
||||||
|
it 'does not return pipeline_schedules list' do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'unauthenticated user' do
|
||||||
|
it 'does not return pipeline_schedules list' do
|
||||||
|
get api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}")
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST /projects/:id/pipeline_schedules' do
|
||||||
|
let(:params) { attributes_for(:ci_pipeline_schedule) }
|
||||||
|
|
||||||
|
context 'authenticated user with valid permissions' do
|
||||||
|
context 'with required parameters' do
|
||||||
|
it 'creates pipeline_schedule' do
|
||||||
|
expect do
|
||||||
|
post api("/projects/#{project.id}/pipeline_schedules", developer),
|
||||||
|
params
|
||||||
|
end.to change { project.pipeline_schedules.count }.by(1)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:created)
|
||||||
|
expect(response).to match_response_schema('pipeline_schedule')
|
||||||
|
expect(json_response['description']).to eq(params[:description])
|
||||||
|
expect(json_response['ref']).to eq(params[:ref])
|
||||||
|
expect(json_response['cron']).to eq(params[:cron])
|
||||||
|
expect(json_response['cron_timezone']).to eq(params[:cron_timezone])
|
||||||
|
expect(json_response['owner']['id']).to eq(developer.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without required parameters' do
|
||||||
|
it 'does not create pipeline_schedule' do
|
||||||
|
post api("/projects/#{project.id}/pipeline_schedules", developer)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:bad_request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when cron has validation error' do
|
||||||
|
it 'does not create pipeline_schedule' do
|
||||||
|
post api("/projects/#{project.id}/pipeline_schedules", developer),
|
||||||
|
params.merge('cron' => 'invalid-cron')
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:bad_request)
|
||||||
|
expect(json_response['message']).to have_key('cron')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with invalid permissions' do
|
||||||
|
it 'does not create pipeline_schedule' do
|
||||||
|
post api("/projects/#{project.id}/pipeline_schedules", user), params
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'unauthenticated user' do
|
||||||
|
it 'does not create pipeline_schedule' do
|
||||||
|
post api("/projects/#{project.id}/pipeline_schedules"), params
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
|
||||||
|
let(:pipeline_schedule) do
|
||||||
|
create(:ci_pipeline_schedule, project: project, owner: developer)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with valid permissions' do
|
||||||
|
it 'updates cron' do
|
||||||
|
put api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer),
|
||||||
|
cron: '1 2 3 4 *'
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(response).to match_response_schema('pipeline_schedule')
|
||||||
|
expect(json_response['cron']).to eq('1 2 3 4 *')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when cron has validation error' do
|
||||||
|
it 'does not update pipeline_schedule' do
|
||||||
|
put api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer),
|
||||||
|
cron: 'invalid-cron'
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:bad_request)
|
||||||
|
expect(json_response['message']).to have_key('cron')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with invalid permissions' do
|
||||||
|
it 'does not update pipeline_schedule' do
|
||||||
|
put api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'unauthenticated user' do
|
||||||
|
it 'does not update pipeline_schedule' do
|
||||||
|
put api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}")
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST /projects/:id/pipeline_schedules/:pipeline_schedule_id/take_ownership' do
|
||||||
|
let(:pipeline_schedule) do
|
||||||
|
create(:ci_pipeline_schedule, project: project, owner: developer)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with valid permissions' do
|
||||||
|
it 'updates owner' do
|
||||||
|
post api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/take_ownership", developer)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:created)
|
||||||
|
expect(response).to match_response_schema('pipeline_schedule')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with invalid permissions' do
|
||||||
|
it 'does not update owner' do
|
||||||
|
post api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/take_ownership", user)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'unauthenticated user' do
|
||||||
|
it 'does not update owner' do
|
||||||
|
post api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}/take_ownership")
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'DELETE /projects/:id/pipeline_schedules/:pipeline_schedule_id' do
|
||||||
|
let(:master) { create(:user) }
|
||||||
|
|
||||||
|
let!(:pipeline_schedule) do
|
||||||
|
create(:ci_pipeline_schedule, project: project, owner: developer)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_master(master)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with valid permissions' do
|
||||||
|
it 'deletes pipeline_schedule' do
|
||||||
|
expect do
|
||||||
|
delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", master)
|
||||||
|
end.to change { project.pipeline_schedules.count }.by(-1)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:accepted)
|
||||||
|
expect(response).to match_response_schema('pipeline_schedule')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'responds with 404 Not Found if requesting non-existing pipeline_schedule' do
|
||||||
|
delete api("/projects/#{project.id}/pipeline_schedules/-5", master)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated user with invalid permissions' do
|
||||||
|
it 'does not delete pipeline_schedule' do
|
||||||
|
delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}", developer)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:forbidden)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'unauthenticated user' do
|
||||||
|
it 'does not delete pipeline_schedule' do
|
||||||
|
delete api("/projects/#{project.id}/pipeline_schedules/#{pipeline_schedule.id}")
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue