summaryrefslogtreecommitdiff
path: root/app/graphql/resolvers
diff options
context:
space:
mode:
Diffstat (limited to 'app/graphql/resolvers')
-rw-r--r--app/graphql/resolvers/alert_management/alert_resolver.rb4
-rw-r--r--app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb4
-rw-r--r--app/graphql/resolvers/assigned_merge_requests_resolver.rb2
-rw-r--r--app/graphql/resolvers/authored_merge_requests_resolver.rb2
-rw-r--r--app/graphql/resolvers/base_resolver.rb3
-rw-r--r--app/graphql/resolvers/board_list_issues_resolver.rb2
-rw-r--r--app/graphql/resolvers/board_lists_resolver.rb10
-rw-r--r--app/graphql/resolvers/board_resolver.rb27
-rw-r--r--app/graphql/resolvers/boards_resolver.rb2
-rw-r--r--app/graphql/resolvers/ci/runner_platforms_resolver.rb30
-rw-r--r--app/graphql/resolvers/concerns/group_issuable_resolver.rb14
-rw-r--r--app/graphql/resolvers/concerns/issue_resolver_arguments.rb6
-rw-r--r--app/graphql/resolvers/concerns/looks_ahead.rb4
-rw-r--r--app/graphql/resolvers/concerns/resolves_merge_requests.rb9
-rw-r--r--app/graphql/resolvers/concerns/time_frame_arguments.rb20
-rw-r--r--app/graphql/resolvers/group_issues_resolver.rb7
-rw-r--r--app/graphql/resolvers/group_merge_requests_resolver.rb25
-rw-r--r--app/graphql/resolvers/merge_requests_resolver.rb12
-rw-r--r--app/graphql/resolvers/milestones_resolver.rb27
-rw-r--r--app/graphql/resolvers/project_merge_requests_resolver.rb8
-rw-r--r--app/graphql/resolvers/projects/jira_projects_resolver.rb2
-rw-r--r--app/graphql/resolvers/projects_resolver.rb16
-rw-r--r--app/graphql/resolvers/snippets/blobs_resolver.rb40
-rw-r--r--app/graphql/resolvers/terraform/states_resolver.rb23
24 files changed, 268 insertions, 31 deletions
diff --git a/app/graphql/resolvers/alert_management/alert_resolver.rb b/app/graphql/resolvers/alert_management/alert_resolver.rb
index 71a7615685a..dc9b1dbb5f4 100644
--- a/app/graphql/resolvers/alert_management/alert_resolver.rb
+++ b/app/graphql/resolvers/alert_management/alert_resolver.rb
@@ -22,6 +22,10 @@ module Resolvers
description: 'Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.',
required: false
+ argument :assignee_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of a user assigned to the issue'
+
type Types::AlertManagement::AlertType, null: true
def resolve_with_lookahead(**args)
diff --git a/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb b/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb
index a45de21002f..96ea4610aff 100644
--- a/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb
+++ b/app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb
@@ -9,6 +9,10 @@ module Resolvers
description: 'Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.',
required: false
+ argument :assignee_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of a user assigned to the issue'
+
def resolve(**args)
::Gitlab::AlertManagement::AlertStatusCounts.new(context[:current_user], object, args)
end
diff --git a/app/graphql/resolvers/assigned_merge_requests_resolver.rb b/app/graphql/resolvers/assigned_merge_requests_resolver.rb
index fa08b142a7e..172a8e298ad 100644
--- a/app/graphql/resolvers/assigned_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/assigned_merge_requests_resolver.rb
@@ -2,6 +2,8 @@
module Resolvers
class AssignedMergeRequestsResolver < UserMergeRequestsResolver
+ accept_author
+
def user_role
:assignee
end
diff --git a/app/graphql/resolvers/authored_merge_requests_resolver.rb b/app/graphql/resolvers/authored_merge_requests_resolver.rb
index e19bc9e8715..bc796f8685a 100644
--- a/app/graphql/resolvers/authored_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/authored_merge_requests_resolver.rb
@@ -2,6 +2,8 @@
module Resolvers
class AuthoredMergeRequestsResolver < UserMergeRequestsResolver
+ accept_assignee
+
def user_role
:author
end
diff --git a/app/graphql/resolvers/base_resolver.rb b/app/graphql/resolvers/base_resolver.rb
index 791c6eab42f..2b8854fb4d0 100644
--- a/app/graphql/resolvers/base_resolver.rb
+++ b/app/graphql/resolvers/base_resolver.rb
@@ -4,6 +4,9 @@ module Resolvers
class BaseResolver < GraphQL::Schema::Resolver
extend ::Gitlab::Utils::Override
include ::Gitlab::Utils::StrongMemoize
+ include ::Gitlab::Graphql::GlobalIDCompatibility
+
+ argument_class ::Types::BaseArgument
def self.single
@single ||= Class.new(self) do
diff --git a/app/graphql/resolvers/board_list_issues_resolver.rb b/app/graphql/resolvers/board_list_issues_resolver.rb
index dba9f99edeb..3421e1024c0 100644
--- a/app/graphql/resolvers/board_list_issues_resolver.rb
+++ b/app/graphql/resolvers/board_list_issues_resolver.rb
@@ -14,7 +14,7 @@ module Resolvers
def resolve(**args)
filter_params = issue_filters(args[:filters]).merge(board_id: list.board.id, id: list.id)
- service = Boards::Issues::ListService.new(list.board.resource_parent, context[:current_user], filter_params)
+ service = ::Boards::Issues::ListService.new(list.board.resource_parent, context[:current_user], filter_params)
Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(service.execute)
end
diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb
index b1d43934f24..3384b37e2ce 100644
--- a/app/graphql/resolvers/board_lists_resolver.rb
+++ b/app/graphql/resolvers/board_lists_resolver.rb
@@ -2,6 +2,7 @@
module Resolvers
class BoardListsResolver < BaseResolver
+ include BoardIssueFilterable
include Gitlab::Graphql::Authorize::AuthorizeResource
type Types::BoardListType, null: true
@@ -10,12 +11,17 @@ module Resolvers
required: false,
description: 'Find a list by its global ID'
+ argument :issue_filters, Types::Boards::BoardIssueInputType,
+ required: false,
+ description: 'Filters applied when getting issue metadata in the board list'
+
alias_method :board, :object
- def resolve(lookahead: nil, id: nil)
+ def resolve(lookahead: nil, id: nil, issue_filters: {})
authorize!(board)
lists = board_lists(id)
+ context.scoped_set!(:issue_filters, issue_filters(issue_filters))
if load_preferences?(lookahead)
List.preload_preferences_for_user(lists, context[:current_user])
@@ -27,7 +33,7 @@ module Resolvers
private
def board_lists(id)
- service = Boards::Lists::ListService.new(
+ service = ::Boards::Lists::ListService.new(
board.resource_parent,
context[:current_user],
list_id: extract_list_id(id)
diff --git a/app/graphql/resolvers/board_resolver.rb b/app/graphql/resolvers/board_resolver.rb
new file mode 100644
index 00000000000..517f4e514c9
--- /dev/null
+++ b/app/graphql/resolvers/board_resolver.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class BoardResolver < BaseResolver.single
+ alias_method :parent, :synchronized_object
+
+ type Types::BoardType, null: true
+
+ argument :id, ::Types::GlobalIDType[::Board],
+ required: true,
+ description: 'The board\'s ID'
+
+ def resolve(id: nil)
+ return unless parent
+
+ ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false).first
+ rescue ActiveRecord::RecordNotFound
+ nil
+ end
+
+ private
+
+ def extract_board_id(gid)
+ GitlabSchema.parse_gid(gid, expected_type: ::Board).model_id
+ end
+ end
+end
diff --git a/app/graphql/resolvers/boards_resolver.rb b/app/graphql/resolvers/boards_resolver.rb
index eceb5b38031..82efd92d33f 100644
--- a/app/graphql/resolvers/boards_resolver.rb
+++ b/app/graphql/resolvers/boards_resolver.rb
@@ -16,7 +16,7 @@ module Resolvers
return Board.none unless parent
- Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false)
+ ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false)
rescue ActiveRecord::RecordNotFound
Board.none
end
diff --git a/app/graphql/resolvers/ci/runner_platforms_resolver.rb b/app/graphql/resolvers/ci/runner_platforms_resolver.rb
new file mode 100644
index 00000000000..9677c5139b4
--- /dev/null
+++ b/app/graphql/resolvers/ci/runner_platforms_resolver.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class RunnerPlatformsResolver < BaseResolver
+ type Types::Ci::RunnerPlatformType, null: false
+
+ def resolve(**args)
+ runner_instructions.map do |platform, data|
+ {
+ name: platform, human_readable_name: data[:human_readable_name],
+ architectures: parse_architectures(data[:download_locations])
+ }
+ end
+ end
+
+ private
+
+ def runner_instructions
+ Gitlab::Ci::RunnerInstructions::OS.merge(Gitlab::Ci::RunnerInstructions::OTHER_ENVIRONMENTS)
+ end
+
+ def parse_architectures(download_locations)
+ download_locations&.map do |architecture, download_location|
+ { name: architecture, download_location: download_location }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/group_issuable_resolver.rb b/app/graphql/resolvers/concerns/group_issuable_resolver.rb
new file mode 100644
index 00000000000..49a79683e9f
--- /dev/null
+++ b/app/graphql/resolvers/concerns/group_issuable_resolver.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module GroupIssuableResolver
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def include_subgroups(name_of_things)
+ argument :include_subgroups, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ default_value: false,
+ description: "Include #{name_of_things} belonging to subgroups"
+ end
+ end
+end
diff --git a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
index 2b14d8275d1..fe6fa0bb262 100644
--- a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
+++ b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
@@ -18,9 +18,15 @@ module IssueResolverArguments
argument :milestone_title, GraphQL::STRING_TYPE.to_list_type,
required: false,
description: 'Milestone applied to this issue'
+ argument :author_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of the author of the issue'
argument :assignee_username, GraphQL::STRING_TYPE,
required: false,
description: 'Username of a user assigned to the issue'
+ argument :assignee_usernames, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Usernames of users assigned to the issue'
argument :assignee_id, GraphQL::STRING_TYPE,
required: false,
description: 'ID of a user assigned to the issues, "none" and "any" values supported'
diff --git a/app/graphql/resolvers/concerns/looks_ahead.rb b/app/graphql/resolvers/concerns/looks_ahead.rb
index e7230287e13..61f23920ebb 100644
--- a/app/graphql/resolvers/concerns/looks_ahead.rb
+++ b/app/graphql/resolvers/concerns/looks_ahead.rb
@@ -3,8 +3,6 @@
module LooksAhead
extend ActiveSupport::Concern
- FEATURE_FLAG = :graphql_lookahead_support
-
included do
attr_accessor :lookahead
end
@@ -16,8 +14,6 @@ module LooksAhead
end
def apply_lookahead(query)
- return query unless Feature.enabled?(FEATURE_FLAG)
-
selection = node_selection
includes = preloads.each.flat_map do |name, requirements|
diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
index 0c01efd4f9a..ab83476ddea 100644
--- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb
+++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb
@@ -12,7 +12,7 @@ module ResolvesMergeRequests
def resolve_with_lookahead(**args)
mr_finder = MergeRequestsFinder.new(current_user, args.compact)
- finder = Gitlab::Graphql::Loaders::IssuableLoader.new(project, mr_finder)
+ finder = Gitlab::Graphql::Loaders::IssuableLoader.new(mr_parent, mr_finder)
select_result(finder.batching_find_all { |query| apply_lookahead(query) })
end
@@ -29,6 +29,10 @@ module ResolvesMergeRequests
private
+ def mr_parent
+ project
+ end
+
def unconditional_includes
[:target_project]
end
@@ -40,7 +44,8 @@ module ResolvesMergeRequests
author: [:author],
merged_at: [:metrics],
commit_count: [:metrics],
- approved_by: [:approver_users],
+ diff_stats_summary: [:metrics],
+ approved_by: [:approved_by_users],
milestone: [:milestone],
head_pipeline: [:merge_request_diff, { head_pipeline: [:merge_request] }]
}
diff --git a/app/graphql/resolvers/concerns/time_frame_arguments.rb b/app/graphql/resolvers/concerns/time_frame_arguments.rb
index ef333dd05a5..94bfe6f7f9f 100644
--- a/app/graphql/resolvers/concerns/time_frame_arguments.rb
+++ b/app/graphql/resolvers/concerns/time_frame_arguments.rb
@@ -3,21 +3,33 @@
module TimeFrameArguments
extend ActiveSupport::Concern
+ OVERLAPPING_TIMEFRAME_DESC = 'List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present)'
+
included do
argument :start_date, Types::TimeType,
required: false,
- description: 'List items within a time frame where items.start_date is between startDate and endDate parameters (endDate parameter must be present)'
+ description: OVERLAPPING_TIMEFRAME_DESC,
+ deprecated: { reason: 'Use timeframe.start', milestone: '13.5' }
argument :end_date, Types::TimeType,
required: false,
- description: 'List items within a time frame where items.end_date is between startDate and endDate parameters (startDate parameter must be present)'
+ description: OVERLAPPING_TIMEFRAME_DESC,
+ deprecated: { reason: 'Use timeframe.end', milestone: '13.5' }
+
+ argument :timeframe, Types::TimeframeInputType,
+ required: false,
+ description: 'List items overlapping the given timeframe'
end
+ # TODO: remove when the start_date and end_date arguments are removed
def validate_timeframe_params!(args)
- return unless args[:start_date].present? || args[:end_date].present?
+ return unless %i[start_date end_date timeframe].any? { |k| args[k].present? }
+ return if args[:timeframe] && %i[start_date end_date].all? { |k| args[k].nil? }
error_message =
- if args[:start_date].nil? || args[:end_date].nil?
+ if args[:timeframe].present?
+ "startDate and endDate are deprecated in favor of timeframe. Please use only timeframe."
+ elsif args[:start_date].nil? || args[:end_date].nil?
"Both startDate and endDate must be present."
elsif args[:start_date] > args[:end_date]
"startDate is after endDate"
diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb
index ac51011eea8..1fa6c78e730 100644
--- a/app/graphql/resolvers/group_issues_resolver.rb
+++ b/app/graphql/resolvers/group_issues_resolver.rb
@@ -2,9 +2,8 @@
module Resolvers
class GroupIssuesResolver < IssuesResolver
- argument :include_subgroups, GraphQL::BOOLEAN_TYPE,
- required: false,
- default_value: false,
- description: 'Include issues belonging to subgroups.'
+ include GroupIssuableResolver
+
+ include_subgroups 'issues'
end
end
diff --git a/app/graphql/resolvers/group_merge_requests_resolver.rb b/app/graphql/resolvers/group_merge_requests_resolver.rb
new file mode 100644
index 00000000000..5ee72e3f781
--- /dev/null
+++ b/app/graphql/resolvers/group_merge_requests_resolver.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class GroupMergeRequestsResolver < MergeRequestsResolver
+ include GroupIssuableResolver
+
+ alias_method :group, :synchronized_object
+
+ include_subgroups 'merge requests'
+ accept_assignee
+ accept_author
+
+ def project
+ nil
+ end
+
+ def mr_parent
+ group
+ end
+
+ def no_results_possible?(args)
+ group.nil? || some_argument_is_empty?(args)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/merge_requests_resolver.rb b/app/graphql/resolvers/merge_requests_resolver.rb
index 677f84e5795..cb4a76243ae 100644
--- a/app/graphql/resolvers/merge_requests_resolver.rb
+++ b/app/graphql/resolvers/merge_requests_resolver.rb
@@ -6,6 +6,18 @@ module Resolvers
alias_method :project, :synchronized_object
+ def self.accept_assignee
+ argument :assignee_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of the assignee'
+ end
+
+ def self.accept_author
+ argument :author_username, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Username of the author'
+ end
+
argument :iids, [GraphQL::STRING_TYPE],
required: false,
description: 'Array of IIDs of merge requests, for example `[1, 2]`'
diff --git a/app/graphql/resolvers/milestones_resolver.rb b/app/graphql/resolvers/milestones_resolver.rb
index 5f80506c01b..84712b674db 100644
--- a/app/graphql/resolvers/milestones_resolver.rb
+++ b/app/graphql/resolvers/milestones_resolver.rb
@@ -13,6 +13,18 @@ module Resolvers
required: false,
description: 'Filter milestones by state'
+ argument :title, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'The title of the milestone'
+
+ argument :search_title, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'A search string for the title'
+
+ argument :containing_date, Types::TimeType,
+ required: false,
+ description: 'A date that the milestone contains'
+
type Types::MilestoneType, null: true
def resolve(**args)
@@ -29,9 +41,18 @@ module Resolvers
{
ids: parse_gids(args[:ids]),
state: args[:state] || 'all',
- start_date: args[:start_date],
- end_date: args[:end_date]
- }.merge(parent_id_parameters(args))
+ title: args[:title],
+ search_title: args[:search_title],
+ containing_date: args[:containing_date]
+ }.merge!(timeframe_parameters(args)).merge!(parent_id_parameters(args))
+ end
+
+ def timeframe_parameters(args)
+ if args[:timeframe]
+ args[:timeframe].transform_keys { |k| :"#{k}_date" }
+ else
+ args.slice(:start_date, :end_date)
+ end
end
def parent
diff --git a/app/graphql/resolvers/project_merge_requests_resolver.rb b/app/graphql/resolvers/project_merge_requests_resolver.rb
index 0526ccd315f..ba13cb6e52c 100644
--- a/app/graphql/resolvers/project_merge_requests_resolver.rb
+++ b/app/graphql/resolvers/project_merge_requests_resolver.rb
@@ -2,11 +2,7 @@
module Resolvers
class ProjectMergeRequestsResolver < MergeRequestsResolver
- argument :assignee_username, GraphQL::STRING_TYPE,
- required: false,
- description: 'Username of the assignee'
- argument :author_username, GraphQL::STRING_TYPE,
- required: false,
- description: 'Username of the author'
+ accept_assignee
+ accept_author
end
end
diff --git a/app/graphql/resolvers/projects/jira_projects_resolver.rb b/app/graphql/resolvers/projects/jira_projects_resolver.rb
index ed382ac82d0..d017f973e17 100644
--- a/app/graphql/resolvers/projects/jira_projects_resolver.rb
+++ b/app/graphql/resolvers/projects/jira_projects_resolver.rb
@@ -22,7 +22,7 @@ module Resolvers
projects_array,
# override default max_page_size to whatever the size of the response is,
# see https://gitlab.com/gitlab-org/gitlab/-/issues/231394
- args.merge({ max_page_size: projects_array.size })
+ **args.merge({ max_page_size: projects_array.size })
)
else
raise Gitlab::Graphql::Errors::BaseError, response.message
diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb
index 3bbadf87a71..69438229a50 100644
--- a/app/graphql/resolvers/projects_resolver.rb
+++ b/app/graphql/resolvers/projects_resolver.rb
@@ -13,8 +13,16 @@ module Resolvers
description: 'Search query for project name, path, or description'
argument :ids, [GraphQL::ID_TYPE],
- required: false,
- description: 'Filter projects by IDs'
+ required: false,
+ description: 'Filter projects by IDs'
+
+ argument :search_namespaces, GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'Include namespace in project search'
+
+ argument :sort, GraphQL::STRING_TYPE,
+ required: false,
+ description: 'Sort order of results'
def resolve(**args)
ProjectsFinder
@@ -28,7 +36,9 @@ module Resolvers
{
without_deleted: true,
non_public: params[:membership],
- search: params[:search]
+ search: params[:search],
+ search_namespaces: params[:search_namespaces],
+ sort: params[:sort]
}.compact
end
diff --git a/app/graphql/resolvers/snippets/blobs_resolver.rb b/app/graphql/resolvers/snippets/blobs_resolver.rb
new file mode 100644
index 00000000000..dc28358cab6
--- /dev/null
+++ b/app/graphql/resolvers/snippets/blobs_resolver.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Snippets
+ class BlobsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ alias_method :snippet, :object
+
+ argument :paths, [GraphQL::STRING_TYPE],
+ required: false,
+ description: 'Paths of the blobs'
+
+ def resolve(**args)
+ authorize!(snippet)
+
+ return [snippet.blob] if snippet.empty_repo?
+
+ paths = Array(args.fetch(:paths, []))
+
+ if paths.empty?
+ snippet.blobs
+ else
+ snippet.repository.blobs_at(transformed_blob_paths(paths))
+ end
+ end
+
+ def authorized_resource?(snippet)
+ Ability.allowed?(context[:current_user], :read_snippet, snippet)
+ end
+
+ private
+
+ def transformed_blob_paths(paths)
+ ref = snippet.default_branch
+ paths.map { |path| [ref, path] }
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/terraform/states_resolver.rb b/app/graphql/resolvers/terraform/states_resolver.rb
new file mode 100644
index 00000000000..38b26a948b1
--- /dev/null
+++ b/app/graphql/resolvers/terraform/states_resolver.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Terraform
+ class StatesResolver < BaseResolver
+ type Types::Terraform::StateType, null: true
+
+ alias_method :project, :object
+
+ def resolve(**args)
+ return ::Terraform::State.none unless can_read_terraform_states?
+
+ project.terraform_states.ordered_by_name
+ end
+
+ private
+
+ def can_read_terraform_states?
+ current_user.can?(:read_terraform_state, project)
+ end
+ end
+ end
+end