Commit afda9806 authored by Mehmet Emin INAC's avatar Mehmet Emin INAC

Create Vulnerabilities::Statistic model

This model will be used to store pre-calculated project vulnerability
stats.
parent 5d7a7584
# frozen_string_literal: true
class CreateVulnerabilityStatistics < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
create_table :vulnerability_statistics do |t|
t.timestamps_with_timezone null: false
t.references :project, null: false, foreign_key: { on_delete: :cascade }
t.integer :total, default: 0, null: false
t.integer :critical, default: 0, null: false
t.integer :high, default: 0, null: false
t.integer :medium, default: 0, null: false
t.integer :low, default: 0, null: false
t.integer :unknown, default: 0, null: false
t.integer :info, default: 0, null: false
t.integer :letter_grade, limit: 1, index: true, null: false
end
end
end
def down
with_lock_retries do
drop_table :vulnerability_statistics # rubocop:disable Migration/DropTable
end
end
end
......@@ -7264,6 +7264,30 @@ CREATE SEQUENCE public.vulnerability_scanners_id_seq
ALTER SEQUENCE public.vulnerability_scanners_id_seq OWNED BY public.vulnerability_scanners.id;
CREATE TABLE public.vulnerability_statistics (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
project_id bigint NOT NULL,
total integer DEFAULT 0 NOT NULL,
critical integer DEFAULT 0 NOT NULL,
high integer DEFAULT 0 NOT NULL,
medium integer DEFAULT 0 NOT NULL,
low integer DEFAULT 0 NOT NULL,
unknown integer DEFAULT 0 NOT NULL,
info integer DEFAULT 0 NOT NULL,
letter_grade smallint NOT NULL
);
CREATE SEQUENCE public.vulnerability_statistics_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.vulnerability_statistics_id_seq OWNED BY public.vulnerability_statistics.id;
CREATE TABLE public.vulnerability_user_mentions (
id bigint NOT NULL,
vulnerability_id bigint NOT NULL,
......@@ -8083,6 +8107,8 @@ ALTER TABLE ONLY public.vulnerability_occurrences ALTER COLUMN id SET DEFAULT ne
ALTER TABLE ONLY public.vulnerability_scanners ALTER COLUMN id SET DEFAULT nextval('public.vulnerability_scanners_id_seq'::regclass);
ALTER TABLE ONLY public.vulnerability_statistics ALTER COLUMN id SET DEFAULT nextval('public.vulnerability_statistics_id_seq'::regclass);
ALTER TABLE ONLY public.vulnerability_user_mentions ALTER COLUMN id SET DEFAULT nextval('public.vulnerability_user_mentions_id_seq'::regclass);
ALTER TABLE ONLY public.web_hook_logs ALTER COLUMN id SET DEFAULT nextval('public.web_hook_logs_id_seq'::regclass);
......@@ -9124,6 +9150,9 @@ ALTER TABLE ONLY public.vulnerability_occurrences
ALTER TABLE ONLY public.vulnerability_scanners
ADD CONSTRAINT vulnerability_scanners_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.vulnerability_statistics
ADD CONSTRAINT vulnerability_statistics_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.vulnerability_user_mentions
ADD CONSTRAINT vulnerability_user_mentions_pkey PRIMARY KEY (id);
......@@ -11159,6 +11188,10 @@ CREATE INDEX index_vulnerability_occurrences_on_vulnerability_id ON public.vulne
CREATE UNIQUE INDEX index_vulnerability_scanners_on_project_id_and_external_id ON public.vulnerability_scanners USING btree (project_id, external_id);
CREATE INDEX index_vulnerability_statistics_on_letter_grade ON public.vulnerability_statistics USING btree (letter_grade);
CREATE INDEX index_vulnerability_statistics_on_project_id ON public.vulnerability_statistics USING btree (project_id);
CREATE UNIQUE INDEX index_vulnerability_user_mentions_on_note_id ON public.vulnerability_user_mentions USING btree (note_id) WHERE (note_id IS NOT NULL);
CREATE UNIQUE INDEX index_vulns_user_mentions_on_vulnerability_id ON public.vulnerability_user_mentions USING btree (vulnerability_id) WHERE (note_id IS NULL);
......@@ -12623,6 +12656,9 @@ ALTER TABLE ONLY public.metrics_dashboard_annotations
ALTER TABLE ONLY public.pool_repositories
ADD CONSTRAINT fk_rails_af3f8c5d62 FOREIGN KEY (shard_id) REFERENCES public.shards(id) ON DELETE RESTRICT;
ALTER TABLE ONLY public.vulnerability_statistics
ADD CONSTRAINT fk_rails_af61a7df4c FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.resource_label_events
ADD CONSTRAINT fk_rails_b126799f57 FOREIGN KEY (label_id) REFERENCES public.labels(id) ON DELETE SET NULL;
......@@ -13997,6 +14033,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200609142507
20200609142508
20200609212701
20200610130002
20200613104045
20200615083635
20200615121217
......
......@@ -47,6 +47,7 @@ module EE
has_one :status_page_setting, inverse_of: :project, class_name: 'StatusPage::ProjectSetting'
has_one :compliance_framework_setting, class_name: 'ComplianceManagement::ComplianceFramework::ProjectSettings', inverse_of: :project
has_one :security_setting, class_name: 'ProjectSecuritySetting'
has_one :vulnerability_statistic, class_name: 'Vulnerabilities::Statistic'
has_many :approvers, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :approver_users, through: :approvers, source: :user
......
# frozen_string_literal: true
module Vulnerabilities
class Statistic < ApplicationRecord
self.table_name = 'vulnerability_statistics'
belongs_to :project, optional: false
enum letter_grade: { a: 0, b: 1, c: 2, d: 3, f: 4 }
validates :total, numericality: { greater_than_or_equal_to: 0 }
validates :critical, numericality: { greater_than_or_equal_to: 0 }
validates :high, numericality: { greater_than_or_equal_to: 0 }
validates :medium, numericality: { greater_than_or_equal_to: 0 }
validates :low, numericality: { greater_than_or_equal_to: 0 }
validates :unknown, numericality: { greater_than_or_equal_to: 0 }
validates :info, numericality: { greater_than_or_equal_to: 0 }
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :vulnerability_statistic, class: 'Vulnerabilities::Statistic' do
project
end
end
......@@ -28,6 +28,7 @@ RSpec.describe Project do
it { is_expected.to have_one(:status_page_setting).class_name('StatusPage::ProjectSetting') }
it { is_expected.to have_one(:compliance_framework_setting).class_name('ComplianceManagement::ComplianceFramework::ProjectSettings') }
it { is_expected.to have_one(:security_setting).class_name('ProjectSecuritySetting') }
it { is_expected.to have_one(:vulnerability_statistic).class_name('Vulnerabilities::Statistic') }
it { is_expected.to have_many(:path_locks) }
it { is_expected.to have_many(:vulnerability_feedback) }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Vulnerabilities::Statistic do
describe 'associations' do
it { is_expected.to belong_to(:project).required(true) }
end
describe 'validations' do
it { is_expected.to validate_numericality_of(:total).is_greater_than_or_equal_to(0) }
it { is_expected.to validate_numericality_of(:critical).is_greater_than_or_equal_to(0) }
it { is_expected.to validate_numericality_of(:high).is_greater_than_or_equal_to(0) }
it { is_expected.to validate_numericality_of(:medium).is_greater_than_or_equal_to(0) }
it { is_expected.to validate_numericality_of(:low).is_greater_than_or_equal_to(0) }
it { is_expected.to validate_numericality_of(:unknown).is_greater_than_or_equal_to(0) }
it { is_expected.to validate_numericality_of(:info).is_greater_than_or_equal_to(0) }
it { is_expected.to define_enum_for(:letter_grade).with_values(%i(a b c d f)) }
end
end
......@@ -507,6 +507,7 @@ project:
- freeze_periods
- webex_teams_service
- build_report_results
- vulnerability_statistic
award_emoji:
- awardable
- user
......
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