2021-10-27 15:23:28 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module Gitlab
|
|
|
|
module Analytics
|
|
|
|
module CycleAnalytics
|
|
|
|
class RequestParams
|
|
|
|
include ActiveModel::Model
|
|
|
|
include ActiveModel::Validations
|
|
|
|
include ActiveModel::Attributes
|
|
|
|
include Gitlab::Utils::StrongMemoize
|
|
|
|
|
|
|
|
MAX_RANGE_DAYS = 180.days.freeze
|
|
|
|
DEFAULT_DATE_RANGE = 29.days # 30 including Date.today
|
|
|
|
|
|
|
|
STRONG_PARAMS_DEFINITION = [
|
|
|
|
:created_before,
|
|
|
|
:created_after,
|
|
|
|
:author_username,
|
|
|
|
:milestone_title,
|
|
|
|
:sort,
|
|
|
|
:direction,
|
|
|
|
:page,
|
|
|
|
:stage_id,
|
|
|
|
:end_event_filter,
|
|
|
|
label_name: [].freeze,
|
|
|
|
assignee_username: [].freeze,
|
|
|
|
project_ids: [].freeze
|
|
|
|
].freeze
|
|
|
|
|
|
|
|
FINDER_PARAM_NAMES = [
|
|
|
|
:assignee_username,
|
|
|
|
:author_username,
|
|
|
|
:milestone_title,
|
|
|
|
:label_name
|
|
|
|
].freeze
|
|
|
|
|
|
|
|
attr_writer :project_ids
|
|
|
|
|
|
|
|
attribute :created_after, :datetime
|
|
|
|
attribute :created_before, :datetime
|
2023-05-27 22:25:52 +05:30
|
|
|
attribute :namespace
|
2021-10-27 15:23:28 +05:30
|
|
|
attribute :current_user
|
|
|
|
attribute :value_stream
|
|
|
|
attribute :sort
|
|
|
|
attribute :direction
|
|
|
|
attribute :page
|
|
|
|
attribute :stage_id
|
|
|
|
attribute :end_event_filter
|
|
|
|
|
|
|
|
FINDER_PARAM_NAMES.each do |param_name|
|
|
|
|
attribute param_name
|
|
|
|
end
|
|
|
|
|
|
|
|
validates :created_after, presence: true
|
|
|
|
validates :created_before, presence: true
|
|
|
|
|
|
|
|
validate :validate_created_before
|
|
|
|
validate :validate_date_range
|
|
|
|
|
|
|
|
def initialize(params = {})
|
|
|
|
super(params)
|
|
|
|
|
|
|
|
self.created_before = (self.created_before || Time.current).at_end_of_day
|
|
|
|
self.created_after = (created_after || default_created_after).at_beginning_of_day
|
|
|
|
self.end_event_filter ||= Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder::DEFAULT_END_EVENT_FILTER
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_data_collector_params
|
|
|
|
{
|
|
|
|
current_user: current_user,
|
|
|
|
from: created_after,
|
|
|
|
to: created_before,
|
|
|
|
project_ids: project_ids,
|
|
|
|
sort: sort&.to_sym,
|
|
|
|
direction: direction&.to_sym,
|
|
|
|
page: page,
|
2021-12-11 22:18:48 +05:30
|
|
|
end_event_filter: end_event_filter.to_sym,
|
2022-05-07 20:08:51 +05:30
|
|
|
use_aggregated_data_collector: use_aggregated_backend?
|
2021-10-27 15:23:28 +05:30
|
|
|
}.merge(attributes.symbolize_keys.slice(*FINDER_PARAM_NAMES))
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_data_attributes
|
|
|
|
{}.tap do |attrs|
|
|
|
|
attrs[:value_stream] = value_stream_data_attributes.to_json if value_stream
|
|
|
|
attrs[:created_after] = created_after.to_date.iso8601
|
|
|
|
attrs[:created_before] = created_before.to_date.iso8601
|
|
|
|
attrs[:labels] = label_name.to_json if label_name.present?
|
|
|
|
attrs[:assignees] = assignee_username.to_json if assignee_username.present?
|
|
|
|
attrs[:author] = author_username if author_username.present?
|
|
|
|
attrs[:milestone] = milestone_title if milestone_title.present?
|
|
|
|
attrs[:sort] = sort if sort.present?
|
|
|
|
attrs[:direction] = direction if direction.present?
|
|
|
|
attrs[:stage] = stage_data_attributes.to_json if stage_id.present?
|
2023-05-27 22:25:52 +05:30
|
|
|
attrs[:namespace] = namespace_attributes
|
|
|
|
attrs[:enable_tasks_by_type_chart] = 'false'
|
|
|
|
attrs[:default_stages] = Gitlab::Analytics::CycleAnalytics::DefaultStages.all.map do |stage_params|
|
|
|
|
::Analytics::CycleAnalytics::StagePresenter.new(stage_params)
|
|
|
|
end.to_json
|
|
|
|
|
|
|
|
attrs.merge!(foss_project_level_params, resource_paths)
|
2021-10-27 15:23:28 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-05-27 22:25:52 +05:30
|
|
|
def project_ids
|
|
|
|
Array(@project_ids)
|
|
|
|
end
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
private
|
|
|
|
|
2023-05-27 22:25:52 +05:30
|
|
|
delegate :url_helpers, to: Gitlab::Routing
|
|
|
|
|
|
|
|
def foss_project_level_params
|
|
|
|
return {} unless project
|
2022-05-07 20:08:51 +05:30
|
|
|
|
|
|
|
{
|
2023-05-27 22:25:52 +05:30
|
|
|
project_id: project.id,
|
|
|
|
group_path: project.group&.path,
|
|
|
|
request_path: url_helpers.project_cycle_analytics_path(project),
|
|
|
|
full_path: project.full_path
|
2022-05-07 20:08:51 +05:30
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2023-05-27 22:25:52 +05:30
|
|
|
def resource_paths
|
|
|
|
helpers = ActionController::Base.helpers
|
|
|
|
|
|
|
|
{}.tap do |paths|
|
|
|
|
paths[:empty_state_svg_path] = helpers.image_path("illustrations/analytics/cycle-analytics-empty-chart.svg")
|
|
|
|
paths[:no_data_svg_path] = helpers.image_path("illustrations/analytics/cycle-analytics-empty-chart.svg")
|
|
|
|
paths[:no_access_svg_path] = helpers.image_path("illustrations/analytics/no-access.svg")
|
|
|
|
|
|
|
|
if project
|
|
|
|
paths[:milestones_path] = url_helpers.project_milestones_path(project, format: :json)
|
|
|
|
paths[:labels_path] = url_helpers.project_labels_path(project, format: :json)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# FOSS version doesn't use the aggregated VSA backend
|
|
|
|
def use_aggregated_backend?
|
|
|
|
false
|
2022-05-07 20:08:51 +05:30
|
|
|
end
|
|
|
|
|
2023-05-27 22:25:52 +05:30
|
|
|
def namespace_attributes
|
|
|
|
return {} unless project
|
|
|
|
|
2021-10-27 15:23:28 +05:30
|
|
|
{
|
2023-05-27 22:25:52 +05:30
|
|
|
name: project.name,
|
|
|
|
full_path: project.full_path
|
2021-10-27 15:23:28 +05:30
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def value_stream_data_attributes
|
|
|
|
{
|
|
|
|
id: value_stream.id,
|
|
|
|
name: value_stream.name,
|
|
|
|
is_custom: value_stream.custom?
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def stage_data_attributes
|
|
|
|
return unless stage
|
|
|
|
|
|
|
|
{
|
|
|
|
id: stage.id || stage.name,
|
|
|
|
title: stage.name
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate_created_before
|
|
|
|
return if created_after.nil? || created_before.nil?
|
|
|
|
|
|
|
|
errors.add(:created_before, :invalid) if created_after > created_before
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate_date_range
|
|
|
|
return if created_after.nil? || created_before.nil?
|
|
|
|
|
|
|
|
if (created_before - created_after) > MAX_RANGE_DAYS
|
|
|
|
errors.add(:created_after, s_('CycleAnalytics|The given date range is larger than 180 days'))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def default_created_after
|
|
|
|
if created_before
|
|
|
|
(created_before - DEFAULT_DATE_RANGE)
|
|
|
|
else
|
|
|
|
DEFAULT_DATE_RANGE.ago
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def stage
|
|
|
|
return unless value_stream
|
|
|
|
|
|
|
|
strong_memoize(:stage) do
|
2023-05-27 22:25:52 +05:30
|
|
|
::Analytics::CycleAnalytics::StageFinder.new(parent: namespace, stage_id: stage_id).execute if stage_id
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def project
|
|
|
|
strong_memoize(:project) do
|
|
|
|
namespace.project if namespace.is_a?(Namespaces::ProjectNamespace)
|
2021-10-27 15:23:28 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2023-05-27 22:25:52 +05:30
|
|
|
|
|
|
|
Gitlab::Analytics::CycleAnalytics::RequestParams.prepend_mod_with('Gitlab::Analytics::CycleAnalytics::RequestParams')
|