diff options
Diffstat (limited to 'app/graphql/types')
38 files changed, 430 insertions, 85 deletions
diff --git a/app/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum.rb b/app/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum.rb index 13c67442c2e..c6ca5963588 100644 --- a/app/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum.rb +++ b/app/graphql/types/admin/analytics/instance_statistics/measurement_identifier_enum.rb @@ -8,12 +8,16 @@ module Types graphql_name 'MeasurementIdentifier' description 'Possible identifier types for a measurement' - value 'PROJECTS', 'Project count', value: :projects - value 'USERS', 'User count', value: :users - value 'ISSUES', 'Issue count', value: :issues - value 'MERGE_REQUESTS', 'Merge request count', value: :merge_requests - value 'GROUPS', 'Group count', value: :groups - value 'PIPELINES', 'Pipeline count', value: :pipelines + value 'PROJECTS', 'Project count', value: 'projects' + value 'USERS', 'User count', value: 'users' + value 'ISSUES', 'Issue count', value: 'issues' + value 'MERGE_REQUESTS', 'Merge request count', value: 'merge_requests' + value 'GROUPS', 'Group count', value: 'groups' + value 'PIPELINES', 'Pipeline count', value: 'pipelines' + value 'PIPELINES_SUCCEEDED', 'Pipeline count with success status', value: 'pipelines_succeeded' + value 'PIPELINES_FAILED', 'Pipeline count with failed status', value: 'pipelines_failed' + value 'PIPELINES_CANCELED', 'Pipeline count with canceled status', value: 'pipelines_canceled' + value 'PIPELINES_SKIPPED', 'Pipeline count with skipped status', value: 'pipelines_skipped' end end end diff --git a/app/graphql/types/alert_management/alert_status_counts_type.rb b/app/graphql/types/alert_management/alert_status_counts_type.rb index f80b289eabc..a84be705445 100644 --- a/app/graphql/types/alert_management/alert_status_counts_type.rb +++ b/app/graphql/types/alert_management/alert_status_counts_type.rb @@ -9,11 +9,11 @@ module Types authorize :read_alert_management_alert - ::Gitlab::AlertManagement::AlertStatusCounts::STATUSES.each_key do |status| + ::AlertManagement::Alert.status_names.each do |status| field status, GraphQL::INT_TYPE, null: true, - description: "Number of alerts with status #{status.upcase} for the project" + description: "Number of alerts with status #{status.to_s.upcase} for the project" end field :open, diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb index 2da97030b88..623762de208 100644 --- a/app/graphql/types/alert_management/alert_type.rb +++ b/app/graphql/types/alert_management/alert_type.rb @@ -40,7 +40,8 @@ module Types field :status, AlertManagement::StatusEnum, null: true, - description: 'Status of the alert' + description: 'Status of the alert', + method: :status_name field :service, GraphQL::STRING_TYPE, @@ -67,6 +68,11 @@ module Types null: true, description: 'Timestamp the alert ended' + field :environment, + Types::EnvironmentType, + null: true, + description: 'Environment for the alert' + field :event_count, GraphQL::INT_TYPE, null: true, diff --git a/app/graphql/types/alert_management/status_enum.rb b/app/graphql/types/alert_management/status_enum.rb index 4ff6c4a9505..9d2c7316254 100644 --- a/app/graphql/types/alert_management/status_enum.rb +++ b/app/graphql/types/alert_management/status_enum.rb @@ -6,8 +6,8 @@ module Types graphql_name 'AlertManagementStatus' description 'Alert status values' - ::AlertManagement::Alert::STATUSES.each do |name, value| - value name.upcase, value: value, description: "#{name.to_s.titleize} status" + ::AlertManagement::Alert.status_names.each do |status| + value status.to_s.upcase, value: status, description: "#{status.to_s.titleize} status" end end end diff --git a/app/graphql/types/base_argument.rb b/app/graphql/types/base_argument.rb new file mode 100644 index 00000000000..11774d0b59d --- /dev/null +++ b/app/graphql/types/base_argument.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Types + class BaseArgument < GraphQL::Schema::Argument + include GitlabStyleDeprecations + + def initialize(*args, **kwargs, &block) + kwargs = gitlab_deprecation(kwargs) + kwargs.delete(:deprecation_reason) + + super(*args, **kwargs, &block) + end + end +end diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb index 1e72a4cddf5..5c8aabfe163 100644 --- a/app/graphql/types/base_field.rb +++ b/app/graphql/types/base_field.rb @@ -5,6 +5,8 @@ module Types prepend Gitlab::Graphql::Authorize include GitlabStyleDeprecations + argument_class ::Types::BaseArgument + DEFAULT_COMPLEXITY = 1 def initialize(*args, **kwargs, &block) diff --git a/app/graphql/types/board_list_type.rb b/app/graphql/types/board_list_type.rb index 24faf1fe8bc..6ee76b0d1f1 100644 --- a/app/graphql/types/board_list_type.rb +++ b/app/graphql/types/board_list_type.rb @@ -32,17 +32,14 @@ module Types metadata[:size] end - def total_weight - metadata[:total_weight] - end - def metadata strong_memoize(:metadata) do list = self.object user = context[:current_user] + params = (context[:issue_filters] || {}).merge(board_id: list.board_id, id: list.id) ::Boards::Issues::ListService - .new(list.board.resource_parent, user, board_id: list.board_id, id: list.id) + .new(list.board.resource_parent, user, params) .metadata end end diff --git a/app/graphql/types/ci/detailed_status_type.rb b/app/graphql/types/ci/detailed_status_type.rb index 90b5283fc9a..f4a50115ee6 100644 --- a/app/graphql/types/ci/detailed_status_type.rb +++ b/app/graphql/types/ci/detailed_status_type.rb @@ -6,24 +6,39 @@ module Types class DetailedStatusType < BaseObject graphql_name 'DetailedStatus' - field :group, GraphQL::STRING_TYPE, null: false, - description: 'Group of the pipeline status' - field :icon, GraphQL::STRING_TYPE, null: false, - description: 'Icon of the pipeline status' - field :favicon, GraphQL::STRING_TYPE, null: false, - description: 'Favicon of the pipeline status' - field :details_path, GraphQL::STRING_TYPE, null: false, - description: 'Path of the details for the pipeline status' - field :has_details, GraphQL::BOOLEAN_TYPE, null: false, - description: 'Indicates if the pipeline status has further details', + field :group, GraphQL::STRING_TYPE, null: true, + description: 'Group of the status' + field :icon, GraphQL::STRING_TYPE, null: true, + description: 'Icon of the status' + field :favicon, GraphQL::STRING_TYPE, null: true, + description: 'Favicon of the status' + field :details_path, GraphQL::STRING_TYPE, null: true, + description: 'Path of the details for the status' + field :has_details, GraphQL::BOOLEAN_TYPE, null: true, + description: 'Indicates if the status has further details', method: :has_details? - field :label, GraphQL::STRING_TYPE, null: false, - description: 'Label of the pipeline status' - field :text, GraphQL::STRING_TYPE, null: false, - description: 'Text of the pipeline status' - field :tooltip, GraphQL::STRING_TYPE, null: false, - description: 'Tooltip associated with the pipeline status', + field :label, GraphQL::STRING_TYPE, null: true, + description: 'Label of the status' + field :text, GraphQL::STRING_TYPE, null: true, + description: 'Text of the status' + field :tooltip, GraphQL::STRING_TYPE, null: true, + description: 'Tooltip associated with the status', method: :status_tooltip + field :action, Types::Ci::StatusActionType, null: true, + description: 'Action information for the status. This includes method, button title, icon, path, and title', + resolve: -> (obj, _args, _ctx) { + if obj.has_action? + { + button_title: obj.action_button_title, + icon: obj.icon, + method: obj.action_method, + path: obj.action_path, + title: obj.action_title + } + else + nil + end + } end # rubocop: enable Graphql/AuthorizeTypes end diff --git a/app/graphql/types/ci/group_type.rb b/app/graphql/types/ci/group_type.rb index 04c0eb93068..d930ae311b7 100644 --- a/app/graphql/types/ci/group_type.rb +++ b/app/graphql/types/ci/group_type.rb @@ -12,6 +12,9 @@ module Types description: 'Size of the group' field :jobs, Ci::JobType.connection_type, null: true, description: 'Jobs in group' + field :detailed_status, Types::Ci::DetailedStatusType, null: true, + description: 'Detailed status of the group', + resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) } end end end diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb index 4c18f3ffd52..0ee1ad47b62 100644 --- a/app/graphql/types/ci/job_type.rb +++ b/app/graphql/types/ci/job_type.rb @@ -10,6 +10,11 @@ module Types description: 'Name of the job' field :needs, JobType.connection_type, null: true, description: 'Builds that must complete before the jobs run' + field :detailed_status, Types::Ci::DetailedStatusType, null: true, + description: 'Detailed status of the job', + resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) } + field :scheduled_at, Types::TimeType, null: true, + description: 'Schedule for the build' end end end diff --git a/app/graphql/types/ci/runner_architecture_type.rb b/app/graphql/types/ci/runner_architecture_type.rb new file mode 100644 index 00000000000..526348abd9d --- /dev/null +++ b/app/graphql/types/ci/runner_architecture_type.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Ci + # rubocop: disable Graphql/AuthorizeTypes + class RunnerArchitectureType < BaseObject + graphql_name 'RunnerArchitecture' + + field :name, GraphQL::STRING_TYPE, null: false, + description: 'Name of the runner platform architecture' + field :download_location, GraphQL::STRING_TYPE, null: false, + description: 'Download location for the runner for the platform architecture' + end + end +end diff --git a/app/graphql/types/ci/runner_platform_type.rb b/app/graphql/types/ci/runner_platform_type.rb new file mode 100644 index 00000000000..64719bc4908 --- /dev/null +++ b/app/graphql/types/ci/runner_platform_type.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Types + module Ci + # rubocop: disable Graphql/AuthorizeTypes + class RunnerPlatformType < BaseObject + graphql_name 'RunnerPlatform' + + field :name, GraphQL::STRING_TYPE, null: false, + description: 'Name slug of the runner platform' + field :human_readable_name, GraphQL::STRING_TYPE, null: false, + description: 'Human readable name of the runner platform' + field :architectures, Types::Ci::RunnerArchitectureType.connection_type, null: true, + description: 'Runner architectures supported for the platform' + end + end +end diff --git a/app/graphql/types/ci/stage_type.rb b/app/graphql/types/ci/stage_type.rb index 278c4d4d748..fc2c72d0d06 100644 --- a/app/graphql/types/ci/stage_type.rb +++ b/app/graphql/types/ci/stage_type.rb @@ -10,6 +10,9 @@ module Types description: 'Name of the stage' field :groups, Ci::GroupType.connection_type, null: true, description: 'Group of jobs for the stage' + field :detailed_status, Types::Ci::DetailedStatusType, null: true, + description: 'Detailed status of the stage', + resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) } end end end diff --git a/app/graphql/types/ci/status_action_type.rb b/app/graphql/types/ci/status_action_type.rb new file mode 100644 index 00000000000..08cbb6d3b59 --- /dev/null +++ b/app/graphql/types/ci/status_action_type.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true +module Types + module Ci + # rubocop: disable Graphql/AuthorizeTypes + class StatusActionType < BaseObject + graphql_name 'StatusAction' + + field :button_title, GraphQL::STRING_TYPE, null: true, + description: 'Title for the button, for example: Retry this job' + field :icon, GraphQL::STRING_TYPE, null: true, + description: 'Icon used in the action button' + field :method, GraphQL::STRING_TYPE, null: true, + description: 'Method for the action, for example: :post', + resolver_method: :action_method + field :path, GraphQL::STRING_TYPE, null: true, + description: 'Path for the action' + field :title, GraphQL::STRING_TYPE, null: true, + description: 'Title for the action, for example: Retry' + + def action_method + object[:method] + end + end + end +end diff --git a/app/graphql/types/date_type.rb b/app/graphql/types/date_type.rb new file mode 100644 index 00000000000..7129b75b8bb --- /dev/null +++ b/app/graphql/types/date_type.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Types + class DateType < BaseScalar + graphql_name 'Date' + description 'Date represented in ISO 8601' + + def self.coerce_input(value, ctx) + return if value.nil? + + Date.iso8601(value) + rescue ArgumentError, TypeError => e + raise GraphQL::CoercionError, e.message + end + + def self.coerce_result(value, ctx) + return if value.nil? + + value.to_date.iso8601 + end + end +end diff --git a/app/graphql/types/design_management/design_collection_copy_state_enum.rb b/app/graphql/types/design_management/design_collection_copy_state_enum.rb new file mode 100644 index 00000000000..7e7303c50ef --- /dev/null +++ b/app/graphql/types/design_management/design_collection_copy_state_enum.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Types + module DesignManagement + class DesignCollectionCopyStateEnum < BaseEnum + graphql_name 'DesignCollectionCopyState' + description 'Copy state of a DesignCollection' + + DESCRIPTION_VARIANTS = { + in_progress: 'is being copied', + error: 'encountered an error during a copy', + ready: 'has no copy in progress' + }.freeze + + def self.description_variant(copy_state) + DESCRIPTION_VARIANTS[copy_state.to_sym] || + (raise ArgumentError, "Unknown copy state: #{copy_state}") + end + + ::DesignManagement::DesignCollection.state_machines[:copy_state].states.keys.each do |copy_state| + value copy_state.upcase, + value: copy_state.to_s, + description: "The DesignCollection #{description_variant(copy_state)}" + end + end + end +end diff --git a/app/graphql/types/design_management/design_collection_type.rb b/app/graphql/types/design_management/design_collection_type.rb index 904fb270e11..9af1f4db425 100644 --- a/app/graphql/types/design_management/design_collection_type.rb +++ b/app/graphql/types/design_management/design_collection_type.rb @@ -39,6 +39,10 @@ module Types null: true, resolver: ::Resolvers::DesignManagement::DesignResolver, description: 'Find a specific design' + + field :copy_state, ::Types::DesignManagement::DesignCollectionCopyStateEnum, + null: true, + description: 'Copy state of the design collection' end end end diff --git a/app/graphql/types/design_management/design_type.rb b/app/graphql/types/design_management/design_type.rb index 4e11a7aaf09..bab22015dc4 100644 --- a/app/graphql/types/design_management/design_type.rb +++ b/app/graphql/types/design_management/design_type.rb @@ -30,7 +30,7 @@ module Types # most recent `Version` for an issue Gitlab::SafeRequestStore.fetch([request_cache_base_key, 'stateful_version', object.issue_id, version_gid]) do if version_gid - GitlabSchema.object_from_id(version_gid)&.sync + GitlabSchema.object_from_id(version_gid, expected_type: ::DesignManagement::Version)&.sync else object.issue.design_versions.most_recent end diff --git a/app/graphql/types/environment_type.rb b/app/graphql/types/environment_type.rb index 239b26f9c38..e4631a4a903 100644 --- a/app/graphql/types/environment_type.rb +++ b/app/graphql/types/environment_type.rb @@ -5,6 +5,8 @@ module Types graphql_name 'Environment' description 'Describes where code is deployed for a project' + present_using ::EnvironmentPresenter + authorize :read_environment field :name, GraphQL::STRING_TYPE, null: false, @@ -16,6 +18,10 @@ module Types field :state, GraphQL::STRING_TYPE, null: false, description: 'State of the environment, for example: available/stopped' + field :path, GraphQL::STRING_TYPE, null: true, + description: 'The path to the environment. Will always return null ' \ + 'if `expose_environment_path_in_alert_details` feature flag is disabled' + field :metrics_dashboard, Types::Metrics::DashboardType, null: true, description: 'Metrics dashboard schema for the environment', resolver: Resolvers::Metrics::DashboardResolver @@ -23,6 +29,6 @@ module Types field :latest_opened_most_severe_alert, Types::AlertManagement::AlertType, null: true, - description: 'The most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned.' + description: 'The most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned' end end diff --git a/app/graphql/types/global_id_type.rb b/app/graphql/types/global_id_type.rb index a3964ba83e1..9ae9ba32c13 100644 --- a/app/graphql/types/global_id_type.rb +++ b/app/graphql/types/global_id_type.rb @@ -1,5 +1,21 @@ # frozen_string_literal: true +module GraphQLExtensions + module ScalarExtensions + # Allow ID to unify with GlobalID Types + def ==(other) + if name == 'ID' && other.is_a?(self.class) && + other.type_class.ancestors.include?(::Types::GlobalIDType) + return true + end + + super + end + end +end + +::GraphQL::ScalarType.prepend(GraphQLExtensions::ScalarExtensions) + module Types class GlobalIDType < BaseScalar graphql_name 'GlobalID' diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index 60b2e3c7b6e..199cc0308c5 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -46,9 +46,15 @@ module Types field :issues, Types::IssueType.connection_type, null: true, - description: 'Issues of the group', + description: 'Issues for projects in this group', resolver: Resolvers::GroupIssuesResolver + field :merge_requests, + Types::MergeRequestType.connection_type, + null: true, + description: 'Merge requests for projects in this group', + resolver: Resolvers::GroupMergeRequestsResolver + field :milestones, Types::MilestoneType.connection_type, null: true, description: 'Milestones of the group', resolver: Resolvers::GroupMilestonesResolver @@ -64,7 +70,7 @@ module Types Types::BoardType, null: true, description: 'A single board of the group', - resolver: Resolvers::BoardsResolver.single + resolver: Resolvers::BoardResolver field :label, Types::LabelType, diff --git a/app/graphql/types/issue_sort_enum.rb b/app/graphql/types/issue_sort_enum.rb index e458d6e02c5..08762264b1b 100644 --- a/app/graphql/types/issue_sort_enum.rb +++ b/app/graphql/types/issue_sort_enum.rb @@ -8,6 +8,8 @@ module Types value 'DUE_DATE_ASC', 'Due date by ascending order', value: :due_date_asc value 'DUE_DATE_DESC', 'Due date by descending order', value: :due_date_desc value 'RELATIVE_POSITION_ASC', 'Relative position by ascending order', value: :relative_position_asc + value 'SEVERITY_ASC', 'Severity from less critical to more critical', value: :severity_asc + value 'SEVERITY_DESC', 'Severity from more critical to less critical', value: :severity_desc end end diff --git a/app/graphql/types/issue_state_event_enum.rb b/app/graphql/types/issue_state_event_enum.rb new file mode 100644 index 00000000000..6a9d840831d --- /dev/null +++ b/app/graphql/types/issue_state_event_enum.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Types + class IssueStateEventEnum < BaseEnum + graphql_name 'IssueStateEvent' + description 'Values for issue state events' + + value 'REOPEN', 'Reopens the issue', value: 'reopen' + value 'CLOSE', 'Closes the issue', value: 'close' + end +end diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb index d6253f74ce5..487508f448f 100644 --- a/app/graphql/types/issue_type.rb +++ b/app/graphql/types/issue_type.rb @@ -36,8 +36,7 @@ module Types end field :author, Types::UserType, null: false, - description: 'User that created the issue', - resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.author_id).find } + description: 'User that created the issue' field :assignees, Types::UserType.connection_type, null: true, description: 'Assignees of the issue' @@ -45,16 +44,14 @@ module Types field :labels, Types::LabelType.connection_type, null: true, description: 'Labels of the issue' field :milestone, Types::MilestoneType, null: true, - description: 'Milestone of the issue', - resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find } + description: 'Milestone of the issue' field :due_date, Types::TimeType, null: true, description: 'Due date of the issue' field :confidential, GraphQL::BOOLEAN_TYPE, null: false, description: 'Indicates the issue is confidential' field :discussion_locked, GraphQL::BOOLEAN_TYPE, null: false, - description: 'Indicates discussion is locked on the issue', - resolve: -> (obj, _args, _ctx) { !!obj.discussion_locked } + description: 'Indicates discussion is locked on the issue' field :upvotes, GraphQL::INT_TYPE, null: false, description: 'Number of upvotes the issue has received' @@ -108,6 +105,18 @@ module Types field :severity, Types::IssuableSeverityEnum, null: true, description: 'Severity level of the incident' + + def author + Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find + end + + def milestone + Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, object.milestone_id).find + end + + def discussion_locked + !!object.discussion_locked + end end end diff --git a/app/graphql/types/label_type.rb b/app/graphql/types/label_type.rb index 738a00ad616..28dee2a9db5 100644 --- a/app/graphql/types/label_type.rb +++ b/app/graphql/types/label_type.rb @@ -4,6 +4,8 @@ module Types class LabelType < BaseObject graphql_name 'Label' + connection_type_class(Types::CountableConnectionType) + authorize :read_label field :id, GraphQL::ID_TYPE, null: false, diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index 56c88491684..372aeac055b 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -152,6 +152,25 @@ module Types field :auto_merge_enabled, GraphQL::BOOLEAN_TYPE, null: false, description: 'Indicates if auto merge is enabled for the merge request' + field :approved_by, Types::UserType.connection_type, null: true, + description: 'Users who approved the merge request' + + def approved_by + object.approved_by_users + end + + # rubocop: disable CodeReuse/ActiveRecord + def user_notes_count + BatchLoader::GraphQL.for(object.id).batch(key: :merge_request_user_notes_count) do |ids, loader, args| + counts = Note.where(noteable_type: 'MergeRequest', noteable_id: ids).user.group(:noteable_id).count + + ids.each do |id| + loader.call(id, counts[id] || 0) + end + end + end + # rubocop: enable CodeReuse/ActiveRecord + def diff_stats(path: nil) stats = Array.wrap(object.diff_stats&.to_a) @@ -163,21 +182,12 @@ module Types end def diff_stats_summary - nil_stats = { additions: 0, deletions: 0, file_count: 0 } - return nil_stats unless object.diff_stats.present? - - object.diff_stats.each_with_object(nil_stats) do |status, hash| - hash.merge!(additions: status.additions, deletions: status.deletions, file_count: 1) { |_, x, y| x + y } - end + BatchLoaders::MergeRequestDiffSummaryBatchLoader.load_for(object) end def commit_count object&.metrics&.commits_count end - - def approvers - object.approver_users - end end end Types::MergeRequestType.prepend_if_ee('::EE::Types::MergeRequestType') diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index b2732d83aac..3f48e7b4a16 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -14,13 +14,16 @@ module Types mount_mutation Mutations::AwardEmojis::Add mount_mutation Mutations::AwardEmojis::Remove mount_mutation Mutations::AwardEmojis::Toggle + mount_mutation Mutations::Boards::Create mount_mutation Mutations::Boards::Destroy mount_mutation Mutations::Boards::Issues::IssueMoveList mount_mutation Mutations::Boards::Lists::Create mount_mutation Mutations::Boards::Lists::Update + mount_mutation Mutations::Boards::Lists::Destroy mount_mutation Mutations::Branches::Create, calls_gitaly: true mount_mutation Mutations::Commits::Create, calls_gitaly: true mount_mutation Mutations::Discussions::ToggleResolve + mount_mutation Mutations::Issues::Create mount_mutation Mutations::Issues::SetAssignees mount_mutation Mutations::Issues::SetConfidential mount_mutation Mutations::Issues::SetLocked @@ -28,6 +31,7 @@ module Types mount_mutation Mutations::Issues::SetSeverity mount_mutation Mutations::Issues::SetSubscription mount_mutation Mutations::Issues::Update + mount_mutation Mutations::Issues::Move mount_mutation Mutations::MergeRequests::Create mount_mutation Mutations::MergeRequests::Update mount_mutation Mutations::MergeRequests::SetLabels @@ -71,4 +75,5 @@ module Types end ::Types::MutationType.prepend(::Types::DeprecatedMutations) +::Types::MutationType.prepend_if_ee('EE::Types::DeprecatedMutations') ::Types::MutationType.prepend_if_ee('::EE::Types::MutationType') diff --git a/app/graphql/types/notes/noteable_type.rb b/app/graphql/types/notes/noteable_type.rb index 3a16d54f9cd..602634d9292 100644 --- a/app/graphql/types/notes/noteable_type.rb +++ b/app/graphql/types/notes/noteable_type.rb @@ -8,24 +8,24 @@ module Types field :notes, Types::Notes::NoteType.connection_type, null: false, description: "All notes on this noteable" field :discussions, Types::Notes::DiscussionType.connection_type, null: false, description: "All discussions on this noteable" - definition_methods do - def resolve_type(object, context) - case object - when Issue - Types::IssueType - when MergeRequest - Types::MergeRequestType - when Snippet - Types::SnippetType - when ::DesignManagement::Design - Types::DesignManagement::DesignType - when ::AlertManagement::Alert - Types::AlertManagement::AlertType - else - raise "Unknown GraphQL type for #{object}" - end + def self.resolve_type(object, context) + case object + when Issue + Types::IssueType + when MergeRequest + Types::MergeRequestType + when Snippet + Types::SnippetType + when ::DesignManagement::Design + Types::DesignManagement::DesignType + when ::AlertManagement::Alert + Types::AlertManagement::AlertType + else + raise "Unknown GraphQL type for #{object}" end end end end end + +Types::Notes::NoteableType.prepend_if_ee('::EE::Types::Notes::NoteableType') diff --git a/app/graphql/types/package_type_enum.rb b/app/graphql/types/package_type_enum.rb index bc03b8f5f8b..6f50c166da3 100644 --- a/app/graphql/types/package_type_enum.rb +++ b/app/graphql/types/package_type_enum.rb @@ -2,8 +2,14 @@ module Types class PackageTypeEnum < BaseEnum + PACKAGE_TYPE_NAMES = { + pypi: 'PyPI', + npm: 'NPM' + }.freeze + ::Packages::Package.package_types.keys.each do |package_type| - value package_type.to_s.upcase, "Packages from the #{package_type} package manager", value: package_type.to_s + type_name = PACKAGE_TYPE_NAMES.fetch(package_type.to_sym, package_type.capitalize) + value package_type.to_s.upcase, "Packages from the #{type_name} package manager", value: package_type.to_s end end end diff --git a/app/graphql/types/project_member_type.rb b/app/graphql/types/project_member_type.rb index f08781238d0..01731531ae2 100644 --- a/app/graphql/types/project_member_type.rb +++ b/app/graphql/types/project_member_type.rb @@ -12,7 +12,10 @@ module Types authorize :read_project field :project, Types::ProjectType, null: true, - description: 'Project that User is a member of', - resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, obj.source_id).find } + description: 'Project that User is a member of' + + def project + Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.source_id).find + end end end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 0fd54af1538..c7fc193abe8 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -234,7 +234,7 @@ module Types Types::BoardType, null: true, description: 'A single board of the project', - resolver: Resolvers::BoardsResolver.single + resolver: Resolvers::BoardResolver field :jira_imports, Types::JiraImportType.connection_type, @@ -294,6 +294,12 @@ module Types description: 'Title of the label' end + field :terraform_states, + Types::Terraform::StateType.connection_type, + null: true, + description: 'Terraform states associated with the project', + resolver: Resolvers::Terraform::StatesResolver + def label(title:) BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args| LabelsFinder diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 447ac63a294..bd4b53bdaa7 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -49,8 +49,7 @@ module Types field :milestone, ::Types::MilestoneType, null: true, - description: 'Find a milestone', - resolve: -> (_obj, args, _ctx) { GitlabSchema.find_by_gid(args[:id]) } do + description: 'Find a milestone' do argument :id, ::Types::GlobalIDType[Milestone], required: true, description: 'Find a milestone by its ID' @@ -81,12 +80,26 @@ module Types description: 'Get statistics on the instance', resolver: Resolvers::Admin::Analytics::InstanceStatistics::MeasurementsResolver + field :runner_platforms, Types::Ci::RunnerPlatformType.connection_type, + null: true, description: 'Supported runner platforms', + resolver: Resolvers::Ci::RunnerPlatformsResolver + def design_management DesignManagementObject.new(nil) end def issue(id:) - GitlabSchema.object_from_id(id, expected_type: ::Issue) + # TODO: remove this line when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + id = ::Types::GlobalIDType[::Issue].coerce_isolated_input(id) + GitlabSchema.find_by_gid(id) + end + + def milestone(id:) + # TODO: remove this line when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + id = ::Types::GlobalIDType[Milestone].coerce_isolated_input(id) + GitlabSchema.find_by_gid(id) end end end diff --git a/app/graphql/types/range_input_type.rb b/app/graphql/types/range_input_type.rb new file mode 100644 index 00000000000..766e523a99e --- /dev/null +++ b/app/graphql/types/range_input_type.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Types + # rubocop: disable Graphql/AuthorizeTypes + class RangeInputType < BaseInputObject + def self.[](type, closed = true) + @subtypes ||= {} + + @subtypes[[type, closed]] ||= Class.new(self) do + argument :start, type, + required: closed, + description: 'The start of the range' + + argument :end, type, + required: closed, + description: 'The end of the range' + end + end + + def prepare + if self[:end] && self[:start] && self[:end] < self[:start] + raise ::Gitlab::Graphql::Errors::ArgumentError, 'start must be before end' + end + + to_h + end + end + # rubocop: enable Graphql/AuthorizeTypes +end diff --git a/app/graphql/types/root_storage_statistics_type.rb b/app/graphql/types/root_storage_statistics_type.rb index 3acc1d9ca44..224e8c7ee03 100644 --- a/app/graphql/types/root_storage_statistics_type.rb +++ b/app/graphql/types/root_storage_statistics_type.rb @@ -13,5 +13,6 @@ module Types field :packages_size, GraphQL::FLOAT_TYPE, null: false, description: 'The packages size in bytes' field :wiki_size, GraphQL::FLOAT_TYPE, null: false, description: 'The wiki size in bytes' field :snippets_size, GraphQL::FLOAT_TYPE, null: false, description: 'The snippets size in bytes' + field :pipeline_artifacts_size, GraphQL::FLOAT_TYPE, null: false, description: 'The CI pipeline artifacts size in bytes' end end diff --git a/app/graphql/types/snippet_type.rb b/app/graphql/types/snippet_type.rb index db98e62c10a..495c25c1776 100644 --- a/app/graphql/types/snippet_type.rb +++ b/app/graphql/types/snippet_type.rb @@ -24,16 +24,14 @@ module Types field :project, Types::ProjectType, description: 'The project the snippet is associated with', null: true, - authorize: :read_project, - resolve: -> (snippet, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, snippet.project_id).find } + authorize: :read_project # Author can be nil in some scenarios. For example, # when the admin setting restricted visibility # level is set to public field :author, Types::UserType, description: 'The owner of the snippet', - null: true, - resolve: -> (snippet, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, snippet.author_id).find } + null: true field :file_name, GraphQL::STRING_TYPE, description: 'File Name of the snippet', @@ -69,10 +67,11 @@ module Types null: false, deprecated: { reason: 'Use `blobs`', milestone: '13.3' } - field :blobs, type: [Types::Snippets::BlobType], + field :blobs, type: Types::Snippets::BlobType.connection_type, description: 'Snippet blobs', calls_gitaly: true, - null: false + null: true, + resolver: Resolvers::Snippets::BlobsResolver field :ssh_url_to_repo, type: GraphQL::STRING_TYPE, description: 'SSH URL to the snippet repository', @@ -85,5 +84,13 @@ module Types null: true markdown_field :description_html, null: true, method: :description + + def author + Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find + end + + def project + Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find + end end end diff --git a/app/graphql/types/sort_enum.rb b/app/graphql/types/sort_enum.rb index 3245cb33e0d..d0a6eecb672 100644 --- a/app/graphql/types/sort_enum.rb +++ b/app/graphql/types/sort_enum.rb @@ -5,9 +5,16 @@ module Types graphql_name 'Sort' description 'Common sort values' - value 'updated_desc', 'Updated at descending order' - value 'updated_asc', 'Updated at ascending order' - value 'created_desc', 'Created at descending order' - value 'created_asc', 'Created at ascending order' + # Deprecated, as we prefer uppercase enums + # https://gitlab.com/groups/gitlab-org/-/epics/1838 + value 'updated_desc', 'Updated at descending order', deprecated: { reason: 'Use UPDATED_DESC', milestone: '13.5' } + value 'updated_asc', 'Updated at ascending order', deprecated: { reason: 'Use UPDATED_ASC', milestone: '13.5' } + value 'created_desc', 'Created at descending order', deprecated: { reason: 'Use CREATED_DESC', milestone: '13.5' } + value 'created_asc', 'Created at ascending order', deprecated: { reason: 'Use CREATED_ASC', milestone: '13.5' } + + value 'UPDATED_DESC', 'Updated at descending order', value: :updated_desc + value 'UPDATED_ASC', 'Updated at ascending order', value: :updated_asc + value 'CREATED_DESC', 'Created at descending order', value: :created_desc + value 'CREATED_ASC', 'Created at ascending order', value: :created_asc end end diff --git a/app/graphql/types/terraform/state_type.rb b/app/graphql/types/terraform/state_type.rb new file mode 100644 index 00000000000..f25f3a7789b --- /dev/null +++ b/app/graphql/types/terraform/state_type.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Types + module Terraform + class StateType < BaseObject + graphql_name 'TerraformState' + + authorize :read_terraform_state + + field :id, GraphQL::ID_TYPE, + null: false, + description: 'ID of the Terraform state' + + field :name, GraphQL::STRING_TYPE, + null: false, + description: 'Name of the Terraform state' + + field :locked_by_user, Types::UserType, + null: true, + authorize: :read_user, + description: 'The user currently holding a lock on the Terraform state', + resolve: -> (state, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, state.locked_by_user_id).find } + + field :locked_at, Types::TimeType, + null: true, + description: 'Timestamp the Terraform state was locked' + + field :created_at, Types::TimeType, + null: false, + description: 'Timestamp the Terraform state was created' + + field :updated_at, Types::TimeType, + null: false, + description: 'Timestamp the Terraform state was updated' + end + end +end diff --git a/app/graphql/types/timeframe_input_type.rb b/app/graphql/types/timeframe_input_type.rb new file mode 100644 index 00000000000..79c1bc5cf01 --- /dev/null +++ b/app/graphql/types/timeframe_input_type.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Types + # rubocop: disable Graphql/AuthorizeTypes + class TimeframeInputType < RangeInputType[::Types::DateType] + graphql_name 'Timeframe' + description 'A time-frame defined as a closed inclusive range of two dates' + end + # rubocop: enable Graphql/AuthorizeTypes +end |