Commit 140b51ce authored by Kamil Trzcinski's avatar Kamil Trzcinski

Introduce tests for pipeline triggers

parent ab972295
module TriggersHelper module TriggersHelper
def builds_trigger_url(project_id, ref: nil) def builds_trigger_url(project_id, ref: nil)
if ref.nil? if ref.nil?
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/trigger/builds" "#{Settings.gitlab.url}/api/v4/projects/#{project_id}/trigger/pipeline"
else else
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/ref/#{ref}/trigger/builds" "#{Settings.gitlab.url}/api/v4/projects/#{project_id}/ref/#{ref}/trigger/pipeline"
end end
end end
......
---
title: Introduce Pipeline Triggers that are user-aware
merge_request:
author:
...@@ -36,7 +36,7 @@ it will not trigger a job. ...@@ -36,7 +36,7 @@ it will not trigger a job.
To trigger a job you need to send a `POST` request to GitLab's API endpoint: To trigger a job you need to send a `POST` request to GitLab's API endpoint:
``` ```
POST /projects/:id/trigger/builds POST /projects/:id/trigger/pipeline
``` ```
The required parameters are the trigger's `token` and the Git `ref` on which The required parameters are the trigger's `token` and the Git `ref` on which
...@@ -71,7 +71,7 @@ To trigger a job from webhook of another project you need to add the following ...@@ -71,7 +71,7 @@ To trigger a job from webhook of another project you need to add the following
webhook url for Push and Tag push events: webhook url for Push and Tag push events:
``` ```
https://gitlab.example.com/api/v4/projects/:id/ref/:ref/trigger/builds?token=TOKEN https://gitlab.example.com/api/v4/projects/:id/ref/:ref/trigger/pipeline?token=TOKEN
``` ```
> **Note**: > **Note**:
...@@ -105,7 +105,7 @@ Using cURL you can trigger a rebuild with minimal effort, for example: ...@@ -105,7 +105,7 @@ Using cURL you can trigger a rebuild with minimal effort, for example:
curl --request POST \ curl --request POST \
--form token=TOKEN \ --form token=TOKEN \
--form ref=master \ --form ref=master \
https://gitlab.example.com/api/v4/projects/9/trigger/builds https://gitlab.example.com/api/v4/projects/9/trigger/pipeline
``` ```
In this case, the project with ID `9` will get rebuilt on `master` branch. In this case, the project with ID `9` will get rebuilt on `master` branch.
...@@ -114,7 +114,7 @@ Alternatively, you can pass the `token` and `ref` arguments in the query string: ...@@ -114,7 +114,7 @@ Alternatively, you can pass the `token` and `ref` arguments in the query string:
```bash ```bash
curl --request POST \ curl --request POST \
"https://gitlab.example.com/api/v4/projects/9/trigger/builds?token=TOKEN&ref=master" "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline?token=TOKEN&ref=master"
``` ```
### Triggering a job within `.gitlab-ci.yml` ### Triggering a job within `.gitlab-ci.yml`
...@@ -128,7 +128,7 @@ need to add in project's A `.gitlab-ci.yml`: ...@@ -128,7 +128,7 @@ need to add in project's A `.gitlab-ci.yml`:
build_docs: build_docs:
stage: deploy stage: deploy
script: script:
- "curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/builds" - "curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
only: only:
- tags - tags
``` ```
...@@ -187,7 +187,7 @@ curl --request POST \ ...@@ -187,7 +187,7 @@ curl --request POST \
--form token=TOKEN \ --form token=TOKEN \
--form ref=master \ --form ref=master \
--form "variables[UPLOAD_TO_S3]=true" \ --form "variables[UPLOAD_TO_S3]=true" \
https://gitlab.example.com/api/v4/projects/9/trigger/builds https://gitlab.example.com/api/v4/projects/9/trigger/pipeline
``` ```
### Using webhook to trigger job ### Using webhook to trigger job
...@@ -195,7 +195,7 @@ curl --request POST \ ...@@ -195,7 +195,7 @@ curl --request POST \
You can add the following webhook to another project in order to trigger a job: You can add the following webhook to another project in order to trigger a job:
``` ```
https://gitlab.example.com/api/v4/projects/9/ref/master/trigger/builds?token=TOKEN&variables[UPLOAD_TO_S3]=true https://gitlab.example.com/api/v4/projects/9/ref/master/trigger/pipeline?token=TOKEN&variables[UPLOAD_TO_S3]=true
``` ```
### Using cron to trigger nightly jobs ### Using cron to trigger nightly jobs
...@@ -205,7 +205,7 @@ in conjunction with cron. The example below triggers a job on the `master` ...@@ -205,7 +205,7 @@ in conjunction with cron. The example below triggers a job on the `master`
branch of project with ID `9` every night at `00:30`: branch of project with ID `9` every night at `00:30`:
```bash ```bash
30 0 * * * curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/builds 30 0 * * * curl --request POST --form token=TOKEN --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/pipeline
``` ```
[ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229 [ci-229]: https://gitlab.com/gitlab-org/gitlab-ci/merge_requests/229
...@@ -639,7 +639,9 @@ module API ...@@ -639,7 +639,9 @@ module API
end end
class Trigger < Grape::Entity class Trigger < Grape::Entity
expose :token, :created_at, :updated_at, :deleted_at, :last_used expose :token, :description
expose :created_at, :updated_at, :deleted_at, :last_used
expose :owner, using: Entities::UserBasic
end end
class Variable < Grape::Entity class Variable < Grape::Entity
......
...@@ -7,7 +7,7 @@ module API ...@@ -7,7 +7,7 @@ module API
end end
resource :projects do resource :projects do
desc 'Trigger a GitLab project pipeline' do desc 'Trigger a GitLab project pipeline' do
success Entities::TriggerRequest success Entities::Pipeline
end end
params do params do
requires :ref, type: String, desc: 'The commit sha or name of a branch or tag' requires :ref, type: String, desc: 'The commit sha or name of a branch or tag'
...@@ -31,7 +31,7 @@ module API ...@@ -31,7 +31,7 @@ module API
if trigger_request if trigger_request
present trigger_request.pipeline, with: Entities::Pipeline present trigger_request.pipeline, with: Entities::Pipeline
else else
errors = 'No pipeline create' errors = 'No pipeline created'
render_api_error!(errors, 400) render_api_error!(errors, 400)
end end
end end
...@@ -61,7 +61,7 @@ module API ...@@ -61,7 +61,7 @@ module API
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
trigger = user_project.triggers.find(params[:trigger_id]) trigger = user_project.triggers.find(params.delete(:trigger_id))
return not_found!('Trigger') unless trigger return not_found!('Trigger') unless trigger
present trigger, with: Entities::Trigger present trigger, with: Entities::Trigger
...@@ -94,15 +94,18 @@ module API ...@@ -94,15 +94,18 @@ module API
requires :trigger_id, type: Integer, desc: 'The trigger ID' requires :trigger_id, type: Integer, desc: 'The trigger ID'
optional :description, type: String, desc: 'The trigger description' optional :description, type: String, desc: 'The trigger description'
end end
delete ':id/triggers/:trigger_id' do put ':id/triggers/:trigger_id' do
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
trigger = user_project.triggers.find(params[:trigger_id]) trigger = user_project.triggers.find(params.delete(:trigger_id))
return not_found!('Trigger') unless trigger return not_found!('Trigger') unless trigger
trigger = trigger.update(declared_params(include_missing: false)) if trigger.update(declared_params(include_missing: false))
present trigger, with: Entities::Trigger present trigger, with: Entities::Trigger
else
render_validation_error!(trigger)
end
end end
desc 'Take ownership of trigger' do desc 'Take ownership of trigger' do
...@@ -115,10 +118,11 @@ module API ...@@ -115,10 +118,11 @@ module API
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
trigger = user_project.triggers.find(params[:trigger_id]) trigger = user_project.triggers.find(params.delete(:trigger_id))
return not_found!('Trigger') unless trigger return not_found!('Trigger') unless trigger
if trigger.update(owner: current_user) if trigger.update(owner: current_user)
status :ok
present trigger, with: Entities::Trigger present trigger, with: Entities::Trigger
else else
render_validation_error!(trigger) render_validation_error!(trigger)
...@@ -135,7 +139,7 @@ module API ...@@ -135,7 +139,7 @@ module API
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
trigger = user_project.triggers.find(params[:trigger_id]) trigger = user_project.triggers.find(params.delete(:trigger_id))
return not_found!('Trigger') unless trigger return not_found!('Trigger') unless trigger
trigger.destroy trigger.destroy
......
...@@ -14,7 +14,7 @@ describe API::Triggers do ...@@ -14,7 +14,7 @@ describe API::Triggers do
let!(:trigger2) { create(:ci_trigger, project: project, token: trigger_token_2) } let!(:trigger2) { create(:ci_trigger, project: project, token: trigger_token_2) }
let!(:trigger_request) { create(:ci_trigger_request, trigger: trigger, created_at: '2015-01-01 12:13:14') } let!(:trigger_request) { create(:ci_trigger_request, trigger: trigger, created_at: '2015-01-01 12:13:14') }
describe 'POST /projects/:project_id/trigger' do describe 'POST /projects/:project_id/trigger/pipeline' do
let!(:project2) { create(:project) } let!(:project2) { create(:project) }
let(:options) do let(:options) do
{ {
...@@ -28,17 +28,20 @@ describe API::Triggers do ...@@ -28,17 +28,20 @@ describe API::Triggers do
context 'Handles errors' do context 'Handles errors' do
it 'returns bad request if token is missing' do it 'returns bad request if token is missing' do
post api("/projects/#{project.id}/trigger/builds"), ref: 'master' post api("/projects/#{project.id}/trigger/pipeline"), ref: 'master'
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
end end
it 'returns not found if project is not found' do it 'returns not found if project is not found' do
post api('/projects/0/trigger/builds'), options.merge(ref: 'master') post api('/projects/0/trigger/pipeline'), options.merge(ref: 'master')
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
it 'returns unauthorized if token is for different project' do it 'returns unauthorized if token is for different project' do
post api("/projects/#{project2.id}/trigger/builds"), options.merge(ref: 'master') post api("/projects/#{project2.id}/trigger/pipeline"), options.merge(ref: 'master')
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
end end
end end
...@@ -46,9 +49,11 @@ describe API::Triggers do ...@@ -46,9 +49,11 @@ describe API::Triggers do
context 'Have a commit' do context 'Have a commit' do
let(:pipeline) { project.pipelines.last } let(:pipeline) { project.pipelines.last }
it 'creates builds' do it 'creates pipeline' do
post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'master') post api("/projects/#{project.id}/trigger/pipeline"), options.merge(ref: 'master')
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
expect(json_response).to include('id' => pipeline.id)
pipeline.builds.reload pipeline.builds.reload
expect(pipeline.builds.pending.size).to eq(2) expect(pipeline.builds.pending.size).to eq(2)
expect(pipeline.builds.size).to eq(5) expect(pipeline.builds.size).to eq(5)
...@@ -56,15 +61,17 @@ describe API::Triggers do ...@@ -56,15 +61,17 @@ describe API::Triggers do
it 'creates builds on webhook from other gitlab repository and branch' do it 'creates builds on webhook from other gitlab repository and branch' do
expect do expect do
post api("/projects/#{project.id}/ref/master/trigger/builds?token=#{trigger_token}"), { ref: 'refs/heads/other-branch' } post api("/projects/#{project.id}/ref/master/trigger/pipeline?token=#{trigger_token}"), { ref: 'refs/heads/other-branch' }
end.to change(project.builds, :count).by(5) end.to change(project.builds, :count).by(5)
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
end end
it 'returns bad request with no builds created if there\'s no commit for that ref' do it 'returns bad request with no pipeline created if there\'s no commit for that ref' do
post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch') post api("/projects/#{project.id}/trigger/pipeline"), options.merge(ref: 'other-branch')
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(json_response['message']).to eq('No builds created') expect(json_response['message']).to eq('No pipeline created')
end end
context 'Validates variables' do context 'Validates variables' do
...@@ -73,22 +80,24 @@ describe API::Triggers do ...@@ -73,22 +80,24 @@ describe API::Triggers do
end end
it 'validates variables to be a hash' do it 'validates variables to be a hash' do
post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: 'value', ref: 'master') post api("/projects/#{project.id}/trigger/pipeline"), options.merge(variables: 'value', ref: 'master')
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(json_response['error']).to eq('variables is invalid') expect(json_response['error']).to eq('variables is invalid')
end end
it 'validates variables needs to be a map of key-valued strings' do it 'validates variables needs to be a map of key-valued strings' do
post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: { key: %w(1 2) }, ref: 'master') post api("/projects/#{project.id}/trigger/pipeline"), options.merge(variables: { key: %w(1 2) }, ref: 'master')
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') expect(json_response['message']).to eq('variables needs to be a map of key-valued strings')
end end
it 'creates trigger request with variables' do it 'creates trigger request with variables' do
post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, ref: 'master') post api("/projects/#{project.id}/trigger/pipeline"), options.merge(variables: variables, ref: 'master')
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
pipeline.builds.reload expect(pipeline.builds.reload.first.trigger_request.variables).to eq(variables)
expect(pipeline.builds.first.trigger_request.variables).to eq(variables)
end end
end end
end end
...@@ -123,17 +132,17 @@ describe API::Triggers do ...@@ -123,17 +132,17 @@ describe API::Triggers do
end end
end end
describe 'GET /projects/:id/triggers/:token' do describe 'GET /projects/:id/triggers/:trigger_id' do
context 'authenticated user with valid permissions' do context 'authenticated user with valid permissions' do
it 'returns trigger details' do it 'returns trigger details' do
get api("/projects/#{project.id}/triggers/#{trigger.token}", user) get api("/projects/#{project.id}/triggers/#{trigger.id}", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response).to be_a(Hash) expect(json_response).to be_a(Hash)
end end
it 'responds with 404 Not Found if requesting non-existing trigger' do it 'responds with 404 Not Found if requesting non-existing trigger' do
get api("/projects/#{project.id}/triggers/abcdef012345", user) get api("/projects/#{project.id}/triggers/-5", user)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
...@@ -141,7 +150,7 @@ describe API::Triggers do ...@@ -141,7 +150,7 @@ describe API::Triggers do
context 'authenticated user with invalid permissions' do context 'authenticated user with invalid permissions' do
it 'does not return triggers list' do it 'does not return triggers list' do
get api("/projects/#{project.id}/triggers/#{trigger.token}", user2) get api("/projects/#{project.id}/triggers/#{trigger.id}", user2)
expect(response).to have_http_status(403) expect(response).to have_http_status(403)
end end
...@@ -149,7 +158,7 @@ describe API::Triggers do ...@@ -149,7 +158,7 @@ describe API::Triggers do
context 'unauthenticated user' do context 'unauthenticated user' do
it 'does not return triggers list' do it 'does not return triggers list' do
get api("/projects/#{project.id}/triggers/#{trigger.token}") get api("/projects/#{project.id}/triggers/#{trigger.id}")
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
end end
...@@ -158,19 +167,31 @@ describe API::Triggers do ...@@ -158,19 +167,31 @@ describe API::Triggers do
describe 'POST /projects/:id/triggers' do describe 'POST /projects/:id/triggers' do
context 'authenticated user with valid permissions' do context 'authenticated user with valid permissions' do
it 'creates trigger' do context 'with required parameters' do
expect do it 'creates trigger' do
expect do
post api("/projects/#{project.id}/triggers", user),
description: 'trigger'
end.to change{project.triggers.count}.by(1)
expect(response).to have_http_status(201)
expect(json_response).to include('description' => 'trigger')
end
end
context 'without required parameters' do
it 'creates trigger' do
post api("/projects/#{project.id}/triggers", user) post api("/projects/#{project.id}/triggers", user)
end.to change{project.triggers.count}.by(1)
expect(response).to have_http_status(201) expect(response).to have_http_status(:bad_request)
expect(json_response).to be_a(Hash) end
end end
end end
context 'authenticated user with invalid permissions' do context 'authenticated user with invalid permissions' do
it 'does not create trigger' do it 'does not create trigger' do
post api("/projects/#{project.id}/triggers", user2) post api("/projects/#{project.id}/triggers", user2),
description: 'trigger'
expect(response).to have_http_status(403) expect(response).to have_http_status(403)
end end
...@@ -178,25 +199,87 @@ describe API::Triggers do ...@@ -178,25 +199,87 @@ describe API::Triggers do
context 'unauthenticated user' do context 'unauthenticated user' do
it 'does not create trigger' do it 'does not create trigger' do
post api("/projects/#{project.id}/triggers") post api("/projects/#{project.id}/triggers"),
description: 'trigger'
expect(response).to have_http_status(401)
end
end
end
describe 'PUT /projects/:id/triggers/:trigger_id' do
context 'authenticated user with valid permissions' do
let(:new_description) { 'new description' }
it 'updates description' do
put api("/projects/#{project.id}/triggers/#{trigger.id}", user),
description: new_description
expect(response).to have_http_status(200)
expect(json_response).to include('description' => new_description)
expect(trigger.reload.description).to eq(new_description)
end
end
context 'authenticated user with invalid permissions' do
it 'does not update trigger' do
put api("/projects/#{project.id}/triggers/#{trigger.id}", user2)
expect(response).to have_http_status(403)
end
end
context 'unauthenticated user' do
it 'does not update trigger' do
put api("/projects/#{project.id}/triggers/#{trigger.id}")
expect(response).to have_http_status(401)
end
end
end
describe 'POST /projects/:id/triggers/:trigger_id/take' do
context 'authenticated user with valid permissions' do
it 'updates owner' do
expect(trigger.owner).to be_nil
post api("/projects/#{project.id}/triggers/#{trigger.id}/take", user)
expect(response).to have_http_status(200)
expect(json_response).to include('owner')
expect(trigger.reload.owner).to eq(user)
end
end
context 'authenticated user with invalid permissions' do
it 'does not update owner' do
post api("/projects/#{project.id}/triggers/#{trigger.id}/take", user2)
expect(response).to have_http_status(403)
end
end
context 'unauthenticated user' do
it 'does not update owner' do
post api("/projects/#{project.id}/triggers/#{trigger.id}/take")
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
end end
end end
end end
describe 'DELETE /projects/:id/triggers/:token' do describe 'DELETE /projects/:id/triggers/:trigger_id' do
context 'authenticated user with valid permissions' do context 'authenticated user with valid permissions' do
it 'deletes trigger' do it 'deletes trigger' do
expect do expect do
delete api("/projects/#{project.id}/triggers/#{trigger.token}", user) delete api("/projects/#{project.id}/triggers/#{trigger.id}", user)
expect(response).to have_http_status(204) expect(response).to have_http_status(204)
end.to change{project.triggers.count}.by(-1) end.to change{project.triggers.count}.by(-1)
end end
it 'responds with 404 Not Found if requesting non-existing trigger' do it 'responds with 404 Not Found if requesting non-existing trigger' do
delete api("/projects/#{project.id}/triggers/abcdef012345", user) delete api("/projects/#{project.id}/triggers/-5", user)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
...@@ -204,7 +287,7 @@ describe API::Triggers do ...@@ -204,7 +287,7 @@ describe API::Triggers do
context 'authenticated user with invalid permissions' do context 'authenticated user with invalid permissions' do
it 'does not delete trigger' do it 'does not delete trigger' do
delete api("/projects/#{project.id}/triggers/#{trigger.token}", user2) delete api("/projects/#{project.id}/triggers/#{trigger.id}", user2)
expect(response).to have_http_status(403) expect(response).to have_http_status(403)
end end
...@@ -212,7 +295,7 @@ describe API::Triggers do ...@@ -212,7 +295,7 @@ describe API::Triggers do
context 'unauthenticated user' do context 'unauthenticated user' do
it 'does not delete trigger' do it 'does not delete trigger' do
delete api("/projects/#{project.id}/triggers/#{trigger.token}") delete api("/projects/#{project.id}/triggers/#{trigger.id}")
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
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