Skip to content
Snippets Groups Projects
Commit 226154b2 authored by Alexandru Croitor's avatar Alexandru Croitor
Browse files

Add labels resolver

Adding separate labels resolved to fetch labels for
groups and projects with corresponding set of parameters.
parent 30741c7a
No related branches found
No related tags found
No related merge requests found
Showing
with 353 additions and 46 deletions
Loading
Loading
@@ -177,7 +177,7 @@ def projects
end
 
if group?
@projects = if params[:include_subgroups]
@projects = if params[:include_descendant_groups]
@projects.in_namespace(group.self_and_descendants.select(:id))
else
@projects.in_namespace(group.id)
Loading
Loading
# frozen_string_literal: true
module Resolvers
class GroupLabelsResolver < LabelsResolver
type Types::LabelType.connection_type, null: true
argument :include_descendant_groups, GraphQL::BOOLEAN_TYPE,
required: false,
description: 'Include labels from descendant groups.',
default_value: false
argument :only_group_labels, GraphQL::BOOLEAN_TYPE,
required: false,
description: 'Include only group level labels.',
default_value: false
end
end
# frozen_string_literal: true
module Resolvers
class LabelsResolver < BaseResolver
include Gitlab::Graphql::Authorize::AuthorizeResource
authorize :read_label
type Types::LabelType.connection_type, null: true
argument :search_term, GraphQL::STRING_TYPE,
required: false,
description: 'A search term to find labels with.'
argument :include_ancestor_groups, GraphQL::BOOLEAN_TYPE,
required: false,
description: 'Include labels from ancestor groups.',
default_value: false
def resolve(**args)
return Label.none if parent.nil?
authorize!(parent)
# LabelsFinder uses `search` param, so we transform `search_term` into `search`
args[:search] = args.delete(:search_term)
LabelsFinder.new(current_user, parent_param.merge(args)).execute
end
private
def parent
object.respond_to?(:sync) ? object.sync : object
end
def parent_param
key = case parent
when Group then :group
when Project then :project
else raise "Unexpected parent type: #{parent.class}"
end
{ "#{key}": parent }
end
end
end
Loading
Loading
@@ -107,17 +107,8 @@ def label(title:)
field :labels,
Types::LabelType.connection_type,
null: true,
description: 'Labels available on this group.' do
argument :search_term, GraphQL::STRING_TYPE,
required: false,
description: 'A search term to find labels with.'
end
def labels(search_term: nil)
LabelsFinder
.new(current_user, group: group, search: search_term)
.execute
end
description: 'Labels available on this group.',
resolver: Resolvers::GroupLabelsResolver
 
def avatar_url
object.avatar_url(only_path: false)
Loading
Loading
Loading
Loading
@@ -337,17 +337,8 @@ def label(title:)
field :labels,
Types::LabelType.connection_type,
null: true,
description: 'Labels available on this project.' do
argument :search_term, GraphQL::STRING_TYPE,
required: false,
description: 'A search term to find labels with.'
end
def labels(search_term: nil)
LabelsFinder
.new(current_user, project: project, search: search_term)
.execute
end
description: 'Labels available on this project.',
resolver: Resolvers::LabelsResolver
 
