Commit 348dff0a authored by Sean McGivern's avatar Sean McGivern

Merge branch 'introduce-pipeline-triggers' into 'master'

Improve pipeline triggers API

See merge request !9713
parents 05be66df 20feaa9e
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
......
...@@ -5,10 +5,11 @@ module Ci ...@@ -5,10 +5,11 @@ module Ci
acts_as_paranoid acts_as_paranoid
belongs_to :project, foreign_key: :gl_project_id belongs_to :project, foreign_key: :gl_project_id
belongs_to :owner, class_name: "User"
has_many :trigger_requests, dependent: :destroy has_many :trigger_requests, dependent: :destroy
validates :token, presence: true validates :token, presence: true, uniqueness: true
validates :token, uniqueness: true
before_validation :set_default_values before_validation :set_default_values
...@@ -25,7 +26,11 @@ module Ci ...@@ -25,7 +26,11 @@ module Ci
end end
def short_token def short_token
token[0...10] token[0...4]
end
def can_show_token?(user)
owner.blank? || owner == user
end end
end end
end end
...@@ -95,6 +95,7 @@ class User < ActiveRecord::Base ...@@ -95,6 +95,7 @@ class User < ActiveRecord::Base
has_many :todos, dependent: :destroy has_many :todos, dependent: :destroy
has_many :notification_settings, dependent: :destroy has_many :notification_settings, dependent: :destroy
has_many :award_emoji, dependent: :destroy has_many :award_emoji, dependent: :destroy
has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id
has_many :assigned_issues, dependent: :nullify, foreign_key: :assignee_id, class_name: "Issue" has_many :assigned_issues, dependent: :nullify, foreign_key: :assignee_id, class_name: "Issue"
has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest"
......
...@@ -3,7 +3,7 @@ module Ci ...@@ -3,7 +3,7 @@ module Ci
def execute(project, trigger, ref, variables = nil) def execute(project, trigger, ref, variables = nil)
trigger_request = trigger.trigger_requests.create(variables: variables) trigger_request = trigger.trigger_requests.create(variables: variables)
pipeline = Ci::CreatePipelineService.new(project, nil, ref: ref). pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: ref).
execute(ignore_skip_ci: true, trigger_request: trigger_request) execute(ignore_skip_ci: true, trigger_request: trigger_request)
if pipeline.persisted? if pipeline.persisted?
trigger_request trigger_request
......
---
title: Introduce Pipeline Triggers that are user-aware
merge_request:
author:
class AddOwnerIdToTriggers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :ci_triggers, :owner_id, :integer
end
end
class AddDescriptionToTriggers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :ci_triggers, :description, :string
end
end
class AddOwnerIdForeignKey < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def change
add_concurrent_foreign_key :ci_triggers, :users, column: :owner_id, on_delete: :cascade
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170217151947) do ActiveRecord::Schema.define(version: 20170305203726) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -111,7 +111,7 @@ ActiveRecord::Schema.define(version: 20170217151947) do ...@@ -111,7 +111,7 @@ ActiveRecord::Schema.define(version: 20170217151947) do
t.boolean "plantuml_enabled" t.boolean "plantuml_enabled"
t.integer "max_pages_size", default: 100, null: false t.integer "max_pages_size", default: 100, null: false
t.integer "terminal_max_session_time", default: 0, null: false t.integer "terminal_max_session_time", default: 0, null: false
t.string "default_artifacts_expire_in", default: '0', null: false t.string "default_artifacts_expire_in", default: "0", null: false
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
...@@ -377,6 +377,8 @@ ActiveRecord::Schema.define(version: 20170217151947) do ...@@ -377,6 +377,8 @@ ActiveRecord::Schema.define(version: 20170217151947) do
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.integer "gl_project_id" t.integer "gl_project_id"
t.integer "owner_id"
t.string "description"
end end
add_index "ci_triggers", ["gl_project_id"], name: "index_ci_triggers_on_gl_project_id", using: :btree add_index "ci_triggers", ["gl_project_id"], name: "index_ci_triggers_on_gl_project_id", using: :btree
...@@ -581,9 +583,9 @@ ActiveRecord::Schema.define(version: 20170217151947) do ...@@ -581,9 +583,9 @@ ActiveRecord::Schema.define(version: 20170217151947) do
end end
add_index "labels", ["group_id", "project_id", "title"], name: "index_labels_on_group_id_and_project_id_and_title", unique: true, using: :btree add_index "labels", ["group_id", "project_id", "title"], name: "index_labels_on_group_id_and_project_id_and_title", unique: true, using: :btree
add_index "labels", ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree
add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree
add_index "labels", ["title"], name: "index_labels_on_title", using: :btree add_index "labels", ["title"], name: "index_labels_on_title", using: :btree
add_index "labels", ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree
create_table "lfs_objects", force: :cascade do |t| create_table "lfs_objects", force: :cascade do |t|
t.string "oid", null: false t.string "oid", null: false
...@@ -1333,6 +1335,7 @@ ActiveRecord::Schema.define(version: 20170217151947) do ...@@ -1333,6 +1335,7 @@ ActiveRecord::Schema.define(version: 20170217151947) do
add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
add_foreign_key "boards", "projects" add_foreign_key "boards", "projects"
add_foreign_key "ci_triggers", "users", column: "owner_id", name: "fk_e8e10d1964", on_delete: :cascade
add_foreign_key "issue_metrics", "issues", on_delete: :cascade add_foreign_key "issue_metrics", "issues", on_delete: :cascade
add_foreign_key "label_priorities", "labels", on_delete: :cascade add_foreign_key "label_priorities", "labels", on_delete: :cascade
add_foreign_key "label_priorities", "projects", on_delete: :cascade add_foreign_key "label_priorities", "projects", on_delete: :cascade
......
...@@ -12,7 +12,6 @@ following locations: ...@@ -12,7 +12,6 @@ following locations:
- [Branches](branches.md) - [Branches](branches.md)
- [Broadcast Messages](broadcast_messages.md) - [Broadcast Messages](broadcast_messages.md)
- [Builds](builds.md) - [Builds](builds.md)
- [Build Triggers](build_triggers.md)
- [Build Variables](build_variables.md) - [Build Variables](build_variables.md)
- [Commits](commits.md) - [Commits](commits.md)
- [Deployments](deployments.md) - [Deployments](deployments.md)
...@@ -33,6 +32,7 @@ following locations: ...@@ -33,6 +32,7 @@ following locations:
- [Notes](notes.md) (comments) - [Notes](notes.md) (comments)
- [Notification settings](notification_settings.md) - [Notification settings](notification_settings.md)
- [Pipelines](pipelines.md) - [Pipelines](pipelines.md)
- [Pipeline Triggers](pipeline_triggers.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)
......
# Build triggers This document was moved to [Pipeline Triggers](pipeline_triggers.md).
You can read more about [triggering builds through the API](../ci/triggers/README.md).
## List project triggers
Get a list of project's build triggers.
```
GET /projects/:id/triggers
```
| Attribute | Type | required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers"
```
```json
[
{
"created_at": "2015-12-23T16:24:34.716Z",
"deleted_at": null,
"last_used": "2016-01-04T15:41:21.986Z",
"token": "fbdb730c2fbdb095a0862dbd8ab88b",
"updated_at": "2015-12-23T16:24:34.716Z"
},
{
"created_at": "2015-12-23T16:25:56.760Z",
"deleted_at": null,
"last_used": null,
"token": "7b9148c158980bbd9bcea92c17522d",
"updated_at": "2015-12-23T16:25:56.760Z"
}
]
```
## Get trigger details
Get details of project's build trigger.
```
GET /projects/:id/triggers/:token
```
| Attribute | Type | required | Description |
|-----------|---------|----------|--------------------------|
| `id` | integer | yes | The ID of a project |
| `token` | string | yes | The `token` of a trigger |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers/7b9148c158980bbd9bcea92c17522d"
```
```json
{
"created_at": "2015-12-23T16:25:56.760Z",
"deleted_at": null,
"last_used": null,
"token": "7b9148c158980bbd9bcea92c17522d",
"updated_at": "2015-12-23T16:25:56.760Z"
}
```
## Create a project trigger
Create a build trigger for a project.
```
POST /projects/:id/triggers
```
| Attribute | Type | required | Description |
|-----------|---------|----------|--------------------------|
| `id` | integer | yes | The ID of a project |
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers"
```
```json
{
"created_at": "2016-01-07T09:53:58.235Z",
"deleted_at": null,
"last_used": null,
"token": "6d056f63e50fe6f8c5f8f4aa10edb7",
"updated_at": "2016-01-07T09:53:58.235Z"
}
```
## Remove a project trigger
Remove a project's build trigger.
```
DELETE /projects/:id/triggers/:token
```
| Attribute | Type | required | Description |
|-----------|---------|----------|--------------------------|
| `id` | integer | yes | The ID of a project |
| `token` | string | yes | The `token` of a trigger |
```
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers/7b9148c158980bbd9bcea92c17522d"
```
# Pipeline triggers
You can read more about [triggering pipelines through the API](../ci/triggers/README.md).
## List project triggers
Get a list of project's build triggers.
```
GET /projects/:id/triggers
```
| Attribute | Type | required | Description |
|-----------|---------|----------|---------------------|
| `id` | integer | yes | The ID of a project |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers"
```
```json
[
{
"id": 10,
"description": "my trigger",
"created_at": "2016-01-07T09:53:58.235Z",
"deleted_at": null,
"last_used": null,
"token": "6d056f63e50fe6f8c5f8f4aa10edb7",
"updated_at": "2016-01-07T09:53:58.235Z",
"owner": null
}
]
```
## Get trigger details
Get details of project's build trigger.
```
GET /projects/:id/triggers/:trigger_id
```
| Attribute | Type | required | Description |
|-----------|---------|----------|--------------------------|
| `id` | integer | yes | The ID of a project |
| `token` | string | yes | The `token` of a trigger |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers/5"
```
```json
{
"id": 10,
"description": "my trigger",
"created_at": "2016-01-07T09:53:58.235Z",
"deleted_at": null,
"last_used": null,
"token": "6d056f63e50fe6f8c5f8f4aa10edb7",
"updated_at": "2016-01-07T09:53:58.235Z",
"owner": null
}
```
## Create a project trigger
Create a trigger for a project.
```
POST /projects/:id/triggers
```
| Attribute | Type | required | Description |
|---------------|---------|----------|--------------------------|
| `id` | integer | yes | The ID of a project |
| `description` | string | yes | The trigger name |
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form description="my description" "https://gitlab.example.com/api/v4/projects/1/triggers"
```
```json
{
"id": 10,
"description": "my trigger",
"created_at": "2016-01-07T09:53:58.235Z",
"deleted_at": null,
"last_used": null,
"token": "6d056f63e50fe6f8c5f8f4aa10edb7",
"updated_at": "2016-01-07T09:53:58.235Z",
"owner": null
}
```
## Update a project trigger
Update a trigger for a project.
```
PUT /projects/:id/triggers/:trigger_id
```
| Attribute | Type | required | Description |
|---------------|---------|----------|--------------------------|
| `trigger_id` | integer | yes | The trigger id |
| `description` | string | no | The trigger name |
```
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form description="my description" "https://gitlab.example.com/api/v4/projects/1/triggers/10"
```
```json
{
"id": 10,
"description": "my trigger",
"created_at": "2016-01-07T09:53:58.235Z",
"deleted_at": null,
"last_used": null,
"token": "6d056f63e50fe6f8c5f8f4aa10edb7",
"updated_at": "2016-01-07T09:53:58.235Z",
"owner": null
}
```
## Take ownership of a project trigger
Update an owner of a project trigger.
```
POST /projects/:id/triggers/:trigger_id/take_ownership
```
| Attribute | Type | required | Description |
|---------------|---------|----------|--------------------------|
| `trigger_id` | integer | yes | The trigger id |
```
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers/10/take_ownership"
```
```json
{
"id": 10,
"description": "my trigger",
"created_at": "2016-01-07T09:53:58.235Z",
"deleted_at": null,
"last_used": null,
"token": "6d056f63e50fe6f8c5f8f4aa10edb7",
"updated_at": "2016-01-07T09:53:58.235Z",
"owner": null
}
```
## Remove a project trigger
Remove a project's build trigger.
```
DELETE /projects/:id/triggers/:trigger_id
```
| Attribute | Type | required | Description |
|----------------|---------|----------|--------------------------|
| `id` | integer | yes | The ID of a project |
| `trigger_id` | integer | yes | The trigger id |
```
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers/5"
```
...@@ -59,3 +59,6 @@ changes are in V4: ...@@ -59,3 +59,6 @@ changes are in V4:
- Return 202 with JSON body on async removals on V4 API (DELETE `/projects/:id/repository/merged_branches` and DELETE `/projects/:id`) [!9449](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9449) - Return 202 with JSON body on async removals on V4 API (DELETE `/projects/:id/repository/merged_branches` and DELETE `/projects/:id`) [!9449](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9449)
- `projects/:id/milestones?iid[]=x&iid[]=y` array filter has been renamed to `iids` [!9096](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9096) - `projects/:id/milestones?iid[]=x&iid[]=y` array filter has been renamed to `iids` [!9096](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9096)
- Return basic info about pipeline in `GET /projects/:id/pipelines` [!8875](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8875) - Return basic info about pipeline in `GET /projects/:id/pipelines` [!8875](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8875)
- 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`
...@@ -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
...@@ -592,10 +592,6 @@ module API ...@@ -592,10 +592,6 @@ module API
end end
end end
class TriggerRequest < Grape::Entity
expose :id, :variables
end
class Runner < Grape::Entity class Runner < Grape::Entity
expose :id expose :id
expose :description expose :description
...@@ -643,7 +639,10 @@ module API ...@@ -643,7 +639,10 @@ module API
end end
class Trigger < Grape::Entity class Trigger < Grape::Entity
expose :token, :created_at, :updated_at, :deleted_at, :last_used expose :id
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
......
...@@ -6,15 +6,15 @@ module API ...@@ -6,15 +6,15 @@ module API
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects do resource :projects do
desc 'Trigger a GitLab project build' 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'
requires :token, type: String, desc: 'The unique token of trigger' requires :token, type: String, desc: 'The unique token of trigger'
optional :variables, type: Hash, desc: 'The list of variables to be injected into build' optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end end
post ":id/(ref/:ref/)trigger/builds" do post ":id/(ref/:ref/)trigger/pipeline" do
project = find_project(params[:id]) project = find_project(params[:id])
trigger = Ci::Trigger.find_by_token(params[:token].to_s) trigger = Ci::Trigger.find_by_token(params[:token].to_s)
not_found! unless project && trigger not_found! unless project && trigger
...@@ -29,9 +29,9 @@ module API ...@@ -29,9 +29,9 @@ module API
# create request and trigger builds # create request and trigger builds
trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables) trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables)
if trigger_request if trigger_request
present trigger_request, with: Entities::TriggerRequest present trigger_request.pipeline, with: Entities::Pipeline
else else
errors = 'No builds created' errors = 'No pipeline created'
render_api_error!(errors, 400) render_api_error!(errors, 400)
end end
end end
...@@ -55,13 +55,13 @@ module API ...@@ -55,13 +55,13 @@ module API
success Entities::Trigger success Entities::Trigger
end end
params do params do
requires :token, type: String, desc: 'The unique token of trigger' requires :trigger_id, type: Integer, desc: 'The trigger ID'
end end
get ':id/triggers/:token' do get ':id/triggers/:trigger_id' do
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
trigger = user_project.triggers.find_by(token: params[:token].to_s) 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
...@@ -70,26 +70,76 @@ module API ...@@ -70,26 +70,76 @@ module API
desc 'Create a trigger' do desc 'Create a trigger' do
success Entities::Trigger success Entities::Trigger
end end
params do
requires :description, type: String, desc: 'The trigger description'
end
post ':id/triggers' do post ':id/triggers' do
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
trigger = user_project.triggers.create trigger = user_project.triggers.create(
declared_params(include_missing: false).merge(owner: current_user))
present trigger, with: Entities::Trigger if trigger.valid?
present trigger, with: Entities::Trigger
else
render_validation_error!(trigger)
end
end
desc 'Update a trigger' do
success Entities::Trigger
end
params do
requires :trigger_id, type: Integer, desc: 'The trigger ID'
optional :description, type: String, desc: 'The trigger description'
end
put ':id/triggers/:trigger_id' do
authenticate!
authorize! :admin_build, user_project
trigger = user_project.triggers.find(params.delete(:trigger_id))
return not_found!('Trigger') unless trigger
if trigger.update(declared_params(include_missing: false))
present trigger, with: Entities::Trigger
else
render_validation_error!(trigger)
end
end
desc 'Take ownership of trigger' do
success Entities::Trigger
end
params do
requires :trigger_id, type: Integer, desc: 'The trigger ID'
end
post ':id/triggers/:trigger_id/take_ownership' do
authenticate!
authorize! :admin_build, user_project
trigger = user_project.triggers.find(params.delete(:trigger_id))
return not_found!('Trigger') unless trigger
if trigger.update(owner: current_user)
status :ok
present trigger, with: Entities::Trigger
else
render_validation_error!(trigger)
end
end end
desc 'Delete a trigger' do desc 'Delete a trigger' do
success Entities::Trigger success Entities::Trigger
end end
params do params do
requires :token, type: String, desc: 'The unique token of trigger' requires :trigger_id, type: Integer, desc: 'The trigger ID'
end end
delete ':id/triggers/:token' do delete ':id/triggers/:trigger_id' do
authenticate! authenticate!
authorize! :admin_build, user_project authorize! :admin_build, user_project
trigger = user_project.triggers.find_by(token: params[:token].to_s) 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
......
...@@ -186,6 +186,15 @@ module API ...@@ -186,6 +186,15 @@ module API
class Environment < ::API::Entities::EnvironmentBasic class Environment < ::API::Entities::EnvironmentBasic
expose :project, using: Entities::Project expose :project, using: Entities::Project
end end
class Trigger < Grape::Entity
expose :token, :created_at, :updated_at, :deleted_at, :last_used
expose :owner, using: ::API::Entities::UserBasic
end
class TriggerRequest < Grape::Entity
expose :id, :variables
end
end end
end end
end end
...@@ -7,8 +7,81 @@ module API ...@@ -7,8 +7,81 @@ module API
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects do resource :projects do
desc 'Trigger a GitLab project build' do
success ::API::V3::Entities::TriggerRequest
end
params do
requires :ref, type: String, desc: 'The commit sha or name of a branch or tag'
requires :token, type: String, desc: 'The unique token of trigger'
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end
post ":id/(ref/:ref/)trigger/builds" do
project = find_project(params[:id])
trigger = Ci::Trigger.find_by_token(params[:token].to_s)
not_found! unless project && trigger
unauthorized! unless trigger.project == project
# validate variables
variables = params[:variables].to_h
unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) }
render_api_error!('variables needs to be a map of key-valued strings', 400)
end
# create request and trigger builds
trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables)
if trigger_request
present trigger_request, with: ::API::V3::Entities::TriggerRequest
else
errors = 'No builds created'
render_api_error!(errors, 400)
end
end
desc 'Get triggers list' do
success ::API::V3::Entities::Trigger
end
params do
use :pagination
end
get ':id/triggers' do
authenticate!
authorize! :admin_build, user_project
triggers = user_project.triggers.includes(:trigger_requests)
present paginate(triggers), with: ::API::V3::Entities::Trigger
end
desc 'Get specific trigger of a project' do
success ::API::V3::Entities::Trigger
end
params do
requires :token, type: String, desc: 'The unique token of trigger'
end
get ':id/triggers/:token' do
authenticate!
authorize! :admin_build, user_project
trigger = user_project.triggers.find_by(token: params[:token].to_s)
return not_found!('Trigger') unless trigger
present trigger, with: ::API::V3::Entities::Trigger
end
desc 'Create a trigger' do
success ::API::V3::Entities::Trigger
end
post ':id/triggers' do
authenticate!
authorize! :admin_build, user_project
trigger = user_project.triggers.create
present trigger, with: ::API::V3::Entities::Trigger
end
desc 'Delete a trigger' do desc 'Delete a trigger' do
success ::API::Entities::Trigger success ::API::V3::Entities::Trigger
end end
params do params do
requires :token, type: String, desc: 'The unique token of trigger' requires :token, type: String, desc: 'The unique token of trigger'
...@@ -22,7 +95,7 @@ module API ...@@ -22,7 +95,7 @@ module API
trigger.destroy trigger.destroy
present trigger, with: ::API::Entities::Trigger present trigger, with: ::API::V3::Entities::Trigger
end end
end end
end end
......
...@@ -97,6 +97,7 @@ variables: ...@@ -97,6 +97,7 @@ variables:
triggers: triggers:
- project - project
- trigger_requests - trigger_requests
- owner
deploy_keys: deploy_keys:
- user - user
- deploy_keys_projects - deploy_keys_projects
......
...@@ -240,6 +240,8 @@ Ci::Trigger: ...@@ -240,6 +240,8 @@ Ci::Trigger:
- created_at - created_at
- updated_at - updated_at
- gl_project_id - gl_project_id
- owner_id
- description
DeployKey: DeployKey:
- id - id
- user_id - user_id
......
require 'spec_helper' require 'spec_helper'
describe Ci::Trigger, models: true do describe Ci::Trigger, models: true do
let(:project) { FactoryGirl.create :empty_project } let(:project) { create :empty_project }
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:owner) }
it { is_expected.to have_many(:trigger_requests) }
end
describe 'before_validation' do describe 'before_validation' do
it 'sets an random token if none provided' do it 'sets an random token if none provided' do
trigger = FactoryGirl.create :ci_trigger_without_token, project: project trigger = create(:ci_trigger_without_token, project: project)
expect(trigger.token).not_to be_nil expect(trigger.token).not_to be_nil
end end
it 'does not set an random token if one provided' do it 'does not set an random token if one provided' do
trigger = FactoryGirl.create :ci_trigger, project: project trigger = create(:ci_trigger, project: project)
expect(trigger.token).to eq('token') expect(trigger.token).to eq('token')
end end
end end
......
...@@ -32,6 +32,7 @@ describe User, models: true do ...@@ -32,6 +32,7 @@ describe User, models: true do
it { is_expected.to have_many(:spam_logs).dependent(:destroy) } it { is_expected.to have_many(:spam_logs).dependent(:destroy) }
it { is_expected.to have_many(:todos).dependent(:destroy) } it { is_expected.to have_many(:todos).dependent(:destroy) }
it { is_expected.to have_many(:award_emoji).dependent(:destroy) } it { is_expected.to have_many(:award_emoji).dependent(:destroy) }
it { is_expected.to have_many(:triggers).dependent(:destroy) }
it { is_expected.to have_many(:builds).dependent(:nullify) } it { is_expected.to have_many(:builds).dependent(:nullify) }
it { is_expected.to have_many(:pipelines).dependent(:nullify) } it { is_expected.to have_many(:pipelines).dependent(:nullify) }
it { is_expected.to have_many(:chat_names).dependent(:destroy) } it { is_expected.to have_many(:chat_names).dependent(: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 'does not create 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_ownership' 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_ownership", 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_ownership", 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_ownership")
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
......
...@@ -11,6 +11,177 @@ describe API::V3::Triggers do ...@@ -11,6 +11,177 @@ describe API::V3::Triggers do
let!(:developer) { create(:project_member, :developer, user: user2, project: project) } let!(:developer) { create(:project_member, :developer, user: user2, project: project) }
let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) } let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) }
describe 'POST /projects/:project_id/trigger' do
let!(:project2) { create(:project) }
let(:options) do
{
token: trigger_token
}
end
before do
stub_ci_pipeline_to_return_yaml_file
end
context 'Handles errors' do
it 'returns bad request if token is missing' do
post v3_api("/projects/#{project.id}/trigger/builds"), ref: 'master'
expect(response).to have_http_status(400)
end
it 'returns not found if project is not found' do
post v3_api('/projects/0/trigger/builds'), options.merge(ref: 'master')
expect(response).to have_http_status(404)
end
it 'returns unauthorized if token is for different project' do
post v3_api("/projects/#{project2.id}/trigger/builds"), options.merge(ref: 'master')
expect(response).to have_http_status(401)
end
end
context 'Have a commit' do
let(:pipeline) { project.pipelines.last }
it 'creates builds' do
post v3_api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'master')
expect(response).to have_http_status(201)
pipeline.builds.reload
expect(pipeline.builds.pending.size).to eq(2)
expect(pipeline.builds.size).to eq(5)
end
it 'creates builds on webhook from other gitlab repository and branch' do
expect do
post v3_api("/projects/#{project.id}/ref/master/trigger/builds?token=#{trigger_token}"), { ref: 'refs/heads/other-branch' }
end.to change(project.builds, :count).by(5)
expect(response).to have_http_status(201)
end
it 'returns bad request with no builds created if there\'s no commit for that ref' do
post v3_api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch')
expect(response).to have_http_status(400)
expect(json_response['message']).to eq('No builds created')
end
context 'Validates variables' do
let(:variables) do
{ 'TRIGGER_KEY' => 'TRIGGER_VALUE' }
end
it 'validates variables to be a hash' do
post v3_api("/projects/#{project.id}/trigger/builds"), options.merge(variables: 'value', ref: 'master')
expect(response).to have_http_status(400)
expect(json_response['error']).to eq('variables is invalid')
end
it 'validates variables needs to be a map of key-valued strings' do
post v3_api("/projects/#{project.id}/trigger/builds"), options.merge(variables: { key: %w(1 2) }, ref: 'master')
expect(response).to have_http_status(400)
expect(json_response['message']).to eq('variables needs to be a map of key-valued strings')
end
it 'creates trigger request with variables' do
post v3_api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, ref: 'master')
expect(response).to have_http_status(201)
pipeline.builds.reload
expect(pipeline.builds.first.trigger_request.variables).to eq(variables)
end
end
end
end
describe 'GET /projects/:id/triggers' do
context 'authenticated user with valid permissions' do
it 'returns list of triggers' do
get v3_api("/projects/#{project.id}/triggers", user)
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_a(Array)
expect(json_response[0]).to have_key('token')
end
end
context 'authenticated user with invalid permissions' do
it 'does not return triggers list' do
get v3_api("/projects/#{project.id}/triggers", user2)
expect(response).to have_http_status(403)
end
end
context 'unauthenticated user' do
it 'does not return triggers list' do
get v3_api("/projects/#{project.id}/triggers")
expect(response).to have_http_status(401)
end
end
end
describe 'GET /projects/:id/triggers/:token' do
context 'authenticated user with valid permissions' do
it 'returns trigger details' do
get v3_api("/projects/#{project.id}/triggers/#{trigger.token}", user)
expect(response).to have_http_status(200)
expect(json_response).to be_a(Hash)
end
it 'responds with 404 Not Found if requesting non-existing trigger' do
get v3_api("/projects/#{project.id}/triggers/abcdef012345", user)
expect(response).to have_http_status(404)
end
end
context 'authenticated user with invalid permissions' do
it 'does not return triggers list' do
get v3_api("/projects/#{project.id}/triggers/#{trigger.token}", user2)
expect(response).to have_http_status(403)
end
end
context 'unauthenticated user' do
it 'does not return triggers list' do
get v3_api("/projects/#{project.id}/triggers/#{trigger.token}")
expect(response).to have_http_status(401)
end
end
end
describe 'POST /projects/:id/triggers' do
context 'authenticated user with valid permissions' do
it 'creates trigger' do
expect do
post v3_api("/projects/#{project.id}/triggers", user)
end.to change{project.triggers.count}.by(1)
expect(response).to have_http_status(201)
expect(json_response).to be_a(Hash)
end
end
context 'authenticated user with invalid permissions' do
it 'does not create trigger' do
post v3_api("/projects/#{project.id}/triggers", user2)
expect(response).to have_http_status(403)
end
end
context 'unauthenticated user' do
it 'does not create trigger' do
post v3_api("/projects/#{project.id}/triggers")
expect(response).to have_http_status(401)
end
end
end
describe 'DELETE /projects/:id/triggers/:token' do describe 'DELETE /projects/:id/triggers/:token' do
context 'authenticated user with valid permissions' do context 'authenticated user with valid permissions' do
it 'deletes trigger' do it 'deletes trigger' do
......
...@@ -13,8 +13,22 @@ describe Ci::CreateTriggerRequestService, services: true do ...@@ -13,8 +13,22 @@ describe Ci::CreateTriggerRequestService, services: true do
context 'valid params' do context 'valid params' do
subject { service.execute(project, trigger, 'master') } subject { service.execute(project, trigger, 'master') }
it { expect(subject).to be_kind_of(Ci::TriggerRequest) } context 'without owner' do
it { expect(subject.builds.first).to be_kind_of(Ci::Build) } it { expect(subject).to be_kind_of(Ci::TriggerRequest) }
it { expect(subject.pipeline).to be_kind_of(Ci::Pipeline) }
it { expect(subject.builds.first).to be_kind_of(Ci::Build) }
end
context 'with owner' do
let(:owner) { create(:user) }
let(:trigger) { create(:ci_trigger, project: project, owner: owner) }
it { expect(subject).to be_kind_of(Ci::TriggerRequest) }
it { expect(subject.pipeline).to be_kind_of(Ci::Pipeline) }
it { expect(subject.pipeline.user).to eq(owner) }
it { expect(subject.builds.first).to be_kind_of(Ci::Build) }
it { expect(subject.builds.first.user).to eq(owner) }
end
end end
context 'no commit for ref' do context 'no commit for ref' do
......
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