Commit d0422e28 authored by Gosia Ksionek's avatar Gosia Ksionek Committed by James Lopez

Add controllers changes

Add new controllers and specs
parent cb7d9090
...@@ -18,3 +18,5 @@ module CycleAnalyticsParams ...@@ -18,3 +18,5 @@ module CycleAnalyticsParams
end end
end end
end end
CycleAnalyticsParams.prepend_if_ee('EE::CycleAnalyticsParams')
# frozen_string_literal: true
module EE
module CycleAnalyticsParams
extend ::Gitlab::Utils::Override
include ::Gitlab::Utils::StrongMemoize
override :options
def options(params)
strong_memoize(:options) do
super.tap do |options|
options[:branch] = params[:branch_name]
options[:projects] = params[:project_ids] if params[:project_ids]
end
end
end
end
end
# frozen_string_literal: true
class Groups::CycleAnalytics::EventsController < Groups::ApplicationController
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TextHelper
include CycleAnalyticsParams
before_action :authorize_group_cycle_analytics!
def issue
render_events(:issue)
end
def plan
render_events(:plan)
end
def code
render_events(:code)
end
def test
render_events(:test)
end
def review
render_events(:review)
end
def staging
render_events(:staging)
end
def production
render_events(:production)
end
private
def render_events(stage)
respond_to do |format|
format.html
format.json { render json: { events: cycle_analytics[stage].events } }
end
end
def cycle_analytics
@cycle_analytics ||= ::CycleAnalytics::GroupLevel.new(group: group, options: options(cycle_analytics_params))
end
def cycle_analytics_params
return {} unless params[:cycle_analytics].present?
params[:cycle_analytics].permit(:start_date, :branch_name, project_ids: [])
end
def authorize_group_cycle_analytics!
unless can?(current_user, :read_group_cycle_analytics, group)
render_403
end
end
end
# frozen_string_literal: true
class Groups::CycleAnalyticsController < Groups::ApplicationController
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TextHelper
include CycleAnalyticsParams
before_action :whitelist_query_limiting, only: [:show]
before_action :authorize_group_cycle_analytics!
def show
respond_to do |format|
format.json { render json: cycle_analytics_json }
end
end
private
def cycle_analytics_params
return {} unless params[:cycle_analytics].present?
params[:cycle_analytics].permit(:start_date, project_ids: [])
end
def cycle_analytics_json
{
summary: cycle_analytics_stats.summary,
stats: cycle_analytics_stats.stats,
permissions: cycle_analytics_stats.permissions(user: current_user)
}
end
def cycle_analytics_stats
@cycle_analytics_stats ||= ::CycleAnalytics::GroupLevel.new(group: group, options: options(cycle_analytics_params))
end
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42671')
end
def authorize_group_cycle_analytics!
unless can?(current_user, :read_group_cycle_analytics, group)
render_403
end
end
end
...@@ -89,6 +89,7 @@ class License < ApplicationRecord ...@@ -89,6 +89,7 @@ class License < ApplicationRecord
custom_prometheus_metrics custom_prometheus_metrics
required_ci_templates required_ci_templates
project_aliases project_aliases
cycle_analytics_for_groups
] ]
EEP_FEATURES.freeze EEP_FEATURES.freeze
......
...@@ -13,6 +13,10 @@ module EE ...@@ -13,6 +13,10 @@ module EE
@subject.feature_available?(:contribution_analytics) @subject.feature_available?(:contribution_analytics)
end end
condition(:cycle_analytics_available) do
@subject.feature_available?(:cycle_analytics_for_groups)
end
condition(:can_owners_manage_ldap, scope: :global) do condition(:can_owners_manage_ldap, scope: :global) do
::Gitlab::CurrentSettings.allow_group_owners_to_manage_ldap? ::Gitlab::CurrentSettings.allow_group_owners_to_manage_ldap?
end end
...@@ -49,6 +53,9 @@ module EE ...@@ -49,6 +53,9 @@ module EE
rule { can?(:read_group) & contribution_analytics_available } rule { can?(:read_group) & contribution_analytics_available }
.enable :read_group_contribution_analytics .enable :read_group_contribution_analytics
rule { reporter & cycle_analytics_available }
.enable :read_group_cycle_analytics
rule { can?(:read_group) & dependency_proxy_available } rule { can?(:read_group) & dependency_proxy_available }
.enable :read_dependency_proxy .enable :read_dependency_proxy
......
---
title: Add cycle analytics on group level
merge_request: 14627
author:
type: added
...@@ -18,6 +18,19 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -18,6 +18,19 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end end
resource :analytics, only: [:show] resource :analytics, only: [:show]
resource :cycle_analytics, only: [:show]
namespace :cycle_analytics do
scope :events, controller: 'events' do
get :issue
get :plan
get :code
get :test
get :review
get :staging
get :production
end
end
resource :ldap, only: [] do resource :ldap, only: [] do
member do member do
put :sync put :sync
......
# frozen_string_literal: true
require 'spec_helper'
RSpec::Matchers.define :nested_hash_including do |path_to_hash, value|
match { |actual| actual.dig(*path_to_hash) == value }
end
describe Groups::CycleAnalytics::EventsController do
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:user) { create(:user) }
let(:group_service) { instance_double(::CycleAnalytics::GroupLevel) }
let(:events_service) { double }
before do
stub_licensed_features(cycle_analytics_for_groups: true)
sign_in(user)
end
describe 'cycle analytics' do
context 'with proper permission' do
before do
group.add_owner(user)
end
it 'calls service' do
expect(events_service).to receive(:events)
expect(group_service).to receive(:[]).and_return(events_service)
expect(::CycleAnalytics::GroupLevel).to receive(:new).and_return(group_service)
get(:issue,
params: {
group_id: group.name
},
format: :json)
expect(response).to be_success
end
it 'calls service with specific params' do
expect(events_service).to receive(:events)
expect(group_service).to receive(:[]).and_return(events_service)
expect(::CycleAnalytics::GroupLevel).to receive(:new)
.with(nested_hash_including([:options, :projects], [project.id.to_s]))
.and_return(group_service)
get(:issue,
params: {
group_id: group.name,
cycle_analytics: {
project_ids: [project.id]
}
},
format: :json)
expect(response).to be_success
end
end
context 'as guest' do
before do
group.add_guest(user)
end
it 'returns 403' do
get(:issue,
params: {
group_id: group.name
},
format: :json)
expect(response.status).to eq(403)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec::Matchers.define :nested_hash_including do |path_to_hash, value|
match { |actual| actual.dig(*path_to_hash) == value }
end
describe Groups::CycleAnalyticsController do
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:user) { create(:user) }
let(:group_service) { instance_double(::CycleAnalytics::GroupLevel) }
before do
stub_licensed_features(cycle_analytics_for_groups: true)
sign_in(user)
end
describe 'cycle analytics' do
context 'with proper permission' do
before do
group.add_owner(user)
end
it 'calls service' do
expect(group_service).to receive(:summary)
expect(group_service).to receive(:stats)
expect(group_service).to receive(:permissions)
expect(::CycleAnalytics::GroupLevel).to receive(:new).and_return(group_service)
get(:show,
params: {
group_id: group.name
},
format: :json)
expect(response).to be_success
end
it 'calls service with specific params' do
expect(group_service).to receive(:summary)
expect(group_service).to receive(:stats)
expect(group_service).to receive(:permissions)
expect(::CycleAnalytics::GroupLevel).to receive(:new)
.with(nested_hash_including([:options, :projects], [project.id.to_s]))
.and_return(group_service)
get(:show,
params: {
group_id: group.name,
cycle_analytics: {
project_ids: [project.id]
}
},
format: :json)
expect(response).to be_success
end
end
context 'as guest' do
before do
group.add_guest(user)
end
it 'returns 403' do
get(:show,
params: {
group_id: group.name
},
format: :json)
expect(response.status).to eq(403)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe 'cycle analytics events' do
let(:user) { create(:user) }
let(:group) { create(:group)}
let(:project) { create(:project, :repository, namespace: group, public_builds: false) }
let(:issue) { create(:issue, project: project, created_at: 2.days.ago) }
describe 'GET /:namespace/-/cycle_analytics/events/:stage' do
before do
stub_licensed_features(cycle_analytics_for_groups: true)
group.add_developer(user)
project.add_developer(user)
3.times do |count|
Timecop.freeze(Time.now + count.days) do
create_cycle
end
end
deploy_master(user, project)
login_as(user)
end
it 'lists the issue events' do
get group_cycle_analytics_issue_path(group, format: :json)
first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['iid']).to eq(first_issue_iid)
end
it 'lists the plan events' do
get group_cycle_analytics_plan_path(group, format: :json)
first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['iid']).to eq(first_issue_iid)
end
it 'lists the code events' do
get group_cycle_analytics_code_path(group, format: :json)
expect(json_response['events']).not_to be_empty
first_mr_iid = project.merge_requests.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
expect(json_response['events'].first['iid']).to eq(first_mr_iid)
end
it 'lists the test events' do
get group_cycle_analytics_test_path(group, format: :json)
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['date']).not_to be_empty
end
it 'lists the review events' do
get group_cycle_analytics_review_path(group, format: :json)
first_mr_iid = project.merge_requests.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['iid']).to eq(first_mr_iid)
end
it 'lists the staging events' do
get group_cycle_analytics_staging_path(group, format: :json)
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['date']).not_to be_empty
end
it 'lists the production events' do
get group_cycle_analytics_production_path(group, format: :json)
first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['iid']).to eq(first_issue_iid)
end
context 'specific branch' do
it 'lists the test events' do
branch = project.merge_requests.first.source_branch
get group_cycle_analytics_test_path(group, format: :json, branch: branch)
expect(json_response['events']).not_to be_empty
expect(json_response['events'].first['date']).not_to be_empty
end
end
end
def create_cycle
milestone = create(:milestone, project: project)
issue.update(milestone: milestone)
mr = create_merge_request_closing_issue(user, project, issue, commit_message: "References #{issue.to_reference}")
pipeline = create(:ci_empty_pipeline, status: 'created', project: project, ref: mr.source_branch, sha: mr.source_branch_sha, head_pipeline_of: mr)
pipeline.run
create(:ci_build, pipeline: pipeline, status: :success, author: user)
create(:ci_build, pipeline: pipeline, status: :success, author: user)
merge_merge_requests_closing_issue(user, project, issue)
ProcessCommitWorker.new.perform(project.id, user.id, mr.commits.last.to_hash)
end
end
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