Commit fff494a0 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch 'environment-name-search-graphql' into 'master'

Get Project's environment names via GraphQL

See merge request gitlab-org/gitlab!22932
parents 7fc3b11a 6e258795
# frozen_string_literal: true
module Resolvers
class EnvironmentsResolver < BaseResolver
argument :name, GraphQL::STRING_TYPE,
required: false,
description: 'Name of the environment'
argument :search, GraphQL::STRING_TYPE,
required: false,
description: 'Search query'
type Types::EnvironmentType, null: true
alias_method :project, :object
def resolve(**args)
return unless project.present?
EnvironmentsFinder.new(project, context[:current_user], args).find
end
end
end
# frozen_string_literal: true
module Types
class EnvironmentType < BaseObject
graphql_name 'Environment'
description 'Describes where code is deployed for a project'
authorize :read_environment
field :name, GraphQL::STRING_TYPE, null: false,
description: 'Human-readable name of the environment'
field :id, GraphQL::ID_TYPE, null: false,
description: 'ID of the environment'
end
end
......@@ -138,6 +138,12 @@ module Types
description: 'Issues of the project',
resolver: Resolvers::IssuesResolver
field :environments,
Types::EnvironmentType.connection_type,
null: true,
description: 'Environments of the project',
resolver: Resolvers::EnvironmentsResolver
field :issue,
Types::IssueType,
null: true,
......
---
title: Get Project's environment names via GraphQL
merge_request: 22932
author:
type: added
......@@ -1412,6 +1412,56 @@ enum EntryType {
tree
}
"""
Describes where code is deployed for a project
"""
type Environment {
"""
ID of the environment
"""
id: ID!
"""
Human-readable name of the environment
"""
name: String!
}
"""
The connection type for Environment.
"""
type EnvironmentConnection {
"""
A list of edges.
"""
edges: [EnvironmentEdge]
"""
A list of nodes.
"""
nodes: [Environment]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type EnvironmentEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: Environment
}
"""
Represents an epic.
"""
......@@ -4706,6 +4756,41 @@ type Project {
"""
descriptionHtml: String
"""
Environments of the project
"""
environments(
"""
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
"""
Name of the environment
"""
name: String
"""
Search query
"""
search: String
): EnvironmentConnection
"""
Number of times the project has been forked
"""
......
......@@ -406,6 +406,79 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "environments",
"description": "Environments of the project",
"args": [
{
"name": "name",
"description": "Name of the environment",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "search",
"description": "Search query",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "EnvironmentConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "forksCount",
"description": "Number of times the project has been forked",
......@@ -15431,6 +15504,167 @@
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "EnvironmentConnection",
"description": "The connection type for Environment.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "EnvironmentEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Environment",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "EnvironmentEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Environment",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Environment",
"description": "Describes where code is deployed for a project",
"fields": [
{
"name": "id",
"description": "ID of the environment",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Human-readable name of the environment",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "SentryDetailedError",
......
......@@ -232,6 +232,15 @@ Autogenerated return type of DestroySnippet
| `replyId` | ID! | ID used to reply to this discussion |
| `createdAt` | Time! | Timestamp of the discussion's creation |
## Environment
Describes where code is deployed for a project
| Name | Type | Description |
| --- | ---- | ---------- |
| `name` | String! | Human-readable name of the environment |
| `id` | ID! | ID of the environment |
## Epic
Represents an epic.
......
# frozen_string_literal: true
require 'spec_helper'
describe Resolvers::EnvironmentsResolver do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
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) }
before do
group.add_developer(current_user)
end
describe '#resolve' do
it 'finds all environments' do
expect(resolve_environments).to contain_exactly(environment1, environment2, environment3)
end
context 'with name' do
it 'finds a specific environment with name' do
expect(resolve_environments(name: environment1.name)).to contain_exactly(environment1)
end
end
context 'with search' do
it 'searches environment by name' do
expect(resolve_environments(search: 'test')).to contain_exactly(environment2, environment3)
end
context 'when the search term does not match any environments' do
it 'is empty' do
expect(resolve_environments(search: 'nonsense')).to be_empty
end
end
end
context 'when project is nil' do
subject { resolve(described_class, obj: nil, args: {}, ctx: { current_user: current_user }) }
it { is_expected.to be_nil }
end
end
end
def resolve_environments(args = {}, context = { current_user: current_user })
resolve(described_class, obj: project, args: args, ctx: context)
end
end
# frozen_string_literal: true
require 'spec_helper'
describe GitlabSchema.types['Environment'] do
it { expect(described_class.graphql_name).to eq('Environment') }
it 'has the expected fields' do
expected_fields = %w[
name id
]
is_expected.to have_graphql_fields(*expected_fields)
end
it { is_expected.to require_graphql_authorizations(:read_environment) }
end
......@@ -23,7 +23,7 @@ describe GitlabSchema.types['Project'] do
only_allow_merge_if_all_discussions_are_resolved printing_merge_request_link_enabled
namespace group statistics repository merge_requests merge_request issues
issue pipelines removeSourceBranchAfterMerge sentryDetailedError snippets
grafanaIntegration autocloseReferencedIssues suggestion_commit_message
grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments
]
is_expected.to include_graphql_fields(*expected_fields)
......@@ -70,4 +70,11 @@ describe GitlabSchema.types['Project'] do
it { is_expected.to have_graphql_type(Types::GrafanaIntegrationType) }
it { is_expected.to have_graphql_resolver(Resolvers::Projects::GrafanaIntegrationResolver) }
end
describe 'environments field' do
subject { described_class.fields['environments'] }
it { is_expected.to have_graphql_type(Types::EnvironmentType.connection_type) }
it { is_expected.to have_graphql_resolver(Resolvers::EnvironmentsResolver) }
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