Commit 5bcb540d authored by Pavel Shutsin's avatar Pavel Shutsin Committed by Martin Wortschack

Rework Devops Adoption API

parent 99a8861f
# frozen_string_literal: true
class DropDevopsAdoptionNamespaceUniqueness < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
INDEX_NAME = 'index_analytics_devops_adoption_segments_on_namespace_id'
NEW_INDEX_NAME = 'idx_analytics_devops_adoption_segments_on_namespace_id'
def up
add_concurrent_index :analytics_devops_adoption_segments, :namespace_id, name: NEW_INDEX_NAME
remove_concurrent_index_by_name :analytics_devops_adoption_segments, INDEX_NAME
end
def down
# Clean up duplicated records
execute "DELETE FROM analytics_devops_adoption_segments WHERE id NOT IN (SELECT MIN(id) FROM analytics_devops_adoption_segments GROUP BY namespace_id)"
add_concurrent_index :analytics_devops_adoption_segments, :namespace_id, name: INDEX_NAME, unique: true
remove_concurrent_index_by_name :analytics_devops_adoption_segments, NEW_INDEX_NAME
end
end
ecef2157c20804acbad9d74df27febcf935f7f36920946fac211f3ef8b419f26
\ No newline at end of file
...@@ -22273,6 +22273,8 @@ CREATE INDEX finding_evidences_on_vulnerability_occurrence_id ON vulnerability_f ...@@ -22273,6 +22273,8 @@ CREATE INDEX finding_evidences_on_vulnerability_occurrence_id ON vulnerability_f
CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_finding_links USING btree (vulnerability_occurrence_id); CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_finding_links USING btree (vulnerability_occurrence_id);
CREATE INDEX idx_analytics_devops_adoption_segments_on_namespace_id ON analytics_devops_adoption_segments USING btree (namespace_id);
CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at); CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at);
CREATE INDEX idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id ON award_emoji USING btree (user_id, name, awardable_type, awardable_id); CREATE INDEX idx_award_emoji_on_user_emoji_name_awardable_type_awardable_id ON award_emoji USING btree (user_id, name, awardable_type, awardable_id);
...@@ -22459,8 +22461,6 @@ CREATE UNIQUE INDEX index_analytics_ca_project_value_streams_on_project_id_and_n ...@@ -22459,8 +22461,6 @@ CREATE UNIQUE INDEX index_analytics_ca_project_value_streams_on_project_id_and_n
CREATE INDEX index_analytics_cycle_analytics_group_stages_custom_only ON analytics_cycle_analytics_group_stages USING btree (id) WHERE (custom = true); CREATE INDEX index_analytics_cycle_analytics_group_stages_custom_only ON analytics_cycle_analytics_group_stages USING btree (id) WHERE (custom = true);
CREATE UNIQUE INDEX index_analytics_devops_adoption_segments_on_namespace_id ON analytics_devops_adoption_segments USING btree (namespace_id);
CREATE INDEX index_application_settings_on_custom_project_templates_group_id ON application_settings USING btree (custom_project_templates_group_id); CREATE INDEX index_application_settings_on_custom_project_templates_group_id ON application_settings USING btree (custom_project_templates_group_id);
CREATE INDEX index_application_settings_on_file_template_project_id ON application_settings USING btree (file_template_project_id); CREATE INDEX index_application_settings_on_file_template_project_id ON application_settings USING btree (file_template_project_id);
...@@ -97,8 +97,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -97,8 +97,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="querydevopsadoptionsegmentsdirectdescendantsonly"></a>`directDescendantsOnly` | [`Boolean`](#boolean) | Limits segments to direct descendants of specified parent. | | <a id="querydevopsadoptionsegmentsdisplaynamespaceid"></a>`displayNamespaceId` | [`NamespaceID`](#namespaceid) | Filter by display namespace. |
| <a id="querydevopsadoptionsegmentsparentnamespaceid"></a>`parentNamespaceId` | [`NamespaceID`](#namespaceid) | Filter by ancestor namespace. |
### `Query.echo` ### `Query.echo`
...@@ -770,6 +769,7 @@ Input type: `BulkFindOrCreateDevopsAdoptionSegmentsInput` ...@@ -770,6 +769,7 @@ Input type: `BulkFindOrCreateDevopsAdoptionSegmentsInput`
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | <a id="mutationbulkfindorcreatedevopsadoptionsegmentsclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsdisplaynamespaceid"></a>`displayNamespaceId` | [`NamespaceID`](#namespaceid) | Display namespace ID. |
| <a id="mutationbulkfindorcreatedevopsadoptionsegmentsnamespaceids"></a>`namespaceIds` | [`[NamespaceID!]!`](#namespaceid) | List of Namespace IDs for the segments. | | <a id="mutationbulkfindorcreatedevopsadoptionsegmentsnamespaceids"></a>`namespaceIds` | [`[NamespaceID!]!`](#namespaceid) | List of Namespace IDs for the segments. |
#### Fields #### Fields
...@@ -1105,6 +1105,7 @@ Input type: `CreateDevopsAdoptionSegmentInput` ...@@ -1105,6 +1105,7 @@ Input type: `CreateDevopsAdoptionSegmentInput`
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="mutationcreatedevopsadoptionsegmentclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | <a id="mutationcreatedevopsadoptionsegmentclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcreatedevopsadoptionsegmentdisplaynamespaceid"></a>`displayNamespaceId` | [`NamespaceID`](#namespaceid) | Display namespace ID. |
| <a id="mutationcreatedevopsadoptionsegmentnamespaceid"></a>`namespaceId` | [`NamespaceID!`](#namespaceid) | Namespace ID to set for the segment. | | <a id="mutationcreatedevopsadoptionsegmentnamespaceid"></a>`namespaceId` | [`NamespaceID!`](#namespaceid) | Namespace ID to set for the segment. |
#### Fields #### Fields
...@@ -8248,6 +8249,7 @@ Segment. ...@@ -8248,6 +8249,7 @@ Segment.
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="devopsadoptionsegmentdisplaynamespace"></a>`displayNamespace` | [`Namespace`](#namespace) | Namespace where data should be displayed. |
| <a id="devopsadoptionsegmentid"></a>`id` | [`ID!`](#id) | ID of the segment. | | <a id="devopsadoptionsegmentid"></a>`id` | [`ID!`](#id) | ID of the segment. |
| <a id="devopsadoptionsegmentlatestsnapshot"></a>`latestSnapshot` | [`DevopsAdoptionSnapshot`](#devopsadoptionsnapshot) | The latest adoption metrics for the segment. | | <a id="devopsadoptionsegmentlatestsnapshot"></a>`latestSnapshot` | [`DevopsAdoptionSnapshot`](#devopsadoptionsnapshot) | The latest adoption metrics for the segment. |
| <a id="devopsadoptionsegmentnamespace"></a>`namespace` | [`Namespace`](#namespace) | Namespace which should be calculated. | | <a id="devopsadoptionsegmentnamespace"></a>`namespace` | [`Namespace`](#namespace) | Namespace which should be calculated. |
......
...@@ -76,12 +76,9 @@ export default { ...@@ -76,12 +76,9 @@ export default {
pageInfo: null, pageInfo: null,
}, },
pollingTableData: null, pollingTableData: null,
segmentsQueryVariables: this.isGroup segmentsQueryVariables: {
? { displayNamespaceId: this.isGroup ? this.groupGid : null,
parentNamespaceId: this.groupGid, },
directDescendantsOnly: false,
}
: {},
adoptionTabClicked: false, adoptionTabClicked: false,
devopsScoreTabClicked: false, devopsScoreTabClicked: false,
selectedTab: 0, selectedTab: 0,
......
...@@ -145,6 +145,7 @@ export default { ...@@ -145,6 +145,7 @@ export default {
mutation: bulkFindOrCreateDevopsAdoptionSegmentsMutation, mutation: bulkFindOrCreateDevopsAdoptionSegmentsMutation,
variables: { variables: {
namespaceIds, namespaceIds,
displayNamespaceId: this.groupGid,
}, },
update: (store, { data }) => { update: (store, { data }) => {
const { const {
......
mutation($namespaceIds: [NamespaceID!]!) { mutation($namespaceIds: [NamespaceID!]!, $displayNamespaceId: NamespaceID) {
bulkFindOrCreateDevopsAdoptionSegments(input: { namespaceIds: $namespaceIds }) { bulkFindOrCreateDevopsAdoptionSegments(
input: { namespaceIds: $namespaceIds, displayNamespaceId: $displayNamespaceId }
) {
segments { segments {
id id
latestSnapshot { latestSnapshot {
...@@ -11,6 +13,7 @@ mutation($namespaceIds: [NamespaceID!]!) { ...@@ -11,6 +13,7 @@ mutation($namespaceIds: [NamespaceID!]!) {
deploySucceeded deploySucceeded
securityScanSucceeded securityScanSucceeded
recordedAt recordedAt
codeOwnersUsedCount
} }
namespace { namespace {
fullName fullName
......
query devopsAdoptionSegments($parentNamespaceId: NamespaceID, $directDescendantsOnly: Boolean) { query devopsAdoptionSegments($displayNamespaceId: NamespaceID) {
devopsAdoptionSegments( devopsAdoptionSegments(displayNamespaceId: $displayNamespaceId) {
parentNamespaceId: $parentNamespaceId
directDescendantsOnly: $directDescendantsOnly
) {
nodes { nodes {
id id
latestSnapshot { latestSnapshot {
......
...@@ -12,28 +12,13 @@ module Analytics ...@@ -12,28 +12,13 @@ module Analytics
def execute def execute
scope = ::Analytics::DevopsAdoption::Segment.ordered_by_name scope = ::Analytics::DevopsAdoption::Segment.ordered_by_name
by_display_namespace(scope)
if direct_descendants_only?
scope = scope.for_namespaces(parent_with_direct_descendants)
else
scope = scope.for_parent(parent_namespace) if parent_namespace
end
scope
end end
private private
def parent_with_direct_descendants def by_display_namespace(scope)
parent_namespace ? [parent_namespace] + parent_namespace.children : ::Group.top_most scope.for_display_namespaces(params[:display_namespace])
end
def parent_namespace
params[:parent_namespace]
end
def direct_descendants_only?
params[:direct_descendants_only]
end end
end end
end end
......
...@@ -15,17 +15,22 @@ module Mutations ...@@ -15,17 +15,22 @@ module Mutations
required: true, required: true,
description: 'List of Namespace IDs for the segments.' description: 'List of Namespace IDs for the segments.'
argument :display_namespace_id, ::Types::GlobalIDType[::Namespace],
required: false,
description: 'Display namespace ID.'
field :segments, field :segments,
[::Types::Admin::Analytics::DevopsAdoption::SegmentType], [::Types::Admin::Analytics::DevopsAdoption::SegmentType],
null: true, null: true,
description: 'Created segments after mutation.' description: 'Created segments after mutation.'
def resolve(namespace_ids:, **) def resolve(namespace_ids:, display_namespace_id: nil, **)
namespaces = GlobalID::Locator.locate_many(namespace_ids) namespaces = GlobalID::Locator.locate_many(namespace_ids)
display_namespace = Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(display_namespace_id))
with_authorization_handler do with_authorization_handler do
service = ::Analytics::DevopsAdoption::Segments::BulkFindOrCreateService service = ::Analytics::DevopsAdoption::Segments::BulkFindOrCreateService
.new(current_user: current_user, params: { namespaces: namespaces }) .new(current_user: current_user, params: { namespaces: namespaces, display_namespace: display_namespace })
segments = service.execute.payload.fetch(:segments) segments = service.execute.payload.fetch(:segments)
......
...@@ -15,17 +15,22 @@ module Mutations ...@@ -15,17 +15,22 @@ module Mutations
required: true, required: true,
description: 'Namespace ID to set for the segment.' description: 'Namespace ID to set for the segment.'
argument :display_namespace_id, ::Types::GlobalIDType[::Namespace],
required: false,
description: 'Display namespace ID.'
field :segment, field :segment,
Types::Admin::Analytics::DevopsAdoption::SegmentType, Types::Admin::Analytics::DevopsAdoption::SegmentType,
null: true, null: true,
description: 'The segment after mutation.' description: 'The segment after mutation.'
def resolve(namespace_id:, **) def resolve(namespace_id:, display_namespace_id: nil, **)
namespace = namespace_id.find namespace = Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(namespace_id))
display_namespace = Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(display_namespace_id))
with_authorization_handler do with_authorization_handler do
service = ::Analytics::DevopsAdoption::Segments::CreateService service = ::Analytics::DevopsAdoption::Segments::CreateService
.new(current_user: current_user, params: { namespace: namespace }) .new(current_user: current_user, params: { namespace: namespace, display_namespace: display_namespace })
response = service.execute response = service.execute
......
...@@ -10,28 +10,25 @@ module Resolvers ...@@ -10,28 +10,25 @@ module Resolvers
type Types::Admin::Analytics::DevopsAdoption::SegmentType, null: true type Types::Admin::Analytics::DevopsAdoption::SegmentType, null: true
argument :parent_namespace_id, ::Types::GlobalIDType[::Namespace], argument :display_namespace_id, ::Types::GlobalIDType[::Namespace],
required: false, required: false,
description: 'Filter by ancestor namespace.' description: 'Filter by display namespace.'
argument :direct_descendants_only, ::GraphQL::BOOLEAN_TYPE, def resolve(display_namespace_id: nil, **)
required: false, display_namespace_id = GlobalID.parse(display_namespace_id)
description: 'Limits segments to direct descendants of specified parent.' display_namespace = Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(display_namespace_id))
def resolve(parent_namespace_id: nil, direct_descendants_only: false, **)
parent = GlobalID::Locator.locate(parent_namespace_id) if parent_namespace_id
authorize!(parent) authorize!(display_namespace)
::Analytics::DevopsAdoption::SegmentsFinder.new(current_user, params: { ::Analytics::DevopsAdoption::SegmentsFinder.new(current_user, params: {
parent_namespace: parent, direct_descendants_only: direct_descendants_only display_namespace: display_namespace
}).execute }).execute
end end
private private
def authorize!(parent) def authorize!(display_namespace)
parent ? authorize_with_namespace!(parent) : authorize_global! display_namespace ? authorize_with_namespace!(display_namespace) : authorize_global!
end end
def authorize_global! def authorize_global!
...@@ -40,8 +37,8 @@ module Resolvers ...@@ -40,8 +37,8 @@ module Resolvers
end end
end end
def authorize_with_namespace!(parent) def authorize_with_namespace!(display_namespace)
unless can?(current_user, :view_group_devops_adoption, parent) unless can?(current_user, :view_group_devops_adoption, display_namespace)
raise_resource_not_available_error! raise_resource_not_available_error!
end end
end end
......
...@@ -15,6 +15,9 @@ module Types ...@@ -15,6 +15,9 @@ module Types
field :namespace, Types::NamespaceType, null: true, field :namespace, Types::NamespaceType, null: true,
description: 'Namespace which should be calculated.' description: 'Namespace which should be calculated.'
field :display_namespace, Types::NamespaceType, null: true,
description: 'Namespace where data should be displayed.'
field :latest_snapshot, SnapshotType, null: true, field :latest_snapshot, SnapshotType, null: true,
description: 'The latest adoption metrics for the segment.' description: 'The latest adoption metrics for the segment.'
......
...@@ -11,6 +11,7 @@ class Analytics::DevopsAdoption::Segment < ApplicationRecord ...@@ -11,6 +11,7 @@ class Analytics::DevopsAdoption::Segment < ApplicationRecord
validates :namespace, uniqueness: { scope: :display_namespace_id }, presence: true validates :namespace, uniqueness: { scope: :display_namespace_id }, presence: true
scope :ordered_by_name, -> { includes(:namespace).order('"namespaces"."name" ASC') } scope :ordered_by_name, -> { includes(:namespace).order('"namespaces"."name" ASC') }
scope :for_display_namespaces, -> (namespaces) { where(display_namespace_id: namespaces) }
scope :for_namespaces, -> (namespaces) { where(namespace_id: namespaces) } scope :for_namespaces, -> (namespaces) { where(namespace_id: namespaces) }
scope :for_parent, -> (namespace) { for_namespaces(namespace.self_and_descendants) } scope :for_parent, -> (namespace) { for_namespaces(namespace.self_and_descendants) }
......
...@@ -30,7 +30,7 @@ module Analytics ...@@ -30,7 +30,7 @@ module Analytics
def services def services
@services ||= params[:namespaces].map do |namespace| @services ||= params[:namespaces].map do |namespace|
FindOrCreateService.new(current_user: current_user, FindOrCreateService.new(current_user: current_user,
params: { namespace: namespace, display_namespace: namespace }) params: { namespace: namespace, display_namespace: params[:display_namespace] })
end end
end end
end end
......
...@@ -10,6 +10,22 @@ module Analytics ...@@ -10,6 +10,22 @@ module Analytics
unless can?(current_user, :manage_devops_adoption_segments, namespace) unless can?(current_user, :manage_devops_adoption_segments, namespace)
raise AuthorizationError.new(self, 'Forbidden') raise AuthorizationError.new(self, 'Forbidden')
end end
unless can?(current_user, :manage_devops_adoption_segments, display_namespace || :global)
raise AuthorizationError.new(self, 'Forbidden')
end
end
private
attr_reader :current_user, :params
def namespace
params[:namespace]
end
def display_namespace
params[:display_namespace]
end end
end end
end end
......
...@@ -15,7 +15,7 @@ module Analytics ...@@ -15,7 +15,7 @@ module Analytics
def execute def execute
authorize! authorize!
segment.assign_attributes(namespace: namespace, display_namespace: namespace) segment.assign_attributes(namespace: namespace, display_namespace: display_namespace)
if segment.save if segment.save
Analytics::DevopsAdoption::CreateSnapshotWorker.perform_async(segment.id) Analytics::DevopsAdoption::CreateSnapshotWorker.perform_async(segment.id)
...@@ -28,15 +28,11 @@ module Analytics ...@@ -28,15 +28,11 @@ module Analytics
private private
attr_reader :segment, :params, :current_user attr_reader :segment
def response_payload def response_payload
{ segment: segment } { segment: segment }
end end
def namespace
params[:namespace]
end
end end
end end
end end
......
...@@ -25,15 +25,13 @@ module Analytics ...@@ -25,15 +25,13 @@ module Analytics
private private
attr_reader :segment, :current_user attr_reader :segment
delegate :namespace, :display_namespace, to: :segment
def response_payload def response_payload
{ segment: segment } { segment: segment }
end end
def namespace
segment.namespace
end
end end
end end
end end
......
...@@ -6,6 +6,8 @@ module Analytics ...@@ -6,6 +6,8 @@ module Analytics
class FindOrCreateService class FindOrCreateService
include CommonMethods include CommonMethods
delegate :authorize!, to: :create_service
def initialize(params: {}, current_user:) def initialize(params: {}, current_user:)
@params = params @params = params
@current_user = current_user @current_user = current_user
...@@ -15,7 +17,7 @@ module Analytics ...@@ -15,7 +17,7 @@ module Analytics
def execute def execute
authorize! authorize!
segment = Analytics::DevopsAdoption::Segment.find_by(namespace_id: namespace.id, display_namespace_id: namespace.id) segment = Analytics::DevopsAdoption::Segment.find_by(namespace_id: namespace.id, display_namespace_id: display_namespace&.id)
if segment if segment
ServiceResponse.success(payload: { segment: segment }) ServiceResponse.success(payload: { segment: segment })
...@@ -31,12 +33,6 @@ module Analytics ...@@ -31,12 +33,6 @@ module Analytics
private private
attr_reader :params, :current_user
def namespace
params[:namespace]
end
def create_service def create_service
@create_service ||= CreateService.new(current_user: current_user, params: params) @create_service ||= CreateService.new(current_user: current_user, params: params)
end end
......
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
FactoryBot.define do FactoryBot.define do
factory :devops_adoption_segment, class: 'Analytics::DevopsAdoption::Segment' do factory :devops_adoption_segment, class: 'Analytics::DevopsAdoption::Segment' do
association :namespace, factory: :group association :namespace, factory: :group
association :display_namespace, factory: :group
after(:build) do |instance|
instance.display_namespace = instance.namespace
end
end end
end end
...@@ -11,52 +11,27 @@ RSpec.describe Analytics::DevopsAdoption::SegmentsFinder do ...@@ -11,52 +11,27 @@ RSpec.describe Analytics::DevopsAdoption::SegmentsFinder do
describe '#execute' do describe '#execute' do
let_it_be(:root_group_1) { create(:group, name: 'bbb') } let_it_be(:root_group_1) { create(:group, name: 'bbb') }
let_it_be(:root_group_2) { create(:group, name: 'aaa') }
let_it_be(:segment_1) { create(:devops_adoption_segment, namespace: root_group_1) } let_it_be(:segment_1) { create(:devops_adoption_segment, namespace: root_group_1, display_namespace: nil) }
let_it_be(:segment_2) { create(:devops_adoption_segment, namespace: root_group_2) } let_it_be(:segment_2) { create(:devops_adoption_segment, namespace: root_group_1, display_namespace: root_group_1) }
let_it_be(:direct_subgroup) { create(:group, name: 'ccc', parent: root_group_1) } let_it_be(:segment_3) { create(:devops_adoption_segment) }
let_it_be(:direct_subgroup_segment) do
create(:devops_adoption_segment, namespace: direct_subgroup)
end
let_it_be(:indirect_subgroup) { create(:group, name: 'ddd', parent: direct_subgroup) }
let_it_be(:indirect_subgroup_segment) do
create(:devops_adoption_segment, namespace: indirect_subgroup)
end
before do before do
stub_licensed_features(instance_level_devops_adoption: true) stub_licensed_features(instance_level_devops_adoption: true)
stub_licensed_features(group_level_devops_adoption: true) stub_licensed_features(group_level_devops_adoption: true)
end end
context 'for instance level' do context 'with display_namespace provided' do
it 'returns segments ordered by name' do let(:params) { super().merge(display_namespace: root_group_1) }
expect(finder_segments).to eq([segment_2, segment_1, direct_subgroup_segment, indirect_subgroup_segment])
end
context 'with direct_descendants_only' do
let(:params) { super().merge(direct_descendants_only: true) }
it 'returns direct descendants only' do it 'returns segments with given display namespace' do
expect(finder_segments).to eq([segment_2, segment_1]) expect(finder_segments).to eq([segment_2])
end
end end
end end
context 'for group level' do context 'without display_namespace provided' do
let(:params) { super().merge(parent_namespace: segment_1.namespace) } it 'returns all namespace without display_namespace' do
expect(finder_segments).to eq([segment_1])
it 'returns segments scoped to given namespace ordered by name' do
expect(finder_segments).to eq([segment_1, direct_subgroup_segment, indirect_subgroup_segment])
end
context 'with direct_descendants_only' do
let(:params) { super().merge(direct_descendants_only: true) }
it 'returns direct descendants only' do
expect(finder_segments).to eq([segment_1, direct_subgroup_segment])
end
end end
end end
end end
......
...@@ -343,8 +343,7 @@ describe('DevopsAdoptionApp', () => { ...@@ -343,8 +343,7 @@ describe('DevopsAdoptionApp', () => {
expect.anything(), expect.anything(),
[devopsAdoptionSegmentsData.nodes[0]], [devopsAdoptionSegmentsData.nodes[0]],
{ {
parentNamespaceId: groupGid, displayNamespaceId: groupGid,
directDescendantsOnly: false,
}, },
); );
}); });
......
...@@ -302,7 +302,10 @@ describe('DevopsAdoptionSegmentModal', () => { ...@@ -302,7 +302,10 @@ describe('DevopsAdoptionSegmentModal', () => {
if (expectedAddGroupGids.length) { if (expectedAddGroupGids.length) {
it('submits the correct add request variables', () => { it('submits the correct add request variables', () => {
expect(mutate).toHaveBeenCalledWith({ namespaceIds: expectedAddGroupGids }); expect(mutate).toHaveBeenCalledWith({
displayNamespaceId: null,
namespaceIds: expectedAddGroupGids,
});
}); });
it('emits segmentsAdded with the correct variables', () => { it('emits segmentsAdded with the correct variables', () => {
......
...@@ -19,18 +19,10 @@ RSpec.describe Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver do ...@@ -19,18 +19,10 @@ RSpec.describe Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:root_group_1) { create(:group, name: 'bbb') } let_it_be(:root_group_1) { create(:group, name: 'bbb') }
let_it_be(:root_group_2) { create(:group, name: 'aaa') } let_it_be(:root_group_2) { create(:group, name: 'aaa') }
let_it_be(:segment_1) { create(:devops_adoption_segment, namespace: root_group_1, display_namespace: root_group_1) }
let_it_be(:segment_1) { create(:devops_adoption_segment, namespace: root_group_1) } let_it_be(:segment_2) { create(:devops_adoption_segment, namespace: root_group_1, display_namespace: nil) }
let_it_be(:segment_2) { create(:devops_adoption_segment, namespace: root_group_2) } let_it_be(:segment_3) { create(:devops_adoption_segment, namespace: root_group_2, display_namespace: root_group_2) }
let_it_be(:direct_subgroup) { create(:group, name: 'ccc', parent: root_group_1) } let_it_be(:segment_4) { create(:devops_adoption_segment, namespace: root_group_2, display_namespace: nil) }
let_it_be(:direct_subgroup_segment) do
create(:devops_adoption_segment, namespace: direct_subgroup)
end
let_it_be(:indirect_subgroup) { create(:group, name: 'ddd', parent: direct_subgroup) }
let_it_be(:indirect_subgroup_segment) do
create(:devops_adoption_segment, namespace: indirect_subgroup)
end
subject(:resolved_segments) { resolve_segments(params, { current_user: current_user }) } subject(:resolved_segments) { resolve_segments(params, { current_user: current_user }) }
...@@ -40,16 +32,8 @@ RSpec.describe Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver do ...@@ -40,16 +32,8 @@ RSpec.describe Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver do
let(:current_user) { admin_user } let(:current_user) { admin_user }
context 'as an admin user' do context 'as an admin user' do
it 'returns segments for all groups, ordered by name' do it 'returns segments for all groups without display_namespace' do
expect(resolved_segments).to eq([segment_2, segment_1, direct_subgroup_segment, indirect_subgroup_segment]) expect(resolved_segments).to match_array([segment_2, segment_4])
end
context 'with direct_descendants_only' do
let(:params) { super().merge(direct_descendants_only: true) }
it 'returns segments for root groups, ordered by name' do
expect(resolved_segments).to eq([segment_2, segment_1])
end
end end
end end
...@@ -73,7 +57,7 @@ RSpec.describe Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver do ...@@ -73,7 +57,7 @@ RSpec.describe Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver do
end end
context 'for group level' do context 'for group level' do
let(:params) { { parent_namespace_id: root_group_1.to_gid.to_s } } let(:params) { { display_namespace_id: root_group_1.to_gid.to_s } }
let(:current_user) { user } let(:current_user) { user }
context 'for reporter+' do context 'for reporter+' do
...@@ -82,15 +66,7 @@ RSpec.describe Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver do ...@@ -82,15 +66,7 @@ RSpec.describe Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver do
end end
it 'returns segments for given parent group and its descendants' do it 'returns segments for given parent group and its descendants' do
expect(resolved_segments).to eq([segment_1, direct_subgroup_segment, indirect_subgroup_segment]) expect(resolved_segments).to eq([segment_1])
end
context 'with direct_descendants_only' do
let(:params) { super().merge(direct_descendants_only: true) }
it 'returns segments for given parent group and its direct descendants' do
expect(resolved_segments).to eq([segment_1, direct_subgroup_segment])
end
end end
end end
......
...@@ -42,6 +42,19 @@ RSpec.describe Analytics::DevopsAdoption::Segment, type: :model do ...@@ -42,6 +42,19 @@ RSpec.describe Analytics::DevopsAdoption::Segment, type: :model do
end end
end end
describe '.for_display_namespaces' do
subject(:segments) { described_class.for_display_namespaces(namespaces) }
let_it_be(:segment1) { create(:devops_adoption_segment) }
let_it_be(:segment2) { create(:devops_adoption_segment) }
let_it_be(:segment3) { create(:devops_adoption_segment) }
let_it_be(:namespaces) { [segment1.display_namespace, segment2.display_namespace]}
it 'selects segments for given namespaces only' do
expect(segments).to match_array([segment1, segment2])
end
end
describe '.for_parent' do describe '.for_parent' do
let_it_be(:group) { create :group } let_it_be(:group) { create :group }
let_it_be(:subgroup) { create :group, parent: group } let_it_be(:subgroup) { create :group, parent: group }
......
...@@ -9,7 +9,7 @@ RSpec.describe 'DevopsAdoptionSegments' do ...@@ -9,7 +9,7 @@ RSpec.describe 'DevopsAdoptionSegments' do
let_it_be(:group) { create(:group, name: 'my-group') } let_it_be(:group) { create(:group, name: 'my-group') }
let_it_be(:segment) do let_it_be(:segment) do
create(:devops_adoption_segment, namespace: group) create(:devops_adoption_segment, namespace: group, display_namespace: group)
end end
let_it_be(:snapshot) do let_it_be(:snapshot) do
...@@ -17,12 +17,15 @@ RSpec.describe 'DevopsAdoptionSegments' do ...@@ -17,12 +17,15 @@ RSpec.describe 'DevopsAdoptionSegments' do
end end
let(:query) do let(:query) do
graphql_query_for(:devopsAdoptionSegments, {}, %( graphql_query_for(:devopsAdoptionSegments, { display_namespace_id: group.to_gid.to_s }, %(
nodes { nodes {
id id
namespace { namespace {
name name
} }
displayNamespace {
name
}
latestSnapshot { latestSnapshot {
issueOpened issueOpened
mergeRequestOpened mergeRequestOpened
...@@ -42,6 +45,7 @@ RSpec.describe 'DevopsAdoptionSegments' do ...@@ -42,6 +45,7 @@ RSpec.describe 'DevopsAdoptionSegments' do
{ {
'id' => segment.to_gid.to_s, 'id' => segment.to_gid.to_s,
'namespace' => { 'name' => group.name }, 'namespace' => { 'name' => group.name },
'displayNamespace' => { 'name' => group.name },
'latestSnapshot' => { 'latestSnapshot' => {
'mergeRequestOpened' => false, 'mergeRequestOpened' => false,
'issueOpened' => true 'issueOpened' => true
......
...@@ -5,22 +5,25 @@ require 'spec_helper' ...@@ -5,22 +5,25 @@ require 'spec_helper'
RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::BulkFindOrCreate do RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::BulkFindOrCreate do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:group) { create(:group, name: 'aaaa') } let_it_be(:display_group) { create(:group, name: 'dddd') }
let_it_be(:group2) { create(:group, name: 'bbbb') } let_it_be(:group) { create(:group, name: 'aaaa', parent: display_group) }
let_it_be(:group3) { create(:group, name: 'cccc') } let_it_be(:group2) { create(:group, name: 'bbbb', parent: display_group) }
let_it_be(:group3) { create(:group, name: 'cccc', parent: display_group) }
let_it_be(:reporter) do let_it_be(:reporter) do
create(:user).tap do |u| create(:user).tap do |u|
display_group.add_reporter(u)
group.add_reporter(u) group.add_reporter(u)
group2.add_reporter(u) group2.add_reporter(u)
group3.add_reporter(u) group3.add_reporter(u)
end end
end end
let_it_be(:existing_segment) { create :devops_adoption_segment, namespace: group3 } let_it_be(:existing_segment) { create :devops_adoption_segment, namespace: group3, display_namespace: display_group }
let(:current_user) { reporter } let(:current_user) { reporter }
let(:variables) { { namespace_ids: [group.to_gid.to_s, group2.to_gid.to_s, group3.to_gid.to_s] } } let(:variables) { { namespace_ids: [group.to_gid.to_s, group2.to_gid.to_s, group3.to_gid.to_s], display_namespace_id: display_group.to_gid.to_s } }
let(:mutation) do let(:mutation) do
graphql_mutation(:bulk_find_or_create_devops_adoption_segments, variables) do graphql_mutation(:bulk_find_or_create_devops_adoption_segments, variables) do
...@@ -30,7 +33,9 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::BulkFindOrCreate ...@@ -30,7 +33,9 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::BulkFindOrCreate
segments { segments {
id id
namespace { namespace {
id name
}
displayNamespace {
name name
} }
} }
...@@ -67,6 +72,7 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::BulkFindOrCreate ...@@ -67,6 +72,7 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::BulkFindOrCreate
segments = mutation_response['segments'] segments = mutation_response['segments']
expect(segments.map { |s| s['namespace']['name'] }).to match_array(%w[aaaa bbbb cccc]) expect(segments.map { |s| s['namespace']['name'] }).to match_array(%w[aaaa bbbb cccc])
expect(segments.map { |s| s['displayNamespace']['name'] }).to match_array(%w[dddd dddd dddd])
expect(segments.map { |s| s['id'] }).to include(existing_segment.to_gid.to_s) expect(segments.map { |s| s['id'] }).to include(existing_segment.to_gid.to_s)
expect(::Analytics::DevopsAdoption::Segment.joins(:namespace) expect(::Analytics::DevopsAdoption::Segment.joins(:namespace)
.where(namespaces: { name: %w[aaaa bbbb cccc] }).count).to eq(3) .where(namespaces: { name: %w[aaaa bbbb cccc] }).count).to eq(3)
......
...@@ -5,11 +5,19 @@ require 'spec_helper' ...@@ -5,11 +5,19 @@ require 'spec_helper'
RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Create do RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Create do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:group) { create(:group, name: 'bbbb') } let_it_be(:display_group) { create(:group, name: 'aaaa') }
let_it_be(:reporter) { create(:user).tap { |u| group.add_reporter(u) } } let_it_be(:group) { create(:group, name: 'bbbb', parent: display_group) }
let_it_be(:reporter) do
create(:user).tap do |u|
group.add_reporter(u)
display_group.add_reporter(u)
end
end
let(:current_user) { reporter } let(:current_user) { reporter }
let(:variables) { { namespace_id: group.to_gid.to_s } } let(:variables) { { namespace_id: group.to_gid.to_s, display_namespace_id: display_group.to_gid.to_s } }
let(:mutation) do let(:mutation) do
graphql_mutation(:create_devops_adoption_segment, variables) do graphql_mutation(:create_devops_adoption_segment, variables) do
...@@ -19,7 +27,9 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Create do ...@@ -19,7 +27,9 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Create do
segment { segment {
id id
namespace { namespace {
id name
}
displayNamespace {
name name
} }
} }
...@@ -49,13 +59,14 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Create do ...@@ -49,13 +59,14 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Create do
it_behaves_like 'a mutation that returns a top-level access error' it_behaves_like 'a mutation that returns a top-level access error'
end end
it 'creates the segment with the group' do it 'creates the segment with the group', :aggregate_failures do
post_graphql_mutation(mutation, current_user: current_user) post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['errors']).to be_empty expect(mutation_response['errors']).to be_empty
segment = mutation_response['segment'] segment = mutation_response['segment']
expect(segment['namespace']['name']).to eq('bbbb') expect(segment['namespace']['name']).to eq('bbbb')
expect(segment['displayNamespace']['name']).to eq('aaaa')
expect(::Analytics::DevopsAdoption::Segment.joins(:namespace).where(namespaces: { name: 'bbbb' }).count).to eq(1) expect(::Analytics::DevopsAdoption::Segment.joins(:namespace).where(namespaces: { name: 'bbbb' }).count).to eq(1)
end end
end end
...@@ -5,10 +5,17 @@ require 'spec_helper' ...@@ -5,10 +5,17 @@ require 'spec_helper'
RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Delete do RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Delete do
include GraphqlHelpers include GraphqlHelpers
let_it_be(:group) { create :group } let_it_be(:display_group) { create :group }
let_it_be(:reporter) { create(:user).tap { |u| group.add_reporter(u) } } let_it_be(:group) { create :group, parent: display_group }
let_it_be(:reporter) do
create(:user).tap do |u|
display_group.add_reporter(u)
group.add_reporter(u)
end
end
let(:current_user) { reporter } let(:current_user) { reporter }
let!(:segment) { create(:devops_adoption_segment, namespace: group) } let!(:segment) { create(:devops_adoption_segment, namespace: group, display_namespace: display_group) }
let(:variables) { { id: segment.to_gid.to_s } } let(:variables) { { id: segment.to_gid.to_s } }
...@@ -58,7 +65,9 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Delete do ...@@ -58,7 +65,9 @@ RSpec.describe Mutations::Analytics::DevopsAdoption::Segments::Delete do
before do before do
segment2.namespace.add_reporter(current_user) segment2.namespace.add_reporter(current_user)
segment2.display_namespace.add_reporter(current_user)
segment3.namespace.add_reporter(current_user) segment3.namespace.add_reporter(current_user)
segment3.display_namespace.add_reporter(current_user)
end end
it 'deletes the segments specified for deletion' do it 'deletes the segments specified for deletion' do
......
...@@ -41,10 +41,18 @@ RSpec.describe Analytics::DevopsAdoption::Segments::BulkDeleteService do ...@@ -41,10 +41,18 @@ RSpec.describe Analytics::DevopsAdoption::Segments::BulkDeleteService do
.with(current_user, :manage_devops_adoption_segments, group) .with(current_user, :manage_devops_adoption_segments, group)
.at_least(1) .at_least(1)
.and_return(true) .and_return(true)
expect(::Ability).to receive(:allowed?)
.with(current_user, :manage_devops_adoption_segments, segment.display_namespace)
.at_least(1)
.and_return(true)
expect(::Ability).to receive(:allowed?) expect(::Ability).to receive(:allowed?)
.with(current_user, :manage_devops_adoption_segments, segment2.namespace) .with(current_user, :manage_devops_adoption_segments, segment2.namespace)
.at_least(1) .at_least(1)
.and_return(true) .and_return(true)
expect(::Ability).to receive(:allowed?)
.with(current_user, :manage_devops_adoption_segments, segment2.display_namespace)
.at_least(1)
.and_return(true)
response response
end end
......
...@@ -5,17 +5,20 @@ require 'spec_helper' ...@@ -5,17 +5,20 @@ require 'spec_helper'
RSpec.describe Analytics::DevopsAdoption::Segments::BulkFindOrCreateService do RSpec.describe Analytics::DevopsAdoption::Segments::BulkFindOrCreateService do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:group2) { create(:group) } let_it_be(:group2) { create(:group) }
let_it_be(:display_group) { create(:group) }
let_it_be(:reporter) do let_it_be(:reporter) do
create(:user).tap do |u| create(:user).tap do |u|
group.add_reporter(u) group.add_reporter(u)
group2.add_reporter(u) group2.add_reporter(u)
display_group.add_reporter(u)
end end
end end
let_it_be(:segment) { create :devops_adoption_segment, namespace: group } let_it_be(:segment) { create :devops_adoption_segment, namespace: group, display_namespace: display_group }
let(:current_user) { reporter } let(:current_user) { reporter }
let(:params) { { namespaces: [group, group2] } } let(:params) { { namespaces: [group, group2], display_namespace: display_group } }
subject(:response) { described_class.new(params: params, current_user: current_user).execute } subject(:response) { described_class.new(params: params, current_user: current_user).execute }
...@@ -23,7 +26,7 @@ RSpec.describe Analytics::DevopsAdoption::Segments::BulkFindOrCreateService do ...@@ -23,7 +26,7 @@ RSpec.describe Analytics::DevopsAdoption::Segments::BulkFindOrCreateService do
stub_licensed_features(group_level_devops_adoption: true, instance_level_devops_adoption: true) stub_licensed_features(group_level_devops_adoption: true, instance_level_devops_adoption: true)
end end
it 'authorizes for manage_devops_adoption' do it 'authorizes for manage_devops_adoption', :aggregate_failures do
expect(::Ability).to receive(:allowed?) expect(::Ability).to receive(:allowed?)
.with(current_user, :manage_devops_adoption_segments, group) .with(current_user, :manage_devops_adoption_segments, group)
.at_least(1) .at_least(1)
...@@ -32,6 +35,10 @@ RSpec.describe Analytics::DevopsAdoption::Segments::BulkFindOrCreateService do ...@@ -32,6 +35,10 @@ RSpec.describe Analytics::DevopsAdoption::Segments::BulkFindOrCreateService do
.with(current_user, :manage_devops_adoption_segments, group2) .with(current_user, :manage_devops_adoption_segments, group2)
.at_least(1) .at_least(1)
.and_return(true) .and_return(true)
expect(::Ability).to receive(:allowed?)
.with(current_user, :manage_devops_adoption_segments, display_group)
.at_least(2)
.and_return(true)
response response
end end
......
...@@ -4,11 +4,18 @@ require 'spec_helper' ...@@ -4,11 +4,18 @@ require 'spec_helper'
RSpec.describe Analytics::DevopsAdoption::Segments::CreateService do RSpec.describe Analytics::DevopsAdoption::Segments::CreateService do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:reporter) { create(:user).tap { |u| group.add_reporter(u) } } let_it_be(:display_group) { create(:group) }
let_it_be(:reporter) do
create(:user).tap do |u|
group.add_reporter(u)
display_group.add_reporter(u)
end
end
let(:current_user) { reporter } let(:current_user) { reporter }
let(:params) { { namespace: group } } let(:params) { { namespace: group, display_namespace: display_group } }
let(:segment) { subject.payload[:segment] } let(:segment) { subject.payload[:segment] }
subject(:response) { described_class.new(params: params, current_user: current_user).execute } subject(:response) { described_class.new(params: params, current_user: current_user).execute }
...@@ -17,9 +24,10 @@ RSpec.describe Analytics::DevopsAdoption::Segments::CreateService do ...@@ -17,9 +24,10 @@ RSpec.describe Analytics::DevopsAdoption::Segments::CreateService do
stub_licensed_features(group_level_devops_adoption: true, instance_level_devops_adoption: true) stub_licensed_features(group_level_devops_adoption: true, instance_level_devops_adoption: true)
end end
it 'persists the segment' do it 'persists the segment', :aggregate_failures do
expect(response).to be_success expect(response).to be_success
expect(segment.namespace).to eq(group) expect(segment.namespace).to eq(group)
expect(segment.display_namespace).to eq(display_group)
end end
it 'schedules for snapshot creation' do it 'schedules for snapshot creation' do
...@@ -30,11 +38,25 @@ RSpec.describe Analytics::DevopsAdoption::Segments::CreateService do ...@@ -30,11 +38,25 @@ RSpec.describe Analytics::DevopsAdoption::Segments::CreateService do
expect(Analytics::DevopsAdoption::CreateSnapshotWorker).to have_received(:perform_async).with(Analytics::DevopsAdoption::Segment.last.id) expect(Analytics::DevopsAdoption::CreateSnapshotWorker).to have_received(:perform_async).with(Analytics::DevopsAdoption::Segment.last.id)
end end
it 'authorizes for manage_devops_adoption' do it 'authorizes for manage_devops_adoption', :aggregate_failures do
expect(::Ability).to receive(:allowed?).with(current_user, :manage_devops_adoption_segments, group).and_return true
expect(::Ability).to receive(:allowed?).with(current_user, :manage_devops_adoption_segments, display_group).and_return true
response
end
context 'without display_namespace_id' do
before do
params[:display_namespace] = nil
end
it 'authorizes for global manage_devops_adoption', :aggregate_failures do
expect(::Ability).to receive(:allowed?).with(current_user, :manage_devops_adoption_segments, group).and_return true expect(::Ability).to receive(:allowed?).with(current_user, :manage_devops_adoption_segments, group).and_return true
expect(::Ability).to receive(:allowed?).with(current_user, :manage_devops_adoption_segments, :global).and_return true
response response
end end
end
context 'for guests' do context 'for guests' do
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
......
...@@ -4,9 +4,16 @@ require 'spec_helper' ...@@ -4,9 +4,16 @@ require 'spec_helper'
RSpec.describe Analytics::DevopsAdoption::Segments::DeleteService do RSpec.describe Analytics::DevopsAdoption::Segments::DeleteService do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:reporter) { create(:user).tap { |u| group.add_reporter(u) } } let_it_be(:display_group) { create(:group) }
let(:segment) { create(:devops_adoption_segment, namespace: group) } let_it_be(:reporter) do
create(:user).tap do |u|
group.add_reporter(u)
display_group.add_reporter(u)
end
end
let(:segment) { create(:devops_adoption_segment, namespace: group, display_namespace: display_group) }
let(:current_user) { reporter } let(:current_user) { reporter }
subject(:response) { described_class.new(segment: segment, current_user: current_user).execute } subject(:response) { described_class.new(segment: segment, current_user: current_user).execute }
...@@ -29,8 +36,9 @@ RSpec.describe Analytics::DevopsAdoption::Segments::DeleteService do ...@@ -29,8 +36,9 @@ RSpec.describe Analytics::DevopsAdoption::Segments::DeleteService do
end end
end end
it 'authorizes for manage_devops_adoption' do it 'authorizes for manage_devops_adoption', :aggregate_failures do
expect(::Ability).to receive(:allowed?).with(current_user, :manage_devops_adoption_segments, group).and_return true expect(::Ability).to receive(:allowed?).with(current_user, :manage_devops_adoption_segments, group).and_return true
expect(::Ability).to receive(:allowed?).with(current_user, :manage_devops_adoption_segments, display_group).and_return true
response response
end end
......
...@@ -4,11 +4,18 @@ require 'spec_helper' ...@@ -4,11 +4,18 @@ require 'spec_helper'
RSpec.describe Analytics::DevopsAdoption::Segments::FindOrCreateService do RSpec.describe Analytics::DevopsAdoption::Segments::FindOrCreateService do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:reporter) { create(:user).tap { |u| group.add_reporter(u) } } let_it_be(:display_group) { create(:group) }
let_it_be(:reporter) do
create(:user).tap do |u|
group.add_reporter(u)
display_group.add_reporter(u)
end
end
let(:current_user) { reporter } let(:current_user) { reporter }
let(:params) { { namespace: group } } let(:params) { { namespace: group, display_namespace: display_group } }
subject(:response) { described_class.new(params: params, current_user: current_user).execute } subject(:response) { described_class.new(params: params, current_user: current_user).execute }
...@@ -16,8 +23,8 @@ RSpec.describe Analytics::DevopsAdoption::Segments::FindOrCreateService do ...@@ -16,8 +23,8 @@ RSpec.describe Analytics::DevopsAdoption::Segments::FindOrCreateService do
stub_licensed_features(group_level_devops_adoption: true, instance_level_devops_adoption: true) stub_licensed_features(group_level_devops_adoption: true, instance_level_devops_adoption: true)
end end
context 'when segment for given namespace already exists' do context 'when segment for given namespace & display_namespace already exists' do
let!(:segment) { create :devops_adoption_segment, namespace: group } let!(:segment) { create :devops_adoption_segment, namespace: group, display_namespace: display_group }
it 'returns existing segment' do it 'returns existing segment' do
expect { response }.not_to change { Analytics::DevopsAdoption::Segment.count } expect { response }.not_to change { Analytics::DevopsAdoption::Segment.count }
...@@ -27,8 +34,13 @@ RSpec.describe Analytics::DevopsAdoption::Segments::FindOrCreateService do ...@@ -27,8 +34,13 @@ RSpec.describe Analytics::DevopsAdoption::Segments::FindOrCreateService do
end end
context 'when segment for given namespace does not exist' do context 'when segment for given namespace does not exist' do
let!(:segment2) { create :devops_adoption_segment, namespace: group }
let!(:segment3) { create :devops_adoption_segment, display_namespace: display_group }
it 'calls for segment creation' do it 'calls for segment creation' do
expect_next_instance_of(Analytics::DevopsAdoption::Segments::CreateService, current_user: current_user, params: { namespace: group }) do |instance| expect_next_instance_of(Analytics::DevopsAdoption::Segments::CreateService,
current_user: current_user,
params: { namespace: group, display_namespace: display_group }) do |instance|
expect(instance).to receive(:execute).and_return('create_response') expect(instance).to receive(:execute).and_return('create_response')
end end
...@@ -42,6 +54,11 @@ RSpec.describe Analytics::DevopsAdoption::Segments::FindOrCreateService do ...@@ -42,6 +54,11 @@ RSpec.describe Analytics::DevopsAdoption::Segments::FindOrCreateService do
.at_least(1) .at_least(1)
.and_return(true) .and_return(true)
expect(::Ability).to receive(:allowed?)
.with(current_user, :manage_devops_adoption_segments, display_group)
.at_least(1)
.and_return(true)
response response
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