Commit ce79c6e6 authored by Erick Bajao's avatar Erick Bajao

Start tracking project ci feature usages

This starts tracking project CI feature usage
for code coverage through the new table project_ci_feature_usages.
This new table will be used to avoid directly joining to CI tables.

Changelog: added
parent bdefcb44
# frozen_string_literal: true
module Projects
class CiFeatureUsage < ApplicationRecord
self.table_name = 'project_ci_feature_usages'
belongs_to :project
validates :project, :feature, presence: true
enum feature: {
code_coverage: 1,
security_report: 2
}
def self.insert_usage(project_id:, feature:, default_branch:)
insert(
{
project_id: project_id,
feature: feature,
default_branch: default_branch
},
unique_by: 'index_project_ci_feature_usages_unique_columns'
)
end
end
end
...@@ -3,7 +3,13 @@ ...@@ -3,7 +3,13 @@
module Ci module Ci
class DailyBuildGroupReportResultService class DailyBuildGroupReportResultService
def execute(pipeline) def execute(pipeline)
DailyBuildGroupReportResult.upsert_reports(coverage_reports(pipeline)) if DailyBuildGroupReportResult.upsert_reports(coverage_reports(pipeline))
Projects::CiFeatureUsage.insert_usage(
project_id: pipeline.project_id,
feature: :code_coverage,
default_branch: pipeline.default_branch?
)
end
end end
private private
......
# frozen_string_literal: true
class CreateProjectCiFeatureUsages < ActiveRecord::Migration[6.1]
def change
create_table :project_ci_feature_usages do |t|
t.references :project, index: false, foreign_key: { on_delete: :cascade }, null: false
t.integer :feature, null: false, limit: 2
t.boolean :default_branch, default: false, null: false
t.index [:project_id, :feature, :default_branch], unique: true, name: 'index_project_ci_feature_usages_unique_columns'
end
end
end
7c62c47ebad110a343c1f9834ae34bd0fa2bad763025da06f911e127a7380542
\ No newline at end of file
...@@ -16933,6 +16933,22 @@ CREATE SEQUENCE project_ci_cd_settings_id_seq ...@@ -16933,6 +16933,22 @@ CREATE SEQUENCE project_ci_cd_settings_id_seq
ALTER SEQUENCE project_ci_cd_settings_id_seq OWNED BY project_ci_cd_settings.id; ALTER SEQUENCE project_ci_cd_settings_id_seq OWNED BY project_ci_cd_settings.id;
CREATE TABLE project_ci_feature_usages (
id bigint NOT NULL,
project_id bigint NOT NULL,
feature smallint NOT NULL,
default_branch boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE project_ci_feature_usages_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE project_ci_feature_usages_id_seq OWNED BY project_ci_feature_usages.id;
CREATE TABLE project_compliance_framework_settings ( CREATE TABLE project_compliance_framework_settings (
project_id bigint NOT NULL, project_id bigint NOT NULL,
framework_id bigint, framework_id bigint,
...@@ -20620,6 +20636,8 @@ ALTER TABLE ONLY project_auto_devops ALTER COLUMN id SET DEFAULT nextval('projec ...@@ -20620,6 +20636,8 @@ ALTER TABLE ONLY project_auto_devops ALTER COLUMN id SET DEFAULT nextval('projec
ALTER TABLE ONLY project_ci_cd_settings ALTER COLUMN id SET DEFAULT nextval('project_ci_cd_settings_id_seq'::regclass); ALTER TABLE ONLY project_ci_cd_settings ALTER COLUMN id SET DEFAULT nextval('project_ci_cd_settings_id_seq'::regclass);
ALTER TABLE ONLY project_ci_feature_usages ALTER COLUMN id SET DEFAULT nextval('project_ci_feature_usages_id_seq'::regclass);
ALTER TABLE ONLY project_compliance_framework_settings ALTER COLUMN project_id SET DEFAULT nextval('project_compliance_framework_settings_project_id_seq'::regclass); ALTER TABLE ONLY project_compliance_framework_settings ALTER COLUMN project_id SET DEFAULT nextval('project_compliance_framework_settings_project_id_seq'::regclass);
ALTER TABLE ONLY project_custom_attributes ALTER COLUMN id SET DEFAULT nextval('project_custom_attributes_id_seq'::regclass); ALTER TABLE ONLY project_custom_attributes ALTER COLUMN id SET DEFAULT nextval('project_custom_attributes_id_seq'::regclass);
...@@ -22208,6 +22226,9 @@ ALTER TABLE ONLY project_auto_devops ...@@ -22208,6 +22226,9 @@ ALTER TABLE ONLY project_auto_devops
ALTER TABLE ONLY project_ci_cd_settings ALTER TABLE ONLY project_ci_cd_settings
ADD CONSTRAINT project_ci_cd_settings_pkey PRIMARY KEY (id); ADD CONSTRAINT project_ci_cd_settings_pkey PRIMARY KEY (id);
ALTER TABLE ONLY project_ci_feature_usages
ADD CONSTRAINT project_ci_feature_usages_pkey PRIMARY KEY (id);
ALTER TABLE ONLY project_compliance_framework_settings ALTER TABLE ONLY project_compliance_framework_settings
ADD CONSTRAINT project_compliance_framework_settings_pkey PRIMARY KEY (project_id); ADD CONSTRAINT project_compliance_framework_settings_pkey PRIMARY KEY (project_id);
...@@ -24772,6 +24793,8 @@ CREATE UNIQUE INDEX index_project_auto_devops_on_project_id ON project_auto_devo ...@@ -24772,6 +24793,8 @@ CREATE UNIQUE INDEX index_project_auto_devops_on_project_id ON project_auto_devo
CREATE UNIQUE INDEX index_project_ci_cd_settings_on_project_id ON project_ci_cd_settings USING btree (project_id); CREATE UNIQUE INDEX index_project_ci_cd_settings_on_project_id ON project_ci_cd_settings USING btree (project_id);
CREATE UNIQUE INDEX index_project_ci_feature_usages_unique_columns ON project_ci_feature_usages USING btree (project_id, feature, default_branch);
CREATE INDEX index_project_compliance_framework_settings_on_framework_id ON project_compliance_framework_settings USING btree (framework_id); CREATE INDEX index_project_compliance_framework_settings_on_framework_id ON project_compliance_framework_settings USING btree (framework_id);
CREATE INDEX index_project_compliance_framework_settings_on_project_id ON project_compliance_framework_settings USING btree (project_id); CREATE INDEX index_project_compliance_framework_settings_on_project_id ON project_compliance_framework_settings USING btree (project_id);
...@@ -27013,6 +27036,9 @@ ALTER TABLE ONLY epic_user_mentions ...@@ -27013,6 +27036,9 @@ ALTER TABLE ONLY epic_user_mentions
ALTER TABLE ONLY approver_groups ALTER TABLE ONLY approver_groups
ADD CONSTRAINT fk_rails_1cdcbd7723 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_1cdcbd7723 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY project_ci_feature_usages
ADD CONSTRAINT fk_rails_1deedbf64b FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_tags ALTER TABLE ONLY packages_tags
ADD CONSTRAINT fk_rails_1dfc868911 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_1dfc868911 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
# frozen_string_literal: true
FactoryBot.define do
factory :project_ci_feature_usage, class: 'Projects::CiFeatureUsage' do
project factory: :project
feature { :code_coverage } # rubocop: disable RSpec/EmptyExampleGroup
default_branch { false }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::CiFeatureUsage, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
it_behaves_like 'having unique enum values'
describe 'validations' do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:feature) }
end
describe '.insert_usage' do
let_it_be(:project) { create(:project) }
context 'when data is not a duplicate' do
it 'creates a new record' do
expect { described_class.insert_usage(project_id: project.id, default_branch: false, feature: :code_coverage) }
.to change { described_class.count }
expect(described_class.first).to have_attributes(
project_id: project.id,
default_branch: false,
feature: 'code_coverage'
)
end
end
context 'when data is a duplicate' do
before do
create(:project_ci_feature_usage, project: project, default_branch: false, feature: :code_coverage)
end
it 'does not create a new record' do
expect { described_class.insert_usage(project_id: project.id, default_branch: false, feature: :code_coverage) }
.not_to change { described_class.count }
end
end
end
end
...@@ -41,6 +41,17 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do ...@@ -41,6 +41,17 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do
expect(Ci::DailyBuildGroupReportResult.find_by(group_name: 'extra')).to be_nil expect(Ci::DailyBuildGroupReportResult.find_by(group_name: 'extra')).to be_nil
end end
it 'creates a project_ci_feature_usage record for the pipeline project' do
described_class.new.execute(pipeline)
expect(Projects::CiFeatureUsage.count).to eq(1)
expect(Projects::CiFeatureUsage.first).to have_attributes(
project_id: pipeline.project.id,
feature: 'code_coverage',
default_branch: false
)
end
context 'when there are multiple builds with the same group name that report coverage' do context 'when there are multiple builds with the same group name that report coverage' do
let!(:test_job_1) { create(:ci_build, pipeline: pipeline, name: 'test 1/2', coverage: 70) } let!(:test_job_1) { create(:ci_build, pipeline: pipeline, name: 'test 1/2', coverage: 70) }
let!(:test_job_2) { create(:ci_build, pipeline: pipeline, name: 'test 2/2', coverage: 80) } let!(:test_job_2) { create(:ci_build, pipeline: pipeline, name: 'test 2/2', coverage: 80) }
...@@ -99,6 +110,16 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do ...@@ -99,6 +110,16 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do
data: { 'coverage' => new_karma_job.coverage } data: { 'coverage' => new_karma_job.coverage }
) )
end end
it 'does not create a new project_ci_feature_usage record for the pipeline project' do
expect { described_class.new.execute(pipeline) }.not_to change { Projects::CiFeatureUsage.count }
expect(Projects::CiFeatureUsage.first).to have_attributes(
project_id: pipeline.project.id,
feature: 'code_coverage',
default_branch: false
)
end
end end
context 'when the ID of the pipeline is older than the last_pipeline_id' do context 'when the ID of the pipeline is older than the last_pipeline_id' do
...@@ -161,6 +182,8 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do ...@@ -161,6 +182,8 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do
it 'does nothing' do it 'does nothing' do
expect { described_class.new.execute(new_pipeline) }.not_to raise_error expect { described_class.new.execute(new_pipeline) }.not_to raise_error
expect(Ci::DailyBuildGroupReportResult.count).to eq(0)
expect(Projects::CiFeatureUsage.count).to eq(0)
end end
end end
...@@ -178,6 +201,17 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do ...@@ -178,6 +201,17 @@ RSpec.describe Ci::DailyBuildGroupReportResultService, '#execute' do
expect(coverage.default_branch).to be_truthy expect(coverage.default_branch).to be_truthy
end end
end end
it 'creates a project_ci_feature_usage record for the pipeline project for default branch' do
described_class.new.execute(pipeline)
expect(Projects::CiFeatureUsage.count).to eq(1)
expect(Projects::CiFeatureUsage.first).to have_attributes(
project_id: pipeline.project.id,
feature: 'code_coverage',
default_branch: true
)
end
end end
context 'when pipeline ref_path is not the project default branch' do context 'when pipeline ref_path is not the project default branch' 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