Commit 65fd63ce authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch '36162-search-by-state' into 'master'

Add search by state in graphql environments API

See merge request gitlab-org/gitlab!28567
parents c85f0acc 7e69e2f8
......@@ -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
......@@ -119,6 +119,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.",
......
......@@ -16997,6 +16997,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