def avatar_url
object.avatar_url(only_path: false)
Loading
Loading
---
title: Adds only_group_labels and include_ancestor_labels and include_descendant_groups
arguments to the project and group labels resolvers respectively
merge_request: 53639
author:
type: fixed
Loading
Loading
@@ -11619,11 +11619,26 @@ type Group {
"""
first: Int
 
"""
Include labels from ancestor groups.
"""
includeAncestorGroups: Boolean = false
"""
Include labels from descendant groups.
"""
includeDescendantGroups: Boolean = false
"""
Returns the last _n_ elements from the list.
"""
last: Int
 
"""
Include only group level labels.
"""
onlyGroupLabels: Boolean = false
"""
A search term to find labels with.
"""
Loading
Loading
@@ -19834,6 +19849,11 @@ type Project {
"""
first: Int
 
"""
Include labels from ancestor groups.
"""
includeAncestorGroups: Boolean = false
"""
Returns the last _n_ elements from the list.
"""
Loading
Loading
Loading
Loading
@@ -31572,6 +31572,46 @@
"name": "labels",
"description": "Labels available on this group.",
"args": [
{
"name": "searchTerm",
"description": "A search term to find labels with.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "includeAncestorGroups",
"description": "Include labels from ancestor groups.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": "false"
},
{
"name": "includeDescendantGroups",
"description": "Include labels from descendant groups.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": "false"
},
{
"name": "onlyGroupLabels",
"description": "Include only group level labels.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": "false"
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
Loading
Loading
@@ -31611,16 +31651,6 @@
"ofType": null
},
"defaultValue": null
},
{
"name": "searchTerm",
"description": "A search term to find labels with.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"type": {
Loading
Loading
@@ -57717,6 +57747,26 @@
"name": "labels",
"description": "Labels available on this project.",
"args": [
{
"name": "searchTerm",
"description": "A search term to find labels with.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "includeAncestorGroups",
"description": "Include labels from ancestor groups.",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": "false"
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
Loading
Loading
@@ -57756,16 +57806,6 @@
"ofType": null
},
"defaultValue": null
},
{
"name": "searchTerm",
"description": "A search term to find labels with.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
}
],
"type": {
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::GroupLabelsResolver do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:current_user) { create(:user) }
let_it_be(:group, reload: true) { create(:group, :private) }
let_it_be(:subgroup, reload: true) { create(:group, :private, parent: group) }
let_it_be(:sub_subgroup, reload: true) { create(:group, :private, parent: subgroup) }
let_it_be(:project, reload: true) { create(:project, :private, group: sub_subgroup) }
let_it_be(:label1) { create(:label, project: project, name: 'project feature') }
let_it_be(:label2) { create(:label, project: project, name: 'new project feature') }
let_it_be(:group_label1) { create(:group_label, group: group, name: 'group feature') }
let_it_be(:group_label2) { create(:group_label, group: group, name: 'new group feature') }
let_it_be(:subgroup_label1) { create(:group_label, group: subgroup, name: 'subgroup feature') }
let_it_be(:subgroup_label2) { create(:group_label, group: subgroup, name: 'new subgroup feature') }
let_it_be(:sub_subgroup_label1) { create(:group_label, group: sub_subgroup, name: 'sub_subgroup feature') }
let_it_be(:sub_subgroup_label2) { create(:group_label, group: sub_subgroup, name: 'new sub_subgroup feature') }
specify do
expect(described_class).to have_nullable_graphql_type(Types::LabelType.connection_type)
end
describe '#resolve' do
context 'with unauthorized user' do
it 'raises error' do
expect { resolve_labels(subgroup) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'with authorized user' do
it 'does not raise error' do
group.add_guest(current_user)
expect { resolve_labels(subgroup) }.not_to raise_error
end
end
context 'without parent' do
it 'returns no labels' do
expect(resolve_labels(nil)).to eq(Label.none)
end
end
context 'at group level' do
before_all do
group.add_developer(current_user)
end
# because :include_ancestor_groups, :include_descendant_groups, :only_group_labels default to false
# the `nil` value would be equivalent to passing in `false` so just check for `nil` option
where(:include_ancestor_groups, :include_descendant_groups, :only_group_labels, :search_term, :test) do
nil | nil | nil | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2) }
nil | nil | true | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2) }
nil | true | nil | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2, label1, label2) }
nil | true | true | nil | -> { expect(subject).to contain_exactly(subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
true | nil | nil | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2) }
true | nil | true | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2) }
true | true | nil | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2, label1, label2) }
true | true | true | nil | -> { expect(subject).to contain_exactly(group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
nil | nil | nil | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2) }
nil | nil | true | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2) }
nil | true | nil | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2, sub_subgroup_label2, label2) }
nil | true | true | 'new' | -> { expect(subject).to contain_exactly(subgroup_label2, sub_subgroup_label2) }
true | nil | nil | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2) }
true | nil | true | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2) }
true | true | nil | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2, sub_subgroup_label2, label2) }
true | true | true | 'new' | -> { expect(subject).to contain_exactly(group_label2, subgroup_label2, sub_subgroup_label2) }
end
with_them do
let(:params) do
{
include_ancestor_groups: include_ancestor_groups,
include_descendant_groups: include_descendant_groups,
only_group_labels: only_group_labels,
search_term: search_term
}
end
subject { resolve_labels(subgroup, params) }
it { self.instance_exec(&test) }
end
end
end
def resolve_labels(parent, args = {}, context = { current_user: current_user })
resolve(described_class, obj: parent, args: args, ctx: context)
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::LabelsResolver do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:current_user) { create(:user) }
let_it_be(:group, reload: true) { create(:group, :private) }
let_it_be(:subgroup, reload: true) { create(:group, :private, parent: group) }
let_it_be(:sub_subgroup, reload: true) { create(:group, :private, parent: subgroup) }
let_it_be(:project, reload: true) { create(:project, :private, group: subgroup) }
let_it_be(:label1) { create(:label, project: project, name: 'project feature') }
let_it_be(:label2) { create(:label, project: project, name: 'new project feature') }
let_it_be(:group_label1) { create(:group_label, group: group, name: 'group feature') }
let_it_be(:group_label2) { create(:group_label, group: group, name: 'new group feature') }
let_it_be(:subgroup_label1) { create(:group_label, group: subgroup, name: 'subgroup feature') }
let_it_be(:subgroup_label2) { create(:group_label, group: subgroup, name: 'new subgroup feature') }
let_it_be(:sub_subgroup_label1) { create(:group_label, group: sub_subgroup, name: 'sub_subgroup feature') }
let_it_be(:sub_subgroup_label2) { create(:group_label, group: sub_subgroup, name: 'new sub_subgroup feature') }
specify do
expect(described_class).to have_nullable_graphql_type(Types::LabelType.connection_type)
end
describe '#resolve' do
context 'with unauthorized user' do
it 'returns no labels' do
expect { resolve_labels(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'with authorized user' do
it 'returns no labels' do
group.add_guest(current_user)
expect { resolve_labels(project) }.not_to raise_error
end
end
context 'without parent' do
it 'returns no labels' do
expect(resolve_labels(nil)).to eq(Label.none)
end
end
context 'at project level' do
before_all do
group.add_developer(current_user)
end
# because :include_ancestor_groups, :include_descendant_groups, :only_group_labels default to false
# the `nil` value would be equivalent to passing in `false` so just check for `nil` option
where(:include_ancestor_groups, :include_descendant_groups, :only_group_labels, :search_term, :test) do
nil | nil | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2) }
nil | nil | true | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2) }
nil | true | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
nil | true | true | nil | -> { expect(subject).to contain_exactly(label1, label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
true | nil | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2) }
true | nil | true | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2) }
true | true | nil | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
true | true | true | nil | -> { expect(subject).to contain_exactly(label1, label2, group_label1, group_label2, subgroup_label1, subgroup_label2, sub_subgroup_label1, sub_subgroup_label2) }
nil | nil | nil | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2) }
nil | nil | true | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2) }
nil | true | nil | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2, sub_subgroup_label2) }
nil | true | true | 'new' | -> { expect(subject).to contain_exactly(label2, subgroup_label2, sub_subgroup_label2) }
true | nil | nil | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2) }
true | nil | true | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2) }
true | true | nil | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2, sub_subgroup_label2) }
true | true | true | 'new' | -> { expect(subject).to contain_exactly(label2, group_label2, subgroup_label2, sub_subgroup_label2) }
end
with_them do
let(:params) do
{
include_ancestor_groups: include_ancestor_groups,
include_descendant_groups: include_descendant_groups,
only_group_labels: only_group_labels,
search_term: search_term
}
end
subject { resolve_labels(project, params) }
it { self.instance_exec(&test) }
end
end
end
def resolve_labels(parent, args = {}, context = { current_user: current_user })
resolve(described_class, obj: parent, args: args, ctx: context)
end
end
Loading
Loading
@@ -38,5 +38,7 @@
it { is_expected.to have_graphql_resolver(Resolvers::GroupMembersResolver) }
end
 
it_behaves_like 'a GraphQL type with labels'
it_behaves_like 'a GraphQL type with labels' do
let(:labels_resolver_arguments) { [:search_term, :includeAncestorGroups, :includeDescendantGroups, :onlyGroupLabels] }
end
end
Loading
Loading
@@ -332,7 +332,9 @@
it { is_expected.to have_graphql_resolver(Resolvers::Terraform::StatesResolver) }
end
 
it_behaves_like 'a GraphQL type with labels'
it_behaves_like 'a GraphQL type with labels' do
let(:labels_resolver_arguments) { [:search_term, :includeAncestorGroups] }
end
 
describe 'jira_imports' do
subject { resolve_field(:jira_imports, project) }
Loading
Loading
Loading
Loading
@@ -18,7 +18,7 @@
subject { described_class.fields['labels'] }
 
it { is_expected.to have_graphql_type(Types::LabelType.connection_type) }
it { is_expected.to have_graphql_arguments(:search_term) }
it { is_expected.to have_graphql_arguments(labels_resolver_arguments) }
end
end
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment