Commit bcf2d2c2 authored by Alishan Ladhani's avatar Alishan Ladhani

Add approvals and pending_approval_count to deployments API

parent 53314815
...@@ -23,7 +23,7 @@ GET /projects/:id/deployments ...@@ -23,7 +23,7 @@ GET /projects/:id/deployments
| `updated_after` | datetime | no | Return deployments updated after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `updated_after` | datetime | no | Return deployments updated after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `updated_before` | datetime | no | Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `updated_before` | datetime | no | Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `environment` | string | no | The [name of the environment](../ci/environments/index.md) to filter deployments by. | | `environment` | string | no | The [name of the environment](../ci/environments/index.md) to filter deployments by. |
| `status` | string | no | The status to filter deployments by. One of `created`, `running`, `success`, `failed`, `canceled`. | `status` | string | no | The status to filter deployments by. One of `created`, `running`, `success`, `failed`, `canceled`, `blocked`.
```shell ```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments" curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments"
...@@ -201,6 +201,7 @@ Example response: ...@@ -201,6 +201,7 @@ Example response:
"sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a", "sha": "a91957a858320c0e17f3a0eca7cfacbff50ea29a",
"created_at": "2016-08-11T11:32:35.444Z", "created_at": "2016-08-11T11:32:35.444Z",
"updated_at": "2016-08-11T11:34:01.123Z", "updated_at": "2016-08-11T11:34:01.123Z",
"status": "success",
"user": { "user": {
"name": "Administrator", "name": "Administrator",
"username": "root", "username": "root",
...@@ -264,6 +265,29 @@ Example response: ...@@ -264,6 +265,29 @@ Example response:
} }
``` ```
Deployments created by users on GitLab Premium or higher include the `approvals` and `pending_approval_count` properties:
```json
{
"status": "created",
"pending_approval_count": 0,
"approvals": [
{
"user": {
"id": 49,
"username": "project_6_bot",
"name": "****",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/e83ac685f68ea07553ad3054c738c709?s=80&d=identicon",
"web_url": "http://localhost:3000/project_6_bot"
},
"status": "approved"
}
],
...
}
```
## Create a deployment ## Create a deployment
```plaintext ```plaintext
...@@ -311,6 +335,29 @@ Example response: ...@@ -311,6 +335,29 @@ Example response:
} }
``` ```
Deployments created by users on GitLab Premium or higher include the `approvals` and `pending_approval_count` properties:
```json
{
"status": "created",
"pending_approval_count": 0,
"approvals": [
{
"user": {
"id": 49,
"username": "project_6_bot",
"name": "****",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/e83ac685f68ea07553ad3054c738c709?s=80&d=identicon",
"web_url": "http://localhost:3000/project_6_bot"
},
"status": "approved"
}
],
...
}
```
## Update a deployment ## Update a deployment
```plaintext ```plaintext
...@@ -354,6 +401,29 @@ Example response: ...@@ -354,6 +401,29 @@ Example response:
} }
``` ```
Deployments created by users on GitLab Premium or higher include the `approvals` and `pending_approval_count` properties:
```json
{
"status": "created",
"pending_approval_count": 0,
"approvals": [
{
"user": {
"id": 49,
"username": "project_6_bot",
"name": "****",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/e83ac685f68ea07553ad3054c738c709?s=80&d=identicon",
"web_url": "http://localhost:3000/project_6_bot"
},
"status": "approved"
}
],
...
}
```
## List of merge requests associated with a deployment ## List of merge requests associated with a deployment
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35739) in GitLab 12.7. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35739) in GitLab 12.7.
......
...@@ -95,7 +95,11 @@ curl --data "status=approved" \ ...@@ -95,7 +95,11 @@ curl --data "status=approved" \
#### Using the API #### Using the API
Use the [Deployments API](../../api/deployments.md) to see deployments. The `status` field indicates if a deployment is blocked. Use the [Deployments API](../../api/deployments.md) to see deployments.
- The `status` field indicates if a deployment is blocked.
- The `pending_approval_count` field indicates how many approvals are remaining to run a deployment.
- The `approvals` field contains the deployment's approvals.
## Related features ## Related features
......
...@@ -32,7 +32,9 @@ module EE ...@@ -32,7 +32,9 @@ module EE
end end
def pending_approval_count def pending_approval_count
environment.required_approval_count - approvals.approved.count return 0 unless blocked?
environment.required_approval_count - approvals.count
end end
end end
end end
# frozen_string_literal: true
module EE
module API
module Entities
module DeploymentExtended
extend ActiveSupport::Concern
prepended do
expose :pending_approval_count
expose :approvals, using: ::API::Entities::Deployments::Approval
end
end
end
end
end
{
"type": "object",
"allOf": [
{
"$ref": "../../../../../../../spec/fixtures/api/schemas/public_api/v4/deployment.json"
},
{
"required": [
"pending_approval_count",
"approvals"
],
"properties": {
"pending_approval_count": {
"type": "integer"
},
"approvals": {
"type": "array",
"items": {
"$ref": "deployment_approval.json"
}
},
"additionalProperties": false
}
}
]
}
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::EE::API::Entities::DeploymentExtended do
subject { ::API::Entities::DeploymentExtended.new(deployment).as_json }
describe '#as_json' do
let(:deployment) { create(:deployment, :blocked) }
before do
stub_licensed_features(protected_environments: true)
create(:protected_environment, project_id: deployment.environment.project_id, name: deployment.environment.name, required_approval_count: 2)
create(:deployment_approval, :approved, deployment: deployment)
end
it 'includes fields from deployment entity' do
is_expected.to include(:id, :iid, :ref, :sha, :created_at, :updated_at, :user, :environment, :deployable, :status)
end
it 'includes pending_approval_count' do
expect(subject[:pending_approval_count]).to eq(1)
end
it 'includes approvals', :aggregate_failures do
expect(subject[:approvals].length).to eq(1)
expect(subject.dig(:approvals, 0, :status)).to eq("approved")
end
end
end
...@@ -28,7 +28,7 @@ RSpec.describe Deployment do ...@@ -28,7 +28,7 @@ RSpec.describe Deployment do
let_it_be(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let(:environment) { create(:environment, project: project) } let(:environment) { create(:environment, project: project) }
let(:deployment) { create(:deployment, project: project, environment: environment) } let(:deployment) { create(:deployment, :blocked, project: project, environment: environment) }
context 'when Protected Environments feature is available' do context 'when Protected Environments feature is available' do
before do before do
...@@ -61,6 +61,14 @@ RSpec.describe Deployment do ...@@ -61,6 +61,14 @@ RSpec.describe Deployment do
expect(deployment.pending_approval_count).to eq(0) expect(deployment.pending_approval_count).to eq(0)
end end
end end
context 'with a deployment that is not blocked' do
let(:deployment) { create(:deployment, :success, project: project, environment: environment) }
it 'returns zero' do
expect(deployment.pending_approval_count).to eq(0)
end
end
end end
context 'when Protected Environments feature is not available' do context 'when Protected Environments feature is not available' do
......
...@@ -11,7 +11,41 @@ RSpec.describe API::Deployments do ...@@ -11,7 +11,41 @@ RSpec.describe API::Deployments do
stub_licensed_features(protected_environments: true) stub_licensed_features(protected_environments: true)
end end
describe 'GET /projects/:id/deployments/:id' do
let(:deployment) { create(:deployment, :blocked, project: project) }
before do
create(:deployment_approval, :approved, deployment: deployment)
project.add_developer(user)
end
it 'matches the response schema' do
get api("/projects/#{project.id}/deployments/#{deployment.id}", user)
expect(response).to have_gitlab_http_status(:success)
expect(response).to match_response_schema('public_api/v4/deployment_extended', dir: 'ee')
end
end
describe 'POST /projects/:id/deployments' do describe 'POST /projects/:id/deployments' do
it 'matches the response schema' do
project.add_developer(user)
post(
api("/projects/#{project.id}/deployments", user),
params: {
environment: environment.name,
sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0',
ref: 'master',
tag: false,
status: 'success'
}
)
expect(response).to have_gitlab_http_status(:success)
expect(response).to match_response_schema('public_api/v4/deployment_extended', dir: 'ee')
end
context 'when deploying to a protected environment that requires maintainer access' do context 'when deploying to a protected environment that requires maintainer access' do
before do before do
create( create(
...@@ -114,6 +148,18 @@ RSpec.describe API::Deployments do ...@@ -114,6 +148,18 @@ RSpec.describe API::Deployments do
) )
end end
it 'matches the response schema' do
project.add_developer(user)
put(
api("/projects/#{project.id}/deployments/#{deploy.id}", user),
params: { status: 'success' }
)
expect(response).to have_gitlab_http_status(:success)
expect(response).to match_response_schema('public_api/v4/deployment_extended', dir: 'ee')
end
context 'when updating a deployment for a protected environment that requires maintainer access' do context 'when updating a deployment for a protected environment that requires maintainer access' do
before do before do
create( create(
......
...@@ -47,7 +47,7 @@ module API ...@@ -47,7 +47,7 @@ module API
desc 'Gets a specific deployment' do desc 'Gets a specific deployment' do
detail 'This feature was introduced in GitLab 8.11.' detail 'This feature was introduced in GitLab 8.11.'
success Entities::Deployment success Entities::DeploymentExtended
end end
params do params do
requires :deployment_id, type: Integer, desc: 'The deployment ID' requires :deployment_id, type: Integer, desc: 'The deployment ID'
...@@ -57,12 +57,12 @@ module API ...@@ -57,12 +57,12 @@ module API
deployment = user_project.deployments.find(params[:deployment_id]) deployment = user_project.deployments.find(params[:deployment_id])
present deployment, with: Entities::Deployment present deployment, with: Entities::DeploymentExtended
end end
desc 'Creates a new deployment' do desc 'Creates a new deployment' do
detail 'This feature was introduced in GitLab 12.4' detail 'This feature was introduced in GitLab 12.4'
success Entities::Deployment success Entities::DeploymentExtended
end end
params do params do
requires :environment, requires :environment,
...@@ -106,7 +106,7 @@ module API ...@@ -106,7 +106,7 @@ module API
deployment = service.execute deployment = service.execute
if deployment.persisted? if deployment.persisted?
present(deployment, with: Entities::Deployment, current_user: current_user) present(deployment, with: Entities::DeploymentExtended, current_user: current_user)
else else
render_validation_error!(deployment) render_validation_error!(deployment)
end end
...@@ -114,7 +114,7 @@ module API ...@@ -114,7 +114,7 @@ module API
desc 'Updates an existing deployment' do desc 'Updates an existing deployment' do
detail 'This feature was introduced in GitLab 12.4' detail 'This feature was introduced in GitLab 12.4'
success Entities::Deployment success Entities::DeploymentExtended
end end
params do params do
requires :status, requires :status,
...@@ -136,7 +136,7 @@ module API ...@@ -136,7 +136,7 @@ module API
service = ::Deployments::UpdateService.new(deployment, declared_params) service = ::Deployments::UpdateService.new(deployment, declared_params)
if service.execute if service.execute
present(deployment, with: Entities::Deployment, current_user: current_user) present(deployment, with: Entities::DeploymentExtended, current_user: current_user)
else else
render_validation_error!(deployment) render_validation_error!(deployment)
end end
......
# frozen_string_literal: true
module API
module Entities
class DeploymentExtended < Deployment
end
end
end
API::Entities::DeploymentExtended.prepend_mod
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
"created_at", "created_at",
"updated_at", "updated_at",
"user", "user",
"deployable" "deployable",
"status"
], ],
"properties": { "properties": {
"id": { "type": "integer" }, "id": { "type": "integer" },
...@@ -30,6 +31,5 @@ ...@@ -30,6 +31,5 @@
] ]
}, },
"status": { "type": "string" } "status": { "type": "string" }
}, }
"additionalProperties": false
} }
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Entities::DeploymentExtended do
describe '#as_json' do
subject { described_class.new(deployment).as_json }
let(:deployment) { create(:deployment) }
it 'includes fields from deployment entity' do
is_expected.to include(:id, :iid, :ref, :sha, :created_at, :updated_at, :user, :environment, :deployable, :status)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment