Commit 1ff17bc2 authored by Toon Claes's avatar Toon Claes

Merge branch '92508-enable-not-filters-for-mr-labels-graphql' into 'master'

Add negative filters for merge requests API

See merge request gitlab-org/gitlab!58021
parents 781df9fc e4339f5b
......@@ -244,7 +244,7 @@ class IssuableFinder
# These are "helper" params that modify the results, like :in and :search. They usually come in at the top-level
# params, but if they do come in inside the `:not` params, the inner ones should take precedence.
not_helpers = params.slice(*NEGATABLE_PARAMS_HELPER_KEYS).merge(params[:not].slice(*NEGATABLE_PARAMS_HELPER_KEYS))
not_helpers = params.slice(*NEGATABLE_PARAMS_HELPER_KEYS).merge(params[:not].to_h.slice(*NEGATABLE_PARAMS_HELPER_KEYS))
not_helpers.each do |key, value|
not_params[key] = value unless not_params[key].present?
end
......
......@@ -3,6 +3,7 @@
module Resolvers
class MergeRequestsResolver < BaseResolver
include ResolvesMergeRequests
extend ::Gitlab::Graphql::NegatableArguments
type ::Types::MergeRequestType.connection_type, null: true
......@@ -68,6 +69,16 @@ module Resolvers
required: false,
default_value: :created_desc
negated do
argument :labels, [GraphQL::STRING_TYPE],
required: false,
as: :label_name,
description: 'Array of label names. All resolved merge requests will not have these labels.'
argument :milestone_title, GraphQL::STRING_TYPE,
required: false,
description: 'Title of the milestone.'
end
def self.single
::Resolvers::MergeRequestResolver
end
......
---
title: Add negative filters for merge requests API
merge_request: 58021
author:
type: added
# frozen_string_literal: true
module Gitlab
module Graphql
module NegatableArguments
class TypeDefiner
def initialize(resolver_class, type_definition)
@resolver_class = resolver_class
@type_definition = type_definition
end
def define!
negated_params_type.instance_eval(&@type_definition)
end
def negated_params_type
@negated_params_type ||= existing_type || build_type
end
private
def existing_type
::Types.const_get(type_class_name, false) if ::Types.const_defined?(type_class_name)
end
def build_type
klass = Class.new(::Types::BaseInputObject)
::Types.const_set(type_class_name, klass)
klass
end
def type_class_name
@type_class_name ||= begin
base_name = @resolver_class.name.sub('Resolvers::', '')
base_name + 'NegatedParamsType'
end
end
end
def negated(param_key: :not, &block)
definer = ::Gitlab::Graphql::NegatableArguments::TypeDefiner.new(self, block)
definer.define!
argument param_key, definer.negated_params_type,
required: false,
description: <<~MD
List of negated arguments.
Warning: this argument is experimental and a subject to change in future.
MD
end
end
end
end
......@@ -189,6 +189,17 @@ RSpec.describe Resolvers::MergeRequestsResolver do
end
end
context 'with negated label argument' do
let_it_be(:label) { merge_request_6.labels.first }
let_it_be(:with_label) { create(:labeled_merge_request, :closed, labels: [label], **common_attrs) }
it 'excludes merge requests with given label from selection' do
result = resolve_mr(project, not: { labels: [label.title] })
expect(result).not_to include(merge_request_6, with_label)
end
end
context 'with merged_after and merged_before arguments' do
before do
merge_request_1.metrics.update!(merged_at: 10.days.ago)
......@@ -221,6 +232,14 @@ RSpec.describe Resolvers::MergeRequestsResolver do
end
end
context 'with negated milestone argument' do
it 'filters out merge requests with given milestone title' do
result = resolve_mr(project, not: { milestone_title: milestone.title })
expect(result).not_to include(merge_request_with_milestone)
end
end
describe 'combinations' do
it 'requires all filters' do
create(:merge_request, :closed, **common_attrs, source_branch: merge_request_4.source_branch)
......
......@@ -243,6 +243,7 @@ RSpec.describe GitlabSchema.types['Project'] do
:assignee_username,
:reviewer_username,
:milestone_title,
:not,
:sort
)
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Graphql::NegatableArguments do
let(:test_resolver) do
Class.new(Resolvers::BaseResolver).tap do |klass|
klass.extend described_class
allow(klass).to receive(:name).and_return('Resolvers::TestResolver')
end
end
describe '#negated' do
it 'defines :not argument' do
test_resolver.negated {}
expect(test_resolver.arguments['not'].type.name).to eq "Types::TestResolverNegatedParamsType"
end
it 'defines any arguments passed as block' do
test_resolver.negated do
argument :foo, GraphQL::STRING_TYPE, required: false
end
expect(test_resolver.arguments['not'].type.arguments.keys).to match_array(['foo'])
end
it 'defines all arguments passed as block even if called multiple times' do
test_resolver.negated do
argument :foo, GraphQL::STRING_TYPE, required: false
end
test_resolver.negated do
argument :bar, GraphQL::STRING_TYPE, required: false
end
expect(test_resolver.arguments['not'].type.arguments.keys).to match_array(%w[foo bar])
end
it 'allows to specify custom argument name' do
test_resolver.negated(param_key: :negative) {}
expect(test_resolver.arguments).to include('negative')
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