Commit b53fd47f authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch '12196-expose-median-and-records-via-api' into 'master'

Expose Cycle Analytics median and records via API

See merge request gitlab-org/gitlab!18991
parents 30b26d73 22c01303
...@@ -6,6 +6,7 @@ module Analytics ...@@ -6,6 +6,7 @@ module Analytics
check_feature_flag Gitlab::Analytics::CYCLE_ANALYTICS_FEATURE_FLAG check_feature_flag Gitlab::Analytics::CYCLE_ANALYTICS_FEATURE_FLAG
before_action :load_group before_action :load_group
before_action :validate_params, only: %i[median records]
def index def index
return render_403 unless can?(current_user, :read_group_cycle_analytics, @group) return render_403 unless can?(current_user, :read_group_cycle_analytics, @group)
...@@ -37,8 +38,45 @@ module Analytics ...@@ -37,8 +38,45 @@ module Analytics
render_stage_service_result(delete_service.execute) render_stage_service_result(delete_service.execute)
end end
def median
return render_403 unless can?(current_user, :read_group_stage, @group)
render json: { value: data_collector.median.seconds }
end
def records
return render_403 unless can?(current_user, :read_group_stage, @group)
render json: data_collector.serialized_records
end
private private
def validate_params
if request_params.invalid?
render(
json: { message: 'Invalid parameters', errors: request_params.errors },
status: :unprocessable_entity
)
end
end
def request_params
@request_params ||= Gitlab::Analytics::CycleAnalytics::RequestParams.new(params.permit(:created_before, :created_after))
end
def data_collector
@data_collector ||= Gitlab::Analytics::CycleAnalytics::DataCollector.new(stage: stage, params: {
current_user: current_user,
from: request_params.created_after,
to: request_params.created_before
})
end
def stage
@stage ||= Analytics::CycleAnalytics::StageFinder.new(parent: @group, stage_id: params[:id]).execute
end
def cycle_analytics_configuration(stages) def cycle_analytics_configuration(stages)
stage_presenters = stages.map { |s| StagePresenter.new(s) } stage_presenters = stages.map { |s| StagePresenter.new(s) }
......
...@@ -8,7 +8,12 @@ namespace :analytics do ...@@ -8,7 +8,12 @@ namespace :analytics do
constraints(::Constraints::FeatureConstrainer.new(Gitlab::Analytics::CYCLE_ANALYTICS_FEATURE_FLAG)) do constraints(::Constraints::FeatureConstrainer.new(Gitlab::Analytics::CYCLE_ANALYTICS_FEATURE_FLAG)) do
resource :cycle_analytics, only: :show resource :cycle_analytics, only: :show
namespace :cycle_analytics do namespace :cycle_analytics do
resources :stages, only: [:index, :create, :update, :destroy] resources :stages, only: [:index, :create, :update, :destroy] do
member do
get :median
get :records
end
end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Analytics
module CycleAnalytics
class RequestParams
include ActiveModel::Model
include ActiveModel::Validations
include ActiveModel::Attributes
attribute :created_after, :date
attribute :created_before, :date
validates :created_after, presence: true
validates :created_before, presence: true
validate :validate_created_before
private
def validate_created_before
return if created_after.nil? || created_before.nil?
errors.add(:created_before, :invalid) if created_after > created_before
end
end
end
end
end
...@@ -158,5 +158,53 @@ describe Analytics::CycleAnalytics::StagesController do ...@@ -158,5 +158,53 @@ describe Analytics::CycleAnalytics::StagesController do
expect(response).to have_gitlab_http_status(:forbidden) expect(response).to have_gitlab_http_status(:forbidden)
end end
end end
describe 'GET `median`' do
subject { get :median, params: params }
before do
params[:created_after] = '2019-01-01'
params[:created_before] = '2020-01-01'
end
it 'succeeds' do
subject
expect(response).to be_successful
expect(response).to match_response_schema('analytics/cycle_analytics/median', dir: 'ee')
end
context 'when params are invalid' do
before do
params[:created_before] = '2018-01-01'
end
it 'renders `unprocessable_entity`' do
subject
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(response).to match_response_schema('analytics/cycle_analytics/validation_error', dir: 'ee')
end
end
include_examples 'group permission check on the controller level'
end
describe 'GET `records`' do
subject { get :records, params: params }
before do
params[:created_after] = '2019-01-01'
params[:created_before] = '2020-01-01'
end
it 'succeeds' do
subject
expect(response).to be_successful
end
include_examples 'group permission check on the controller level'
end
end end
end end
{
"type": "object",
"properties": {
"value": {
"anyOf": [
{ "type": "integer" },
{ "type": "null" }
]
}
},
"required": ["value"],
"additionalProperties": false
}
...@@ -51,7 +51,7 @@ describe Gitlab::Analytics::CycleAnalytics::DataCollector do ...@@ -51,7 +51,7 @@ describe Gitlab::Analytics::CycleAnalytics::DataCollector do
end end
it 'loads serialized records' do it 'loads serialized records' do
items = data_collector.records_fetcher.serialized_records items = data_collector.serialized_records
expect(items.size).to eq(3) expect(items.size).to eq(3)
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Analytics::CycleAnalytics::RequestParams do
let(:params) { { created_after: '2018-01-01', created_before: '2019-01-01' } }
subject { described_class.new(params) }
describe 'validations' do
it 'is valid' do
expect(subject).to be_valid
end
context 'when `created_before` is missing' do
before do
params[:created_before] = nil
end
it 'is invalid' do
expect(subject).not_to be_valid
end
end
context 'when `created_before` is earlier than `created_after`' do
before do
params[:created_before] = '2015-01-01'
end
it 'is invalid' do
expect(subject).not_to be_valid
expect(subject.errors.messages[:created_before]).not_to be_empty
end
end
end
it 'casts `created_after` to date' do
expect(subject.created_after).to be_a_kind_of(Date)
end
it 'casts `created_before` to date' do
expect(subject.created_before).to be_a_kind_of(Date)
end
end
...@@ -12,6 +12,8 @@ module Gitlab ...@@ -12,6 +12,8 @@ module Gitlab
class DataCollector class DataCollector
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
delegate :serialized_records, to: :records_fetcher
def initialize(stage:, params: {}) def initialize(stage:, params: {})
@stage = stage @stage = stage
@params = params @params = params
......
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