Commit e1b0ac5b authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch '50460-send-deployment-information-in-job-api' into 'master'

Resolve "Send deployment information in job API"

Closes #50460

See merge request gitlab-org/gitlab-ce!21307
parents 5a8908bf 3bc5f711
...@@ -654,8 +654,31 @@ module Ci ...@@ -654,8 +654,31 @@ module Ci
end end
end end
# Virtual deployment status depending on the environment status.
def deployment_status
return nil unless starts_environment?
if success?
return successful_deployment_status
elsif complete? && !success?
return :failed
end
:creating
end
private private
def successful_deployment_status
if success? && last_deployment&.last?
return :last
elsif success? && last_deployment.present?
return :out_of_date
end
:creating
end
def each_test_report def each_test_report
Ci::JobArtifact::TEST_REPORT_FILE_TYPES.each do |file_type| Ci::JobArtifact::TEST_REPORT_FILE_TYPES.each do |file_type|
public_send("job_artifacts_#{file_type}").each_blob do |blob| # rubocop:disable GitlabSecurity/PublicSend public_send("job_artifacts_#{file_type}").each_blob do |blob| # rubocop:disable GitlabSecurity/PublicSend
......
# frozen_string_literal: true # frozen_string_literal: true
class BuildDetailsEntity < JobEntity class BuildDetailsEntity < JobEntity
include EnvironmentHelper
include RequestAwareEntity
include CiStatusHelper
expose :coverage, :erased_at, :duration expose :coverage, :erased_at, :duration
expose :tag_list, as: :tags expose :tag_list, as: :tags
expose :user, using: UserEntity expose :user, using: UserEntity
expose :runner, using: RunnerEntity expose :runner, using: RunnerEntity
expose :pipeline, using: PipelineEntity expose :pipeline, using: PipelineEntity
expose :deployment_status, if: -> (*) { build.has_environment? } do
expose :deployment_status, as: :status
expose :icon do |build|
ci_label_for_status(build.status)
end
expose :persisted_environment, as: :environment, with: EnvironmentEntity
end
expose :metadata, using: BuildMetadataEntity expose :metadata, using: BuildMetadataEntity
expose :artifact, if: -> (*) { can?(current_user, :read_build, build) } do expose :artifact, if: -> (*) { can?(current_user, :read_build, build) } do
......
---
title: Send deployment information in job API
merge_request: 21307
author:
type: other
...@@ -86,7 +86,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do ...@@ -86,7 +86,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
def create_job(name, status) def create_job(name, status)
pipeline = create(:ci_pipeline, project: project) pipeline = create(:ci_pipeline, project: project)
create(:ci_build, :tags, :triggered, :artifacts, create(:ci_build, :tags, :triggered, :artifacts,
pipeline: pipeline, name: name, status: status) pipeline: pipeline, name: name, status: status)
end end
end end
...@@ -206,6 +206,29 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do ...@@ -206,6 +206,29 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(json_response['status']['illustration']).to have_key('title') expect(json_response['status']['illustration']).to have_key('title')
end end
end end
context 'with no deployment' do
let(:job) { create(:ci_build, :success, pipeline: pipeline) }
it 'does not exposes the deployment information' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['deployment_status']).to be_nil
end
end
context 'with deployment' do
let(:merge_request) { create(:merge_request, source_project: project) }
let(:environment) { create(:environment, project: project, name: 'staging', state: :available) }
let(:job) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) }
it 'exposes the deployment information' do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to match_schema('job/job_details')
expect(json_response['deployment_status']["status"]).to eq 'creating'
expect(json_response['deployment_status']["icon"]).to eq 'passed'
expect(json_response['deployment_status']["environment"]).not_to be_nil
end
end
end end
context 'when requesting JSON job is triggered' do context 'when requesting JSON job is triggered' do
......
{ {
"additionalProperties": false, "type": "object",
"properties": { "required": [
"created_at": { "sha",
"type": "string" "created_at",
}, "iid",
"id": { "tag",
"type": "integer" "last?",
}, "ref",
"iid": { "id"
"type": "integer" ],
}, "properties": {
"last?": { "created_at": { "type": "string" },
"type": "boolean" "id": { "type": "integer" },
}, "iid": { "type": "integer" },
"ref": { "last?": { "type": "boolean" },
"additionalProperties": false, "ref": {
"properties": { "type": "object",
"name": { "required": [
"type": "string" "name"
} ],
}, "properties": {
"required": [ "name": { "type": "string" }
"name" },
], "additionalProperties": false
"type": "object"
},
"sha": {
"type": "string"
},
"tag": {
"type": "boolean"
}
}, },
"required": [ "sha": { "type": "string" },
"sha", "tag": { "type": "boolean" }
"created_at", },
"iid", "additionalProperties": false
"tag",
"last?",
"ref",
"id"
],
"type": "object"
} }
{
"type": "object",
"allOf": [
{ "$ref": "../public_api/v4/commit/basic.json" },
{
"type": "object",
"required": [
"author_gravatar_url",
"commit_url",
"commit_path",
"author"
],
"properties": {
"author_gravatar_url": { "type": "string" },
"commit_url": { "type": "string" },
"commit_path": { "type": "string" },
"author": {
"oneOf": [
{ "type": "null" },
{ "type": "user.json" }
]
}
},
"additionalProperties": false
}
]
}
...@@ -5,13 +5,19 @@ ...@@ -5,13 +5,19 @@
"state", "state",
"avatar_url", "avatar_url",
"web_url", "web_url",
"path" "path",
"name",
"username"
], ],
"properties": { "properties": {
"id": { "type": "integer" }, "id": { "type": "integer" },
"state": { "type": "string" }, "state": { "type": "string" },
"avatar_url": { "type": "string" }, "avatar_url": { "type": "string" },
"web_url": { "type": "string" }, "web_url": { "type": "string" },
"path": { "type": "string" } "path": { "type": "string" },
} "name": { "type": "string" },
"username": { "type": "string" },
"status_tooltip_html": { "$ref": "../types/nullable_string.json" }
},
"additionalProperties": false
} }
{
"type": "object",
"required": [
"id",
"name",
"state",
"external_url",
"environment_type",
"has_stop_action",
"environment_path",
"stop_path",
"folder_path",
"created_at",
"updated_at",
"can_stop"
],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"state": { "type": "string" },
"external_url": { "$ref": "types/nullable_string.json" },
"environment_type": { "$ref": "types/nullable_string.json" },
"has_stop_action": { "type": "boolean" },
"environment_path": { "type": "string" },
"stop_path": { "type": "string" },
"folder_path": { "type": "string" },
"created_at": { "type": "string", "format": "date-time" },
"updated_at": { "type": "string", "format": "date-time" },
"can_stop": { "type": "boolean" },
"last_deployment": {
"oneOf": [
{ "type": "null" },
{ "$ref": "deployment.json" }
]
}
},
"additionalProperties": false
}
{
"type": "object",
"required": [
"status",
"icon",
"environment"
],
"properties": {
"status": {
"oneOf": [
{
"type": "string",
"enum": [
"last",
"creating",
"failed",
"out_of_date"
]
},
{ "type": "null" }
]
},
"icon": { "type": "string" },
"environment": { "$ref": "../environment.json" }
},
"additionalProperties": false
}
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
"properties": { "properties": {
"artifact": { "$ref": "artifact.json" }, "artifact": { "$ref": "artifact.json" },
"terminal_path": { "type": "string" }, "terminal_path": { "type": "string" },
"trigger": { "$ref": "trigger.json" } "trigger": { "$ref": "trigger.json" },
"deployment_status": { "$ref": "deployment_status.json" }
} }
} }
{
"oneOf": [
{ "type": "null" },
{ "type": "string" }
]
}
...@@ -2981,4 +2981,46 @@ describe Ci::Build do ...@@ -2981,4 +2981,46 @@ describe Ci::Build do
end end
end end
end end
describe '#deployment_status' do
context 'when build is a last deployment' do
let(:build) { create(:ci_build, :success, environment: 'production') }
let(:environment) { create(:environment, name: 'production', project: build.project) }
let!(:deployment) { create(:deployment, environment: environment, project: environment.project, deployable: build) }
it { expect(build.deployment_status).to eq(:last) }
end
context 'when there is a newer build with deployment' do
let(:build) { create(:ci_build, :success, environment: 'production') }
let(:environment) { create(:environment, name: 'production', project: build.project) }
let!(:deployment) { create(:deployment, environment: environment, project: environment.project, deployable: build) }
let!(:last_deployment) { create(:deployment, environment: environment, project: environment.project) }
it { expect(build.deployment_status).to eq(:out_of_date) }
end
context 'when build with deployment has failed' do
let(:build) { create(:ci_build, :failed, environment: 'production') }
let(:environment) { create(:environment, name: 'production', project: build.project) }
let!(:deployment) { create(:deployment, environment: environment, project: environment.project, deployable: build) }
it { expect(build.deployment_status).to eq(:failed) }
end
context 'when build with deployment is running' do
let(:build) { create(:ci_build, environment: 'production') }
let(:environment) { create(:environment, name: 'production', project: build.project) }
let!(:deployment) { create(:deployment, environment: environment, project: environment.project, deployable: build) }
it { expect(build.deployment_status).to eq(:creating) }
end
context 'when build is successful but deployment is not ready yet' do
let(:build) { create(:ci_build, :success, environment: 'production') }
let(:environment) { create(:environment, name: 'production', project: build.project) }
it { expect(build.deployment_status).to eq(:creating) }
end
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