Commit 249020bd authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '35526-introduce-value-stream-model' into 'master'

Setting up GroupValueStream model

See merge request gitlab-org/gitlab!36658
parents 17a5d870 8107d606
# frozen_string_literal: true
class CreateAnalyticsCycleAnalyticsGroupValueStreams < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_analytics_ca_group_value_streams_on_group_id_and_name'
disable_ddl_transaction!
def up
unless table_exists?(:analytics_cycle_analytics_group_value_streams)
with_lock_retries do
create_table :analytics_cycle_analytics_group_value_streams do |t|
t.timestamps_with_timezone
t.references(:group, {
null: false,
index: false,
foreign_key: { to_table: :namespaces, on_delete: :cascade }
})
t.text :name, null: false
t.index [:group_id, :name], unique: true, name: INDEX_NAME
end
end
end
add_text_limit :analytics_cycle_analytics_group_value_streams, :name, 100
end
def down
drop_table :analytics_cycle_analytics_group_value_streams
end
end
# frozen_string_literal: true
class AddGroupValueStreamToCycleAnalyticsGroupStages < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
add_column :analytics_cycle_analytics_group_stages, :group_value_stream_id, :bigint
end
end
def down
with_lock_retries do
remove_column :analytics_cycle_analytics_group_stages, :group_value_stream_id
end
end
end
# frozen_string_literal: true
class AddNotValidForeignKeyToCycleAnalyticsGroupStages < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
CONSTRAINT_NAME = 'fk_analytics_cycle_analytics_group_stages_group_value_stream_id'
INDEX_NAME = 'index_analytics_ca_group_stages_on_value_stream_id'
disable_ddl_transaction!
def up
add_concurrent_index :analytics_cycle_analytics_group_stages, :group_value_stream_id, name: INDEX_NAME
add_foreign_key :analytics_cycle_analytics_group_stages, :analytics_cycle_analytics_group_value_streams,
column: :group_value_stream_id, name: CONSTRAINT_NAME, on_delete: :cascade, validate: false
end
def down
remove_foreign_key_if_exists :analytics_cycle_analytics_group_stages, column: :group_value_stream_id, name: CONSTRAINT_NAME
remove_concurrent_index :analytics_cycle_analytics_group_stages, :group_value_stream_id
end
end
...@@ -8800,7 +8800,8 @@ CREATE TABLE public.analytics_cycle_analytics_group_stages ( ...@@ -8800,7 +8800,8 @@ CREATE TABLE public.analytics_cycle_analytics_group_stages (
end_event_label_id bigint, end_event_label_id bigint,
hidden boolean DEFAULT false NOT NULL, hidden boolean DEFAULT false NOT NULL,
custom boolean DEFAULT true NOT NULL, custom boolean DEFAULT true NOT NULL,
name character varying(255) NOT NULL name character varying(255) NOT NULL,
group_value_stream_id bigint
); );
CREATE SEQUENCE public.analytics_cycle_analytics_group_stages_id_seq CREATE SEQUENCE public.analytics_cycle_analytics_group_stages_id_seq
...@@ -8812,6 +8813,24 @@ CREATE SEQUENCE public.analytics_cycle_analytics_group_stages_id_seq ...@@ -8812,6 +8813,24 @@ CREATE SEQUENCE public.analytics_cycle_analytics_group_stages_id_seq
ALTER SEQUENCE public.analytics_cycle_analytics_group_stages_id_seq OWNED BY public.analytics_cycle_analytics_group_stages.id; ALTER SEQUENCE public.analytics_cycle_analytics_group_stages_id_seq OWNED BY public.analytics_cycle_analytics_group_stages.id;
CREATE TABLE public.analytics_cycle_analytics_group_value_streams (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
group_id bigint NOT NULL,
name text NOT NULL,
CONSTRAINT check_bc1ed5f1f7 CHECK ((char_length(name) <= 100))
);
CREATE SEQUENCE public.analytics_cycle_analytics_group_value_streams_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.analytics_cycle_analytics_group_value_streams_id_seq OWNED BY public.analytics_cycle_analytics_group_value_streams.id;
CREATE TABLE public.analytics_cycle_analytics_project_stages ( CREATE TABLE public.analytics_cycle_analytics_project_stages (
id bigint NOT NULL, id bigint NOT NULL,
created_at timestamp with time zone NOT NULL, created_at timestamp with time zone NOT NULL,
...@@ -16364,6 +16383,8 @@ ALTER TABLE ONLY public.allowed_email_domains ALTER COLUMN id SET DEFAULT nextva ...@@ -16364,6 +16383,8 @@ ALTER TABLE ONLY public.allowed_email_domains ALTER COLUMN id SET DEFAULT nextva
ALTER TABLE ONLY public.analytics_cycle_analytics_group_stages ALTER COLUMN id SET DEFAULT nextval('public.analytics_cycle_analytics_group_stages_id_seq'::regclass); ALTER TABLE ONLY public.analytics_cycle_analytics_group_stages ALTER COLUMN id SET DEFAULT nextval('public.analytics_cycle_analytics_group_stages_id_seq'::regclass);
ALTER TABLE ONLY public.analytics_cycle_analytics_group_value_streams ALTER COLUMN id SET DEFAULT nextval('public.analytics_cycle_analytics_group_value_streams_id_seq'::regclass);
ALTER TABLE ONLY public.analytics_cycle_analytics_project_stages ALTER COLUMN id SET DEFAULT nextval('public.analytics_cycle_analytics_project_stages_id_seq'::regclass); ALTER TABLE ONLY public.analytics_cycle_analytics_project_stages ALTER COLUMN id SET DEFAULT nextval('public.analytics_cycle_analytics_project_stages_id_seq'::regclass);
ALTER TABLE ONLY public.appearances ALTER COLUMN id SET DEFAULT nextval('public.appearances_id_seq'::regclass); ALTER TABLE ONLY public.appearances ALTER COLUMN id SET DEFAULT nextval('public.appearances_id_seq'::regclass);
...@@ -17228,6 +17249,9 @@ ALTER TABLE ONLY public.allowed_email_domains ...@@ -17228,6 +17249,9 @@ ALTER TABLE ONLY public.allowed_email_domains
ALTER TABLE ONLY public.analytics_cycle_analytics_group_stages ALTER TABLE ONLY public.analytics_cycle_analytics_group_stages
ADD CONSTRAINT analytics_cycle_analytics_group_stages_pkey PRIMARY KEY (id); ADD CONSTRAINT analytics_cycle_analytics_group_stages_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.analytics_cycle_analytics_group_value_streams
ADD CONSTRAINT analytics_cycle_analytics_group_value_streams_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.analytics_cycle_analytics_project_stages ALTER TABLE ONLY public.analytics_cycle_analytics_project_stages
ADD CONSTRAINT analytics_cycle_analytics_project_stages_pkey PRIMARY KEY (id); ADD CONSTRAINT analytics_cycle_analytics_project_stages_pkey PRIMARY KEY (id);
...@@ -18563,6 +18587,10 @@ CREATE INDEX index_analytics_ca_group_stages_on_relative_position ON public.anal ...@@ -18563,6 +18587,10 @@ CREATE INDEX index_analytics_ca_group_stages_on_relative_position ON public.anal
CREATE INDEX index_analytics_ca_group_stages_on_start_event_label_id ON public.analytics_cycle_analytics_group_stages USING btree (start_event_label_id); CREATE INDEX index_analytics_ca_group_stages_on_start_event_label_id ON public.analytics_cycle_analytics_group_stages USING btree (start_event_label_id);
CREATE INDEX index_analytics_ca_group_stages_on_value_stream_id ON public.analytics_cycle_analytics_group_stages USING btree (group_value_stream_id);
CREATE UNIQUE INDEX index_analytics_ca_group_value_streams_on_group_id_and_name ON public.analytics_cycle_analytics_group_value_streams USING btree (group_id, name);
CREATE INDEX index_analytics_ca_project_stages_on_end_event_label_id ON public.analytics_cycle_analytics_project_stages USING btree (end_event_label_id); CREATE INDEX index_analytics_ca_project_stages_on_end_event_label_id ON public.analytics_cycle_analytics_project_stages USING btree (end_event_label_id);
CREATE INDEX index_analytics_ca_project_stages_on_project_id ON public.analytics_cycle_analytics_project_stages USING btree (project_id); CREATE INDEX index_analytics_ca_project_stages_on_project_id ON public.analytics_cycle_analytics_project_stages USING btree (project_id);
...@@ -21230,6 +21258,9 @@ ALTER TABLE ONLY public.ci_variables ...@@ -21230,6 +21258,9 @@ ALTER TABLE ONLY public.ci_variables
ALTER TABLE ONLY public.merge_request_metrics ALTER TABLE ONLY public.merge_request_metrics
ADD CONSTRAINT fk_ae440388cc FOREIGN KEY (latest_closed_by_id) REFERENCES public.users(id) ON DELETE SET NULL; ADD CONSTRAINT fk_ae440388cc FOREIGN KEY (latest_closed_by_id) REFERENCES public.users(id) ON DELETE SET NULL;
ALTER TABLE ONLY public.analytics_cycle_analytics_group_stages
ADD CONSTRAINT fk_analytics_cycle_analytics_group_stages_group_value_stream_id FOREIGN KEY (group_value_stream_id) REFERENCES public.analytics_cycle_analytics_group_value_streams(id) ON DELETE CASCADE NOT VALID;
ALTER TABLE ONLY public.fork_network_members ALTER TABLE ONLY public.fork_network_members
ADD CONSTRAINT fk_b01280dae4 FOREIGN KEY (forked_from_project_id) REFERENCES public.projects(id) ON DELETE SET NULL; ADD CONSTRAINT fk_b01280dae4 FOREIGN KEY (forked_from_project_id) REFERENCES public.projects(id) ON DELETE SET NULL;
...@@ -21809,6 +21840,9 @@ ALTER TABLE ONLY public.project_repository_storage_moves ...@@ -21809,6 +21840,9 @@ ALTER TABLE ONLY public.project_repository_storage_moves
ALTER TABLE ONLY public.x509_commit_signatures ALTER TABLE ONLY public.x509_commit_signatures
ADD CONSTRAINT fk_rails_53fe41188f FOREIGN KEY (x509_certificate_id) REFERENCES public.x509_certificates(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_53fe41188f FOREIGN KEY (x509_certificate_id) REFERENCES public.x509_certificates(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.analytics_cycle_analytics_group_value_streams
ADD CONSTRAINT fk_rails_540627381a FOREIGN KEY (group_id) REFERENCES public.namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY public.geo_node_namespace_links ALTER TABLE ONLY public.geo_node_namespace_links
ADD CONSTRAINT fk_rails_546bf08d3e FOREIGN KEY (geo_node_id) REFERENCES public.geo_nodes(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_546bf08d3e FOREIGN KEY (geo_node_id) REFERENCES public.geo_nodes(id) ON DELETE CASCADE;
...@@ -23668,6 +23702,8 @@ COPY "schema_migrations" (version) FROM STDIN; ...@@ -23668,6 +23702,8 @@ COPY "schema_migrations" (version) FROM STDIN;
20200623170000 20200623170000
20200623185440 20200623185440
20200624075411 20200624075411
20200624142107
20200624142207
20200624222443 20200624222443
20200625045442 20200625045442
20200625082258 20200625082258
...@@ -23678,6 +23714,7 @@ COPY "schema_migrations" (version) FROM STDIN; ...@@ -23678,6 +23714,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200629192638 20200629192638
20200630091656 20200630091656
20200630110826 20200630110826
20200701064756
20200701093859 20200701093859
20200701205710 20200701205710
20200702123805 20200702123805
......
# frozen_string_literal: true # frozen_string_literal: true
module Analytics module Analytics
class ApplicationController < ApplicationController class ApplicationController < ::ApplicationController
include RoutableActions include RoutableActions
layout 'analytics' layout 'analytics'
......
# frozen_string_literal: true
class Groups::Analytics::CycleAnalytics::ValueStreamsController < Analytics::ApplicationController
respond_to :json
check_feature_flag Gitlab::Analytics::CYCLE_ANALYTICS_FEATURE_FLAG
before_action :load_group
before_action do
render_403 unless can?(current_user, :read_group_cycle_analytics, @group)
end
def index
render json: Analytics::GroupValueStreamSerializer.new.represent(@group.value_streams)
end
def create
value_stream = @group.value_streams.build(value_stream_params)
if value_stream.save
render json: Analytics::GroupValueStreamSerializer.new.represent(value_stream)
else
render json: { message: 'Invalid parameters', payload: { errors: value_stream.errors } }, status: :unprocessable_entity
end
end
private
def value_stream_params
params.require(:value_stream).permit(:name)
end
end
...@@ -7,6 +7,7 @@ module Analytics ...@@ -7,6 +7,7 @@ module Analytics
validates :group, presence: true validates :group, presence: true
belongs_to :group belongs_to :group
belongs_to :value_stream, class_name: 'Analytics::CycleAnalytics::GroupValueStream', foreign_key: :group_value_stream_id
alias_attribute :parent, :group alias_attribute :parent, :group
alias_attribute :parent_id, :group_id alias_attribute :parent_id, :group_id
......
# frozen_string_literal: true
class Analytics::CycleAnalytics::GroupValueStream < ApplicationRecord
belongs_to :group
has_many :stages, class_name: 'Analytics::CycleAnalytics::GroupStage'
validates :group, :name, presence: true
validates :name, length: { minimum: 3, maximum: 100, allow_nil: false }, uniqueness: { scope: :group_id }
end
...@@ -42,6 +42,7 @@ module EE ...@@ -42,6 +42,7 @@ module EE
has_many :managed_users, class_name: 'User', foreign_key: 'managing_group_id', inverse_of: :managing_group has_many :managed_users, class_name: 'User', foreign_key: 'managing_group_id', inverse_of: :managing_group
has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::GroupStage' has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::GroupStage'
has_many :value_streams, class_name: 'Analytics::CycleAnalytics::GroupValueStream'
has_one :deletion_schedule, class_name: 'GroupDeletionSchedule' has_one :deletion_schedule, class_name: 'GroupDeletionSchedule'
delegate :deleting_user, :marked_for_deletion_on, to: :deletion_schedule, allow_nil: true delegate :deleting_user, :marked_for_deletion_on, to: :deletion_schedule, allow_nil: true
......
# frozen_string_literal: true
module Analytics
class GroupValueStreamEntity < Grape::Entity
expose :name
expose :id
end
end
# frozen_string_literal: true
module Analytics
class GroupValueStreamSerializer < BaseSerializer
entity ::Analytics::GroupValueStreamEntity
end
end
---
title: Setup group level Value Stream DB table
merge_request: 36658
author:
type: added
...@@ -37,6 +37,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -37,6 +37,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
get :records get :records
end end
end end
resources :value_streams, only: [:index, :create]
resource :summary, controller: :summary, only: :show resource :summary, controller: :summary, only: :show
get '/time_summary' => 'summary#time_summary' get '/time_summary' => 'summary#time_summary'
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Groups::Analytics::CycleAnalytics::ValueStreamsController do
let_it_be(:user) { create(:user) }
let_it_be(:group, refind: true) { create(:group) }
let(:params) { { group_id: group } }
let!(:value_stream) { create(:cycle_analytics_group_value_stream, group: group) }
before do
stub_feature_flags(Gitlab::Analytics::CYCLE_ANALYTICS_FEATURE_FLAG => true)
stub_licensed_features(cycle_analytics_for_groups: true)
group.add_maintainer(user)
sign_in(user)
end
describe 'GET #index' do
it 'succeeds' do
get :index, params: params
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('analytics/cycle_analytics/value_streams', dir: 'ee')
end
end
describe 'POST #create' do
context 'with valid params' do
it 'returns a successful 200 response' do
expect do
post :create, params: { group_id: group, value_stream: { name: "busy value stream" } }
end.to change { Analytics::CycleAnalytics::GroupValueStream.count }.by(1)
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'with invalid params' do
it 'returns an unprocessable entity 422 response' do
expect do
post :create, params: { group_id: group, value_stream: { name: '' } }
end.not_to change { Analytics::CycleAnalytics::GroupValueStream.count }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response["message"]).to eq('Invalid parameters')
end
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :cycle_analytics_group_value_stream, class: 'Analytics::CycleAnalytics::GroupValueStream' do
sequence(:name) { |n| "Value Stream ##{n}" }
end
end
{
"type": "object",
"required": ["name", "id"],
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
},
"additionalProperties": false
}
{
"type": "array",
"items": { "$ref": "./value_stream.json" }
}
\ No newline at end of file
...@@ -5,6 +5,7 @@ require 'spec_helper' ...@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Analytics::CycleAnalytics::GroupStage do RSpec.describe Analytics::CycleAnalytics::GroupStage do
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:group) } it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:value_stream) }
end end
it_behaves_like 'cycle analytics stage' do it_behaves_like 'cycle analytics stage' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Analytics::CycleAnalytics::GroupValueStream, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:group) }
it { is_expected.to have_many(:stages) }
end
describe 'validations' do
it { is_expected.to validate_presence_of(:group) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_length_of(:name).is_at_most(100) }
it 'validates uniqueness of name' do
group = create(:group)
create(:cycle_analytics_group_value_stream, name: 'test', group: group)
value_stream = build(:cycle_analytics_group_value_stream, name: 'test', group: group)
expect(value_stream).to be_invalid
expect(value_stream.errors.messages).to eq(name: [I18n.t('errors.messages.taken')])
end
end
end
...@@ -15,6 +15,7 @@ RSpec.describe Group do ...@@ -15,6 +15,7 @@ RSpec.describe Group do
it { is_expected.to belong_to(:file_template_project).class_name('Project').without_validating_presence } it { is_expected.to belong_to(:file_template_project).class_name('Project').without_validating_presence }
it { is_expected.to have_many(:dependency_proxy_blobs) } it { is_expected.to have_many(:dependency_proxy_blobs) }
it { is_expected.to have_many(:cycle_analytics_stages) } it { is_expected.to have_many(:cycle_analytics_stages) }
it { is_expected.to have_many(:value_streams) }
it { is_expected.to have_many(:ip_restrictions) } it { is_expected.to have_many(:ip_restrictions) }
it { is_expected.to have_many(:allowed_email_domains) } it { is_expected.to have_many(:allowed_email_domains) }
it { is_expected.to have_one(:dependency_proxy_setting) } it { is_expected.to have_one(:dependency_proxy_setting) }
......
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