Commit 8107d606 authored by Adam Hegyi's avatar Adam Hegyi

Setting up GroupValueStream model

- Associate GroupValueStream with Group and Group Stage.
- Controller actions for creating value streams.
parent 3c3635fc
# 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 (
end_event_label_id bigint,
hidden boolean DEFAULT false 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
......@@ -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;
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 (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
......@@ -16359,6 +16378,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_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.appearances ALTER COLUMN id SET DEFAULT nextval('public.appearances_id_seq'::regclass);
......@@ -17223,6 +17244,9 @@ ALTER TABLE ONLY public.allowed_email_domains
ALTER TABLE ONLY public.analytics_cycle_analytics_group_stages
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
ADD CONSTRAINT analytics_cycle_analytics_project_stages_pkey PRIMARY KEY (id);
......@@ -18558,6 +18582,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_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_project_id ON public.analytics_cycle_analytics_project_stages USING btree (project_id);
......@@ -21225,6 +21253,9 @@ ALTER TABLE ONLY public.ci_variables
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;
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
ADD CONSTRAINT fk_b01280dae4 FOREIGN KEY (forked_from_project_id) REFERENCES public.projects(id) ON DELETE SET NULL;
......@@ -21804,6 +21835,9 @@ ALTER TABLE ONLY public.project_repository_storage_moves
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;
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
ADD CONSTRAINT fk_rails_546bf08d3e FOREIGN KEY (geo_node_id) REFERENCES public.geo_nodes(id) ON DELETE CASCADE;
......@@ -23663,6 +23697,8 @@ COPY "schema_migrations" (version) FROM STDIN;
20200623170000
20200623185440
20200624075411
20200624142107
20200624142207
20200624222443
20200625045442
20200625082258
......@@ -23673,6 +23709,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200629192638
20200630091656
20200630110826
20200701064756
20200701093859
20200701205710
20200702123805
......
# frozen_string_literal: true
module Analytics
class ApplicationController < ApplicationController
class ApplicationController < ::ApplicationController
include RoutableActions
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
validates :group, presence: true
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_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
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 :value_streams, class_name: 'Analytics::CycleAnalytics::GroupValueStream'
has_one :deletion_schedule, class_name: 'GroupDeletionSchedule'
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
get :records
end
end
resources :value_streams, only: [:index, :create]
resource :summary, controller: :summary, only: :show
get '/time_summary' => 'summary#time_summary'
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'
RSpec.describe Analytics::CycleAnalytics::GroupStage do
describe 'associations' do
it { is_expected.to belong_to(:group) }
it { is_expected.to belong_to(:value_stream) }
end
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
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(: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(:allowed_email_domains) }
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