Commit ebf78c1a authored by James Fargher's avatar James Fargher

Merge branch '334680-remove-ci-build-join-in-vsa' into 'master'

Remove special case for test and staging stages [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!68000
parents d6336e51 755fdd44
......@@ -17,8 +17,6 @@ import {
PAGINATION_SORT_FIELD_DURATION,
PAGINATION_SORT_DIRECTION_ASC,
PAGINATION_SORT_DIRECTION_DESC,
STAGE_TITLE_STAGING,
STAGE_TITLE_TEST,
} from '../constants';
import TotalTime from './total_time_component.vue';
......@@ -107,28 +105,12 @@ export default {
emptyStateTitleText() {
return this.emptyStateTitle || NOT_ENOUGH_DATA_ERROR;
},
isDefaultTestStage() {
const { selectedStage } = this;
return (
!selectedStage.custom && selectedStage.title?.toLowerCase().trim() === STAGE_TITLE_TEST
);
},
isDefaultStagingStage() {
const { selectedStage } = this;
return (
!selectedStage.custom && selectedStage.title?.toLowerCase().trim() === STAGE_TITLE_STAGING
);
},
isMergeRequestStage() {
const [firstEvent] = this.stageEvents;
return this.isMrLink(firstEvent.url);
},
workflowTitle() {
if (this.isDefaultTestStage) {
return WORKFLOW_COLUMN_TITLES.jobs;
} else if (this.isDefaultStagingStage) {
return WORKFLOW_COLUMN_TITLES.deployments;
} else if (this.isMergeRequestStage) {
if (this.isMergeRequestStage) {
return WORKFLOW_COLUMN_TITLES.mergeRequests;
}
return WORKFLOW_COLUMN_TITLES.issues;
......@@ -209,22 +191,6 @@ export default {
<div data-testid="vsa-stage-event">
<div v-if="item.id" data-testid="vsa-stage-content">
<p class="gl-m-0">
<template v-if="isDefaultTestStage">
<span
class="icon-build-status gl-vertical-align-middle gl-text-green-500"
data-testid="vsa-stage-event-build-status"
>
<gl-icon name="status_success" :size="14" />
</span>
<gl-link
class="gl-text-black-normal item-build-name"
data-testid="vsa-stage-event-build-name"
:href="item.url"
>
{{ item.name }}
</gl-link>
&middot;
</template>
<gl-link class="gl-text-black-normal pipeline-id" :href="item.url"
>#{{ item.id }}</gl-link
>
......@@ -246,12 +212,7 @@ export default {
>
</p>
<p class="gl-m-0">
<span v-if="isDefaultTestStage" data-testid="vsa-stage-event-build-status-date">
<gl-link class="gl-text-black-normal issue-date" :href="item.url">{{
item.date
}}</gl-link>
</span>
<span v-else data-testid="vsa-stage-event-build-author-and-date">
<span data-testid="vsa-stage-event-build-author-and-date">
<gl-link class="gl-text-black-normal build-date" :href="item.url">{{
item.date
}}</gl-link>
......
......@@ -25,9 +25,6 @@ export const PAGINATION_SORT_FIELD_DURATION = 'duration';
export const PAGINATION_SORT_DIRECTION_DESC = 'desc';
export const PAGINATION_SORT_DIRECTION_ASC = 'asc';
export const STAGE_TITLE_STAGING = 'staging';
export const STAGE_TITLE_TEST = 'test';
export const I18N_VSA_ERROR_STAGES = __(
'There was an error fetching value stream analytics stages.',
);
......
......@@ -38,36 +38,19 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def serialized_records
strong_memoize(:serialized_records) do
# special case (legacy): 'Test' and 'Staging' stages should show Ci::Build records
if default_test_stage? || default_staging_stage?
ci_build_join = mr_metrics_table
.join(build_table)
.on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id]))
.join_sources
records = ordered_and_limited_query
.joins(ci_build_join)
.select(build_table[:id], *time_columns)
yield records if block_given?
ci_build_records = preload_ci_build_associations(records)
AnalyticsBuildSerializer.new.represent(ci_build_records.map { |e| e['build'] })
else
records = ordered_and_limited_query.select(*columns, *time_columns)
yield records if block_given?
records = preload_associations(records)
records.map do |record|
project = record.project
attributes = record.attributes.merge({
project_path: project.path,
namespace_path: project.namespace.route.path,
author: record.author
})
serializer.represent(attributes)
end
records = ordered_and_limited_query.select(*columns, *time_columns)
yield records if block_given?
records = preload_associations(records)
records.map do |record|
project = record.project
attributes = record.attributes.merge({
project_path: project.path,
namespace_path: project.namespace.route.path,
author: record.author
})
serializer.represent(attributes)
end
end
end
......@@ -83,26 +66,10 @@ module Gitlab
end
end
def default_test_stage?
stage.matches_with_stage_params?(Gitlab::Analytics::CycleAnalytics::DefaultStages.params_for_test_stage)
end
def default_staging_stage?
stage.matches_with_stage_params?(Gitlab::Analytics::CycleAnalytics::DefaultStages.params_for_staging_stage)
end
def serializer
MAPPINGS.fetch(subject_class).fetch(:serializer_class).new
end
# rubocop: disable CodeReuse/ActiveRecord
def preload_ci_build_associations(records)
results = records.map(&:attributes)
Gitlab::CycleAnalytics::Updater.update!(results, from: 'id', to: 'build', klass: ::Ci::Build.includes({ project: [:namespace], user: [], pipeline: [] }))
end
# rubocop: enable CodeReuse/ActiveRecord
def ordered_and_limited_query
strong_memoize(:ordered_and_limited_query) do
order_by(query, sort, direction, columns).page(page).per(per_page).without_count
......
......@@ -84,13 +84,13 @@ RSpec.describe 'Value Stream Analytics', :js do
expect_merge_request_to_be_present
click_stage('Test')
expect_build_to_be_present
expect_merge_request_to_be_present
click_stage('Review')
expect_merge_request_to_be_present
click_stage('Staging')
expect_build_to_be_present
expect_merge_request_to_be_present
end
context "when I change the time period observed" do
......@@ -168,12 +168,6 @@ RSpec.describe 'Value Stream Analytics', :js do
expect(find(stage_table_selector)).to have_content("##{issue.iid}")
end
def expect_build_to_be_present
expect(find(stage_table_selector)).to have_content(@build.ref)
expect(find(stage_table_selector)).to have_content(@build.short_sha)
expect(find(stage_table_selector)).to have_content("##{@build.id}")
end
def expect_merge_request_to_be_present
expect(find(stage_table_selector)).to have_content(mr.title)
expect(find(stage_table_selector)).to have_content(mr.author.name)
......
......@@ -135,8 +135,6 @@ export const convertedData = {
export const rawIssueEvents = stageFixtures.issue;
export const issueEvents = deepCamelCase(rawIssueEvents);
export const reviewEvents = deepCamelCase(stageFixtures.review);
export const testEvents = deepCamelCase(stageFixtures.test);
export const stagingEvents = deepCamelCase(stageFixtures.staging);
export const pathNavIssueMetric = 172800;
......
......@@ -4,16 +4,7 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import StageTable from '~/cycle_analytics/components/stage_table.vue';
import { PAGINATION_SORT_FIELD_DURATION } from '~/cycle_analytics/constants';
import {
stagingEvents,
stagingStage,
issueEvents,
issueStage,
testEvents,
testStage,
reviewStage,
reviewEvents,
} from './mock_data';
import { issueEvents, issueStage, reviewStage, reviewEvents } from './mock_data';
let wrapper = null;
let trackingSpy = null;
......@@ -22,12 +13,8 @@ const noDataSvgPath = 'path/to/no/data';
const emptyStateTitle = 'Too much data';
const notEnoughDataError = "We don't have enough data to show this stage.";
const issueEventItems = issueEvents.events;
const stagingEventItems = stagingEvents.events;
const testEventItems = testEvents.events;
const reviewEventItems = reviewEvents.events;
const [firstIssueEvent] = issueEventItems;
const [firstStagingEvent] = stagingEventItems;
const [firstTestEvent] = testEventItems;
const [firstReviewEvent] = reviewEventItems;
const pagination = { page: 1, hasNextPage: true };
......@@ -156,99 +143,6 @@ describe('StageTable', () => {
});
});
describe('staging event', () => {
beforeEach(() => {
wrapper = createComponent({
stageEvents: [{ ...firstStagingEvent }],
selectedStage: { ...stagingStage, custom: false },
});
});
it('will set the workflow title to "Deployments"', () => {
expect(findTableHead().text()).toContain('Deployments');
expect(findTableHead().text()).not.toContain('Issues');
});
it('will not render the event title', () => {
expect(wrapper.findByTestId('vsa-stage-event-title').exists()).toBe(false);
});
it('will render the fork icon', () => {
expect(findIcon('fork').exists()).toBe(true);
});
it('will render the branch icon', () => {
expect(findIcon('commit').exists()).toBe(true);
});
it('will render the total time', () => {
expect(findStageTime().text()).toBe('2 mins');
});
it('will render the build shortSha', () => {
expect(wrapper.findByTestId('vsa-stage-event-build-sha').text()).toBe(
firstStagingEvent.shortSha,
);
});
it('will render the author and date', () => {
const content = wrapper.findByTestId('vsa-stage-event-build-author-and-date').text();
expect(content).toContain(firstStagingEvent.author.name);
expect(content).toContain(firstStagingEvent.date);
});
});
describe('test event', () => {
beforeEach(() => {
wrapper = createComponent({
stageEvents: [{ ...firstTestEvent }],
selectedStage: { ...testStage, custom: false },
});
});
it('will set the workflow title to "Jobs"', () => {
expect(findTableHead().text()).toContain('Jobs');
expect(findTableHead().text()).not.toContain('Issues');
});
it('will not render the event title', () => {
expect(wrapper.findByTestId('vsa-stage-event-title').exists()).toBe(false);
});
it('will render the fork icon', () => {
expect(findIcon('fork').exists()).toBe(true);
});
it('will render the branch icon', () => {
expect(findIcon('commit').exists()).toBe(true);
});
it('will render the total time', () => {
expect(findStageTime().text()).toBe('2 mins');
});
it('will render the build shortSha', () => {
expect(wrapper.findByTestId('vsa-stage-event-build-sha').text()).toBe(
firstTestEvent.shortSha,
);
});
it('will render the build pipeline success icon', () => {
expect(wrapper.findByTestId('status_success-icon').exists()).toBe(true);
});
it('will render the build date', () => {
const content = wrapper.findByTestId('vsa-stage-event-build-status-date').text();
expect(content).toContain(firstTestEvent.date);
});
it('will render the build event name', () => {
expect(wrapper.findByTestId('vsa-stage-event-build-name').text()).toContain(
firstTestEvent.name,
);
});
});
describe('isLoading = true', () => {
beforeEach(() => {
wrapper = createComponent({ isLoading: true }, true);
......
......@@ -79,56 +79,6 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do
include_context 'when records are loaded by maintainer'
end
describe 'special case' do
let(:mr1) { create(:merge_request, source_project: project, allow_broken: true, created_at: 20.days.ago) }
let(:mr2) { create(:merge_request, source_project: project, allow_broken: true, created_at: 19.days.ago) }
let(:ci_build1) { create(:ci_build) }
let(:ci_build2) { create(:ci_build) }
let(:default_stages) { Gitlab::Analytics::CycleAnalytics::DefaultStages }
let(:stage) { build(:cycle_analytics_project_stage, default_stages.params_for_test_stage.merge(project: project)) }
before do
mr1.metrics.update!({
merged_at: 5.days.ago,
first_deployed_to_production_at: 1.day.ago,
latest_build_started_at: 5.days.ago,
latest_build_finished_at: 1.day.ago,
pipeline: ci_build1.pipeline
})
mr2.metrics.update!({
merged_at: 10.days.ago,
first_deployed_to_production_at: 5.days.ago,
latest_build_started_at: 9.days.ago,
latest_build_finished_at: 7.days.ago,
pipeline: ci_build2.pipeline
})
project.add_user(user, Gitlab::Access::MAINTAINER)
end
context 'returns build records' do
shared_examples 'orders build records by `latest_build_finished_at`' do
it 'orders by `latest_build_finished_at`' do
build_ids = subject.map { |item| item[:id] }
expect(build_ids).to eq([ci_build1.id, ci_build2.id])
end
end
context 'when requesting records for default test stage' do
include_examples 'orders build records by `latest_build_finished_at`'
end
context 'when requesting records for default staging stage' do
before do
stage.assign_attributes(default_stages.params_for_staging_stage)
end
include_examples 'orders build records by `latest_build_finished_at`'
end
end
end
end
describe 'pagination' do
......
......@@ -8,6 +8,9 @@ RSpec.describe 'value stream analytics events' do
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
describe 'GET /:namespace/:project/value_stream_analytics/events/issues' do
let(:first_issue_iid) { project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s }
let(:first_mr_iid) { project.merge_requests.sort_by_attribute(:created_desc).pluck(:iid).first.to_s }
before do
project.add_developer(user)
......@@ -25,8 +28,6 @@ RSpec.describe 'value stream analytics events' do
it 'lists the issue events' do
get project_cycle_analytics_issue_path(project, format: :json)
first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['iid']).to eq(first_issue_iid)
end
......@@ -34,8 +35,6 @@ RSpec.describe 'value stream analytics events' do
it 'lists the plan events' do
get project_cycle_analytics_plan_path(project, format: :json)
first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['iid']).to eq(first_issue_iid)
end
......@@ -45,8 +44,6 @@ RSpec.describe 'value stream analytics events' do
expect(json_response['events']).not_to be_empty
first_mr_iid = project.merge_requests.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
expect(json_response['events'].first['iid']).to eq(first_mr_iid)
end
......@@ -54,15 +51,15 @@ RSpec.describe 'value stream analytics events' do
get project_cycle_analytics_test_path(project, format: :json)
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['date']).not_to be_empty
expect(json_response['events'].first['iid']).to eq(first_mr_iid)
end
it 'lists the review events' do
get project_cycle_analytics_review_path(project, format: :json)
first_mr_iid = project.merge_requests.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['iid']).to eq(first_mr_iid)
end
......@@ -70,7 +67,8 @@ RSpec.describe 'value stream analytics events' do
get project_cycle_analytics_staging_path(project, format: :json)
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['date']).not_to be_empty
expect(json_response['events'].first['iid']).to eq(first_issue_iid)
end
context 'with private project and builds' 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