Commit 93c1b5da authored by Adam Hegyi's avatar Adam Hegyi

Expose Devops Adoption segments via GraphQL

This change exposes Devops Adoption segments via Graphql.
parent f1bdac24
...@@ -4,10 +4,16 @@ class Analytics::DevopsAdoption::Segment < ApplicationRecord ...@@ -4,10 +4,16 @@ class Analytics::DevopsAdoption::Segment < ApplicationRecord
ALLOWED_SEGMENT_COUNT = 20 ALLOWED_SEGMENT_COUNT = 20
has_many :segment_selections has_many :segment_selections
has_many :groups, through: :segment_selections
validates :name, presence: true, uniqueness: true, length: { maximum: 255 } validates :name, presence: true, uniqueness: true, length: { maximum: 255 }
validate :validate_segment_count validate :validate_segment_count
accepts_nested_attributes_for :segment_selections, allow_destroy: true
scope :ordered_by_name, -> { order(:name) }
scope :with_groups, -> { preload(:groups) }
private private
def validate_segment_count def validate_segment_count
......
...@@ -6025,6 +6025,81 @@ type DetailedStatus { ...@@ -6025,6 +6025,81 @@ type DetailedStatus {
tooltip: String tooltip: String
} }
"""
Segment
"""
type DevopsAdoptionSegment {
"""
Assigned groups
"""
groups(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): GroupConnection
"""
ID of the segment
"""
id: ID!
"""
Name of the segment
"""
name: String!
}
"""
The connection type for DevopsAdoptionSegment.
"""
type DevopsAdoptionSegmentConnection {
"""
A list of edges.
"""
edges: [DevopsAdoptionSegmentEdge]
"""
A list of nodes.
"""
nodes: [DevopsAdoptionSegment]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type DevopsAdoptionSegmentEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: DevopsAdoptionSegment
}
input DiffImagePositionInput { input DiffImagePositionInput {
""" """
Merge base of the branch the comment was made on Merge base of the branch the comment was made on
...@@ -9165,6 +9240,41 @@ type Group { ...@@ -9165,6 +9240,41 @@ type Group {
webUrl: String! webUrl: String!
} }
"""
The connection type for Group.
"""
type GroupConnection {
"""
A list of edges.
"""
edges: [GroupEdge]
"""
A list of nodes.
"""
nodes: [Group]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type GroupEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: Group
}
""" """
Identifier of Group Identifier of Group
""" """
...@@ -16540,6 +16650,31 @@ type Query { ...@@ -16540,6 +16650,31 @@ type Query {
""" """
designManagement: DesignManagement! designManagement: DesignManagement!
"""
Get configured DevOps adoption segments on the instance
"""
devopsAdoptionSegments(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): DevopsAdoptionSegmentConnection
""" """
Text to echo back Text to echo back
""" """
......
...@@ -1000,6 +1000,16 @@ Autogenerated return type of DestroySnippet. ...@@ -1000,6 +1000,16 @@ Autogenerated return type of DestroySnippet.
| `text` | String | Text of the status | | `text` | String | Text of the status |
| `tooltip` | String | Tooltip associated with the status | | `tooltip` | String | Tooltip associated with the status |
### DevopsAdoptionSegment
Segment.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `groups` | GroupConnection | Assigned groups |
| `id` | ID! | ID of the segment |
| `name` | String! | Name of the segment |
### DiffPosition ### DiffPosition
| Field | Type | Description | | Field | Type | Description |
......
...@@ -51,6 +51,11 @@ module EE ...@@ -51,6 +51,11 @@ module EE
null: true, null: true,
resolver: ::Resolvers::InstanceSecurityDashboardResolver, resolver: ::Resolvers::InstanceSecurityDashboardResolver,
description: 'Fields related to Instance Security Dashboard' description: 'Fields related to Instance Security Dashboard'
field :devops_adoption_segments, ::Types::Admin::Analytics::DevopsAdoption::SegmentType.connection_type,
null: true,
description: 'Get configured DevOps adoption segments on the instance',
resolver: ::Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver
end end
def vulnerability(id:) def vulnerability(id:)
......
# frozen_string_literal: true
module Resolvers
module Admin
module Analytics
module DevopsAdoption
class SegmentsResolver < BaseResolver
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::Admin::Analytics::DevopsAdoption::SegmentType, null: true
def resolve
authorize!
if segments_feature_available?
::Analytics::DevopsAdoption::Segment.with_groups.ordered_by_name
else
::Analytics::DevopsAdoption::Segment.none
end
end
private
def segments_feature_available?
License.feature_available?(:instance_level_devops_adoption)
end
def authorize!
admin? || raise_resource_not_available_error!
end
def admin?
context[:current_user].present? && context[:current_user].admin?
end
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Graphql/AuthorizeTypes
module Types
module Admin
module Analytics
module DevopsAdoption
class SegmentType < BaseObject
graphql_name 'DevopsAdoptionSegment'
description 'Segment'
field :id, GraphQL::ID_TYPE, null: false,
description: "ID of the segment"
field :name, GraphQL::STRING_TYPE, null: false,
description: 'Name of the segment'
field :groups, Types::GroupType.connection_type, null: true,
description: 'Assigned groups'
end
end
end
end
end
...@@ -19,6 +19,7 @@ class License < ApplicationRecord ...@@ -19,6 +19,7 @@ class License < ApplicationRecord
group_activity_analytics group_activity_analytics
group_bulk_edit group_bulk_edit
group_webhooks group_webhooks
instance_level_devops_adoption
issuable_default_templates issuable_default_templates
issue_weights issue_weights
iterations iterations
......
---
title: Expose Devops Adoption segments via GraphQL
merge_request: 46879
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::Admin::Analytics::DevopsAdoption::SegmentsResolver do
include GraphqlHelpers
let_it_be(:admin_user) { create(:user, :admin) }
let(:current_user) { admin_user }
def resolve_segments(args = {}, context = {})
resolve(described_class, args: args, ctx: context)
end
describe '#resolve' do
let_it_be(:user) { create(:user) }
let_it_be(:segment_1) { create(:devops_adoption_segment, name: 'bbb') }
let_it_be(:segment_2) { create(:devops_adoption_segment, name: 'aaa') }
subject { resolve_segments({}, { current_user: current_user }) }
before do
stub_licensed_features(instance_level_devops_adoption: true)
end
context 'when requesting project count measurements' do
context 'as an admin user' do
let(:current_user) { admin_user }
it 'returns the records, ordered by name' do
expect(subject).to eq([segment_2, segment_1])
end
end
context 'when the feature is not available' do
let(:current_user) { admin_user }
before do
stub_licensed_features(instance_level_devops_adoption: false)
end
it 'returns the records, ordered by name' do
expect(subject).to be_empty
end
end
context 'as a non-admin user' do
let(:current_user) { user }
it 'raises ResourceNotAvailable error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'as an unauthenticated user' do
let(:current_user) { nil }
it 'raises ResourceNotAvailable error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
end
end
...@@ -26,4 +26,15 @@ RSpec.describe Analytics::DevopsAdoption::Segment, type: :model do ...@@ -26,4 +26,15 @@ RSpec.describe Analytics::DevopsAdoption::Segment, type: :model do
end end
end end
end end
describe '.ordered_by_name' do
let(:segment_1) { create(:devops_adoption_segment, name: 'bbb') }
let(:segment_2) { create(:devops_adoption_segment, name: 'aaa') }
subject { described_class.ordered_by_name }
it 'orders segments by name' do
expect(subject).to eq([segment_2, segment_1])
end
end
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