Commit 7e69e2f8 authored by rpereira2's avatar rpereira2

Add search by state in graphql environments API

Add 'states' argument to Environments GraphQL API to allow filtering
of environments by state.
parent cb9ac5a6
......@@ -3,6 +3,8 @@
class EnvironmentsFinder
attr_reader :project, :current_user, :params
InvalidStatesError = Class.new(StandardError)
def initialize(project, current_user, params = {})
@project, @current_user, @params = project, current_user, params
end
......@@ -45,6 +47,9 @@ class EnvironmentsFinder
environments = by_name(environments)
environments = by_search(environments)
# Raises InvalidStatesError if params[:states] contains invalid states.
environments = by_states(environments)
environments
end
......@@ -91,4 +96,27 @@ class EnvironmentsFinder
environments
end
end
def by_states(environments)
if params[:states].present?
environments_with_states(environments)
else
environments
end
end
def environments_with_states(environments)
# Convert to array of strings
states = Array(params[:states]).map(&:to_s)
raise InvalidStatesError, _('Requested states are invalid') unless valid_states?(states)
environments.with_states(states)
end
def valid_states?(states)
valid_states = Environment.valid_states.map(&:to_s)
(states - valid_states).empty?
end
end
......@@ -10,6 +10,10 @@ module Resolvers
required: false,
description: 'Search query'
argument :states, [GraphQL::STRING_TYPE],
required: false,
description: 'States of environments that should be included in result'
type Types::EnvironmentType, null: true
alias_method :project, :object
......@@ -18,6 +22,8 @@ module Resolvers
return unless project.present?
EnvironmentsFinder.new(project, context[:current_user], args).find
rescue EnvironmentsFinder::InvalidStatesError => exception
raise Gitlab::Graphql::Errors::ArgumentError, exception.message
end
end
end
......@@ -118,6 +118,10 @@ class Environment < ApplicationRecord
find_or_create_by(name: name)
end
def self.valid_states
self.state_machine.states.map(&:name)
end
class << self
##
# This method returns stop actions (jobs) for multiple environments within one
......
---
title: Add ability to search by environment state in environments GraphQL API
merge_request: 28567
author:
type: changed
......@@ -5875,6 +5875,11 @@ type Project {
Search query
"""
search: String
"""
States of environments that should be included in result
"""
states: [String!]
): EnvironmentConnection
"""
......
......@@ -17785,6 +17785,24 @@
},
"defaultValue": null
},
{
"name": "states",
"description": "States of environments that should be included in result",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
......
......@@ -16970,6 +16970,9 @@ msgstr ""
msgid "Requested design version does not exist"
msgstr ""
msgid "Requested states are invalid"
msgstr ""
msgid "Requests Profiles"
msgstr ""
......
......@@ -7,6 +7,14 @@ FactoryBot.define do
association :project, :repository
sequence(:external_url) { |n| "https://env#{n}.example.gitlab.com" }
trait :available do
state { :available }
end
trait :stopped do
state { :stopped }
end
trait :with_review_app do |environment|
transient do
ref { 'master' }
......
......@@ -3,15 +3,15 @@
require 'spec_helper'
describe EnvironmentsFinder do
describe '#execute' do
let(:project) { create(:project, :repository) }
let(:user) { project.creator }
let(:environment) { create(:environment, project: project) }
let(:environment) { create(:environment, :available, project: project) }
before do
project.add_maintainer(user)
end
describe '#execute' do
context 'tagged deployment' do
let(:environment_two) { create(:environment, project: project) }
# Environments need to include commits, so rewind two commits to fit
......@@ -124,4 +124,53 @@ describe EnvironmentsFinder do
end
end
end
describe '#find' do
context 'with states parameter' do
let(:stopped_environment) { create(:environment, :stopped, project: project) }
it 'returns environments with the requested state' do
result = described_class.new(project, user, states: 'available').find
expect(result).to contain_exactly(environment)
end
it 'returns environments with any of the requested states' do
result = described_class.new(project, user, states: %w(available stopped)).find
expect(result).to contain_exactly(environment, stopped_environment)
end
it 'raises exception when requested state is invalid' do
expect { described_class.new(project, user, states: %w(invalid stopped)).find }.to(
raise_error(described_class::InvalidStatesError, 'Requested states are invalid')
)
end
context 'works with symbols' do
it 'returns environments with the requested state' do
result = described_class.new(project, user, states: :available).find
expect(result).to contain_exactly(environment)
end
it 'returns environments with any of the requested states' do
result = described_class.new(project, user, states: [:available, :stopped]).find
expect(result).to contain_exactly(environment, stopped_environment)
end
end
end
context 'with search and states' do
let(:environment2) { create(:environment, :stopped, name: 'test2', project: project) }
let(:environment3) { create(:environment, :available, name: 'test3', project: project) }
it 'searches environments by name and state' do
result = described_class.new(project, user, search: 'test', states: :available).find
expect(result).to contain_exactly(environment3)
end
end
end
end
......@@ -10,9 +10,9 @@ describe Resolvers::EnvironmentsResolver do
context "with a group" do
let(:group) { create(:group) }
let(:project) { create(:project, :public, group: group) }
let!(:environment1) { create(:environment, name: 'production', project: project) }
let!(:environment2) { create(:environment, name: 'test', project: project) }
let!(:environment3) { create(:environment, name: 'test2', project: project) }
let!(:environment1) { create(:environment, :available, name: 'production', project: project) }
let!(:environment2) { create(:environment, :stopped, name: 'test', project: project) }
let!(:environment3) { create(:environment, :available, name: 'test2', project: project) }
before do
group.add_developer(current_user)
......@@ -41,6 +41,18 @@ describe Resolvers::EnvironmentsResolver do
end
end
context 'with states' do
it 'searches environments by state' do
expect(resolve_environments(states: ['available'])).to contain_exactly(environment1, environment3)
end
it 'returns error if requested state is invalid' do
expect { resolve_environments(states: ['invalid']) }.to(
raise_error(Gitlab::Graphql::Errors::ArgumentError)
)
end
end
context 'when project is nil' do
subject { resolve(described_class, obj: nil, args: {}, ctx: { current_user: current_user }) }
......
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