summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMałgorzata Ksionek <mksionek@gitlab.com>2019-06-19 22:51:06 +0200
committerMałgorzata Ksionek <mksionek@gitlab.com>2019-07-03 16:45:52 +0200
commite8e7881daeaa7083be871701b1b123a7a4e88740 (patch)
tree7eaaa14a06ec71a79f651abb74d76ff064188d20
parent406b67ca824790a5965b44ac76f87db68037248f (diff)
downloadgitlab-ce-e8e7881daeaa7083be871701b1b123a7a4e88740.tar.gz
First batch of changes regarding cycle analytics fior groups
Changing cycle analytics classes to accept multiple projects at once
-rw-r--r--app/controllers/projects/cycle_analytics/events_controller.rb2
-rw-r--r--app/controllers/projects/cycle_analytics_controller.rb2
-rw-r--r--app/models/cycle_analytics.rb46
-rw-r--r--app/models/cycle_analytics/base.rb33
-rw-r--r--app/models/cycle_analytics/group_level.rb22
-rw-r--r--app/models/cycle_analytics/project_level.rb21
-rw-r--r--app/serializers/analytics_issue_entity.rb6
-rw-r--r--app/serializers/analytics_merge_request_entity.rb2
-rw-r--r--lib/gitlab/cycle_analytics/base_event_fetcher.rb16
-rw-r--r--lib/gitlab/cycle_analytics/base_query.rb4
-rw-r--r--lib/gitlab/cycle_analytics/base_stage.rb14
-rw-r--r--lib/gitlab/cycle_analytics/code_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/issue_event_fetcher.rb5
-rw-r--r--lib/gitlab/cycle_analytics/issue_helper.rb2
-rw-r--r--lib/gitlab/cycle_analytics/metrics_tables.rb4
-rw-r--r--lib/gitlab/cycle_analytics/permissions.rb2
-rw-r--r--lib/gitlab/cycle_analytics/plan_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/plan_helper.rb2
-rw-r--r--lib/gitlab/cycle_analytics/production_event_fetcher.rb2
-rw-r--r--lib/gitlab/cycle_analytics/review_event_fetcher.rb2
-rw-r--r--spec/models/cycle_analytics/project_level_spec.rb (renamed from spec/models/cycle_analytics_spec.rb)4
21 files changed, 123 insertions, 72 deletions
diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb
index fb43356ff10..ae55bbcb60f 100644
--- a/app/controllers/projects/cycle_analytics/events_controller.rb
+++ b/app/controllers/projects/cycle_analytics/events_controller.rb
@@ -50,7 +50,7 @@ module Projects
end
def cycle_analytics
- @cycle_analytics ||= ::CycleAnalytics.new(project, options(events_params))
+ @cycle_analytics ||= ::CycleAnalytics::ProjectLevel.new(project: project, options: options(events_params))
end
def events_params
diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb
index 8c071496ba9..fe89b519a3f 100644
--- a/app/controllers/projects/cycle_analytics_controller.rb
+++ b/app/controllers/projects/cycle_analytics_controller.rb
@@ -9,7 +9,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
before_action :authorize_read_cycle_analytics!
def show
- @cycle_analytics = ::CycleAnalytics.new(@project, options(cycle_analytics_params))
+ @cycle_analytics = ::CycleAnalytics::ProjectLevel.new(project: @project, options: options(cycle_analytics_params))
@cycle_analytics_no_data = @cycle_analytics.no_stats?
diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb
deleted file mode 100644
index d0f5b6970b1..00000000000
--- a/app/models/cycle_analytics.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-class CycleAnalytics
- STAGES = %i[issue plan code test review staging production].freeze
-
- def initialize(project, options)
- @project = project
- @options = options
- end
-
- def all_medians_per_stage
- STAGES.each_with_object({}) do |stage_name, medians_per_stage|
- medians_per_stage[stage_name] = self[stage_name].median
- end
- end
-
- def summary
- @summary ||= ::Gitlab::CycleAnalytics::StageSummary.new(@project,
- from: @options[:from],
- current_user: @options[:current_user]).data
- end
-
- def stats
- @stats ||= stats_per_stage
- end
-
- def no_stats?
- stats.all? { |hash| hash[:value].nil? }
- end
-
- def permissions(user:)
- Gitlab::CycleAnalytics::Permissions.get(user: user, project: @project)
- end
-
- def [](stage_name)
- Gitlab::CycleAnalytics::Stage[stage_name].new(project: @project, options: @options)
- end
-
- private
-
- def stats_per_stage
- STAGES.map do |stage_name|
- self[stage_name].as_json
- end
- end
-end
diff --git a/app/models/cycle_analytics/base.rb b/app/models/cycle_analytics/base.rb
new file mode 100644
index 00000000000..24f6701d47b
--- /dev/null
+++ b/app/models/cycle_analytics/base.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module CycleAnalytics
+ class Base
+ STAGES = %i[issue plan code test review staging production].freeze
+
+ def all_medians_per_stage
+ STAGES.each_with_object({}) do |stage_name, medians_per_stage|
+ medians_per_stage[stage_name] = self[stage_name].median
+ end
+ end
+
+ def stats
+ @stats ||= stats_per_stage
+ end
+
+ def no_stats?
+ stats.all? { |hash| hash[:value].nil? }
+ end
+
+ def [](stage_name)
+ Gitlab::CycleAnalytics::Stage[stage_name].new(projects: @projects, options: @options)
+ end
+
+ private
+
+ def stats_per_stage
+ STAGES.map do |stage_name|
+ self[stage_name].as_json
+ end
+ end
+ end
+end
diff --git a/app/models/cycle_analytics/group_level.rb b/app/models/cycle_analytics/group_level.rb
new file mode 100644
index 00000000000..c48919a61ac
--- /dev/null
+++ b/app/models/cycle_analytics/group_level.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module CycleAnalytics
+ class GroupLevel < Base
+ def initialize(project: nil, projects:, options:)
+ @projects = projects
+ @options = options
+ end
+
+ def summary
+ @summary ||= ::Gitlab::CycleAnalytics::GroupStageSummary.new(@project,
+ from: @options[:from],
+ current_user: @options[:current_user]).data
+ end
+
+ def permissions(user:)
+ STAGES.each_with_object({}) do |stage, obj|
+ obj[stage] = true
+ end
+ end
+ end
+end
diff --git a/app/models/cycle_analytics/project_level.rb b/app/models/cycle_analytics/project_level.rb
new file mode 100644
index 00000000000..64e4352cb64
--- /dev/null
+++ b/app/models/cycle_analytics/project_level.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module CycleAnalytics
+ class ProjectLevel < Base
+ def initialize(project:, projects: nil, options:)
+ @projects = [project]
+ @project = project
+ @options = options
+ end
+
+ def summary
+ @summary ||= ::Gitlab::CycleAnalytics::StageSummary.new(@project,
+ from: @options[:from],
+ current_user: @options[:current_user]).data
+ end
+
+ def permissions(user:)
+ Gitlab::CycleAnalytics::Permissions.get(user: user, project: @project)
+ end
+ end
+end
diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb
index ab15bd0ac7a..6d17b9af3c5 100644
--- a/app/serializers/analytics_issue_entity.rb
+++ b/app/serializers/analytics_issue_entity.rb
@@ -20,12 +20,12 @@ class AnalyticsIssueEntity < Grape::Entity
end
expose :url do |object|
- url_to(:namespace_project_issue, id: object[:iid].to_s)
+ url_to(:namespace_project_issue, object)
end
private
- def url_to(route, id)
- public_send("#{route}_url", request.project.namespace, request.project, id) # rubocop:disable GitlabSecurity/PublicSend
+ def url_to(route, object)
+ public_send("#{route}_url", request.namespace, object[:project_name], object[:iid].to_s) # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb
index b7134da9461..21d7eeb81b0 100644
--- a/app/serializers/analytics_merge_request_entity.rb
+++ b/app/serializers/analytics_merge_request_entity.rb
@@ -4,6 +4,6 @@ class AnalyticsMergeRequestEntity < AnalyticsIssueEntity
expose :state
expose :url do |object|
- url_to(:namespace_project_merge_request, id: object[:iid].to_s)
+ url_to(:namespace_project_merge_request, object)
end
end
diff --git a/lib/gitlab/cycle_analytics/base_event_fetcher.rb b/lib/gitlab/cycle_analytics/base_event_fetcher.rb
index 304d60996a6..21f15fb4965 100644
--- a/lib/gitlab/cycle_analytics/base_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/base_event_fetcher.rb
@@ -9,8 +9,9 @@ module Gitlab
MAX_EVENTS = 50
- def initialize(project:, stage:, options:)
- @project = project
+ def initialize(projects:, stage:, options:)
+ @projects = projects
+ @project = @projects.first
@stage = stage
@options = options
end
@@ -59,13 +60,22 @@ module Gitlab
def allowed_ids
@allowed_ids ||= allowed_ids_finder_class
- .new(@options[:current_user], project_id: @project.id)
+ .new(@options[:current_user], allowed_ids_source)
.execute.where(id: event_result_ids).pluck(:id)
end
def event_result_ids
event_result.map { |event| event['id'] }
end
+
+ def allowed_ids_source
+ { project_id: @project.id }
+ end
+
+ def serialization_context
+ namespace = @group ? @group : @project.namespace
+ { namespace: namespace }
+ end
end
end
end
diff --git a/lib/gitlab/cycle_analytics/base_query.rb b/lib/gitlab/cycle_analytics/base_query.rb
index 36231b187cd..653786b85a7 100644
--- a/lib/gitlab/cycle_analytics/base_query.rb
+++ b/lib/gitlab/cycle_analytics/base_query.rb
@@ -10,12 +10,14 @@ module Gitlab
private
def base_query
- @base_query ||= stage_query(@project.id) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ @base_query ||= stage_query(@projects.map(&:id)) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def stage_query(project_ids)
query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id]))
.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
+ .join(projects_table).on(issue_table[:project_id].eq(projects_table[:id]))
+ .project(projects_table[:name].as("project_name"))
.project(issue_table[:project_id].as("project_id"))
.where(issue_table[:project_id].in(project_ids))
.where(issue_table[:created_at].gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables
diff --git a/lib/gitlab/cycle_analytics/base_stage.rb b/lib/gitlab/cycle_analytics/base_stage.rb
index e2d6a301734..0095a7b344d 100644
--- a/lib/gitlab/cycle_analytics/base_stage.rb
+++ b/lib/gitlab/cycle_analytics/base_stage.rb
@@ -5,8 +5,8 @@ module Gitlab
class BaseStage
include BaseQuery
- def initialize(project:, options:)
- @project = project
+ def initialize(projects:, options:)
+ @projects = projects
@options = options
end
@@ -23,18 +23,18 @@ module Gitlab
end
def median
- BatchLoader.for(@project.id).batch(key: name) do |project_ids, loader|
+ ids = @projects.map(&:id)
+ BatchLoader.for(@projects.first.id).batch(key: name) do |project_ids, loader|
cte_table = Arel::Table.new("cte_table_for_#{name}")
-
# Build a `SELECT` query. We find the first of the `end_time_attrs` that isn't `NULL` (call this end_time).
# Next, we find the first of the start_time_attrs that isn't `NULL` (call this start_time).
# We compute the (end_time - start_time) interval, and give it an alias based on the current
# cycle analytics stage.
interval_query = Arel::Nodes::As.new(cte_table,
- subtract_datetimes(stage_query(project_ids), start_time_attrs, end_time_attrs, name.to_s))
+ subtract_datetimes(stage_query(ids), start_time_attrs, end_time_attrs, name.to_s))
if project_ids.one?
- loader.call(@project.id, median_datetime(cte_table, interval_query, name))
+ loader.call(@projects.first.id, median_datetime(cte_table, interval_query, name))
else
begin
median_datetimes(cte_table, interval_query, name, :project_id)&.each do |project_id, median|
@@ -54,7 +54,7 @@ module Gitlab
private
def event_fetcher
- @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(project: @project,
+ @event_fetcher ||= Gitlab::CycleAnalytics::EventFetcher[name].new(projects: @projects,
stage: name,
options: event_options)
end
diff --git a/lib/gitlab/cycle_analytics/code_event_fetcher.rb b/lib/gitlab/cycle_analytics/code_event_fetcher.rb
index 6c348f1862d..fcc282bf7a6 100644
--- a/lib/gitlab/cycle_analytics/code_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/code_event_fetcher.rb
@@ -20,7 +20,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsMergeRequestSerializer.new(project: @project).represent(event)
+ AnalyticsMergeRequestSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
index 8a870f2e2a3..1465e71fd74 100644
--- a/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/issue_event_fetcher.rb
@@ -10,7 +10,8 @@ module Gitlab
issue_table[:iid],
issue_table[:id],
issue_table[:created_at],
- issue_table[:author_id]]
+ issue_table[:author_id],
+ projects_table[:name]]
super(*args)
end
@@ -18,7 +19,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsIssueSerializer.new(project: @project).represent(event)
+ AnalyticsIssueSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/lib/gitlab/cycle_analytics/issue_helper.rb b/lib/gitlab/cycle_analytics/issue_helper.rb
index c9266341378..bef2854777a 100644
--- a/lib/gitlab/cycle_analytics/issue_helper.rb
+++ b/lib/gitlab/cycle_analytics/issue_helper.rb
@@ -5,6 +5,8 @@ module Gitlab
module IssueHelper
def stage_query(project_ids)
query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
+ .join(projects_table).on(issue_table[:project_id].eq(projects_table[:id]))
+ .project(projects_table[:name].as("project_name"))
.project(issue_table[:project_id].as("project_id"))
.where(issue_table[:project_id].in(project_ids))
.where(issue_table[:created_at].gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables
diff --git a/lib/gitlab/cycle_analytics/metrics_tables.rb b/lib/gitlab/cycle_analytics/metrics_tables.rb
index 3e0302d308d..bccb5542f1a 100644
--- a/lib/gitlab/cycle_analytics/metrics_tables.rb
+++ b/lib/gitlab/cycle_analytics/metrics_tables.rb
@@ -31,6 +31,10 @@ module Gitlab
Issue::Metrics.arel_table
end
+ def projects_table
+ Project.arel_table
+ end
+
def user_table
User.arel_table
end
diff --git a/lib/gitlab/cycle_analytics/permissions.rb b/lib/gitlab/cycle_analytics/permissions.rb
index afefd09b614..03ba98b4dfb 100644
--- a/lib/gitlab/cycle_analytics/permissions.rb
+++ b/lib/gitlab/cycle_analytics/permissions.rb
@@ -23,7 +23,7 @@ module Gitlab
end
def get
- ::CycleAnalytics::STAGES.each do |stage|
+ ::CycleAnalytics::Base::STAGES.each do |stage|
@stage_permission_hash[stage] = authorized_stage?(stage)
end
diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
index d924f956dcd..bad02e00a13 100644
--- a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
@@ -18,7 +18,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsIssueSerializer.new(project: @project).represent(event)
+ AnalyticsIssueSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/lib/gitlab/cycle_analytics/plan_helper.rb b/lib/gitlab/cycle_analytics/plan_helper.rb
index 30fc2ce6d40..6858cf0afe8 100644
--- a/lib/gitlab/cycle_analytics/plan_helper.rb
+++ b/lib/gitlab/cycle_analytics/plan_helper.rb
@@ -5,6 +5,8 @@ module Gitlab
module PlanHelper
def stage_query(project_ids)
query = issue_table.join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
+ .join(projects_table).on(issue_table[:project_id].eq(projects_table[:id]))
+ .project(projects_table[:name].as("project_name"))
.project(issue_table[:project_id].as("project_id"))
.where(issue_table[:project_id].in(project_ids))
.where(issue_table[:created_at].gteq(@options[:from])) # rubocop:disable Gitlab/ModuleWithInstanceVariables
diff --git a/lib/gitlab/cycle_analytics/production_event_fetcher.rb b/lib/gitlab/cycle_analytics/production_event_fetcher.rb
index 6bcbe0412a9..c4b54d06d6e 100644
--- a/lib/gitlab/cycle_analytics/production_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/production_event_fetcher.rb
@@ -18,7 +18,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsIssueSerializer.new(project: @project).represent(event)
+ AnalyticsIssueSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/lib/gitlab/cycle_analytics/review_event_fetcher.rb b/lib/gitlab/cycle_analytics/review_event_fetcher.rb
index b6354b5ffad..4b5d79097b7 100644
--- a/lib/gitlab/cycle_analytics/review_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/review_event_fetcher.rb
@@ -19,7 +19,7 @@ module Gitlab
private
def serialize(event)
- AnalyticsMergeRequestSerializer.new(project: @project).represent(event)
+ AnalyticsMergeRequestSerializer.new(serialization_context).represent(event)
end
def allowed_ids_finder_class
diff --git a/spec/models/cycle_analytics_spec.rb b/spec/models/cycle_analytics/project_level_spec.rb
index 5d8b5b573cf..196306fd52a 100644
--- a/spec/models/cycle_analytics_spec.rb
+++ b/spec/models/cycle_analytics/project_level_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe CycleAnalytics do
+describe CycleAnalytics::ProjectLevel do
let(:project) { create(:project, :repository) }
let(:from_date) { 10.days.ago }
let(:user) { create(:user, :admin) }
@@ -11,7 +11,7 @@ describe CycleAnalytics do
let(:mr) { create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}") }
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr) }
- subject { described_class.new(project, from: from_date) }
+ subject { described_class.new(project: project, options: { from: from_date }) }
describe '#all_medians_per_stage' do
before do