diff options
Diffstat (limited to 'app/graphql/types')
46 files changed, 655 insertions, 34 deletions
diff --git a/app/graphql/types/access_level_enum.rb b/app/graphql/types/access_level_enum.rb new file mode 100644 index 00000000000..6754d3d28ce --- /dev/null +++ b/app/graphql/types/access_level_enum.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + class AccessLevelEnum < BaseEnum + graphql_name 'AccessLevelEnum' + description 'Access level to a resource' + + value 'NO_ACCESS', value: Gitlab::Access::NO_ACCESS + value 'GUEST', value: Gitlab::Access::GUEST + value 'REPORTER', value: Gitlab::Access::REPORTER + value 'DEVELOPER', value: Gitlab::Access::DEVELOPER + value 'MAINTAINER', value: Gitlab::Access::MAINTAINER + value 'OWNER', value: Gitlab::Access::OWNER + end +end diff --git a/app/graphql/types/access_level_type.rb b/app/graphql/types/access_level_type.rb new file mode 100644 index 00000000000..c7f915f5038 --- /dev/null +++ b/app/graphql/types/access_level_type.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +# rubocop:disable Graphql/AuthorizeTypes + +module Types + class AccessLevelType < Types::BaseObject + graphql_name 'AccessLevel' + description 'Represents the access level of a relationship between a User and object that it is related to' + + field :integer_value, GraphQL::INT_TYPE, null: true, + description: 'Integer representation of access level', + method: :to_i + + field :string_value, Types::AccessLevelEnum, null: true, + description: 'String representation of access level', + method: :to_i + end +end diff --git a/app/graphql/types/alert_management/alert_sort_enum.rb b/app/graphql/types/alert_management/alert_sort_enum.rb index e6d38af8170..3faac9ce53c 100644 --- a/app/graphql/types/alert_management/alert_sort_enum.rb +++ b/app/graphql/types/alert_management/alert_sort_enum.rb @@ -6,16 +6,16 @@ module Types graphql_name 'AlertManagementAlertSort' description 'Values for sorting alerts' - value 'START_TIME_ASC', 'Start time by ascending order', value: :start_time_asc - value 'START_TIME_DESC', 'Start time by descending order', value: :start_time_desc - value 'END_TIME_ASC', 'End time by ascending order', value: :end_time_asc - value 'END_TIME_DESC', 'End time by descending order', value: :end_time_desc + value 'STARTED_AT_ASC', 'Start time by ascending order', value: :started_at_asc + value 'STARTED_AT_DESC', 'Start time by descending order', value: :started_at_desc + value 'ENDED_AT_ASC', 'End time by ascending order', value: :ended_at_asc + value 'ENDED_AT_DESC', 'End time by descending order', value: :ended_at_desc value 'CREATED_TIME_ASC', 'Created time by ascending order', value: :created_at_asc value 'CREATED_TIME_DESC', 'Created time by descending order', value: :created_at_desc value 'UPDATED_TIME_ASC', 'Created time by ascending order', value: :updated_at_asc value 'UPDATED_TIME_DESC', 'Created time by descending order', value: :updated_at_desc - value 'EVENTS_COUNT_ASC', 'Events count by ascending order', value: :events_count_asc - value 'EVENTS_COUNT_DESC', 'Events count by descending order', value: :events_count_desc + value 'EVENT_COUNT_ASC', 'Events count by ascending order', value: :event_count_asc + value 'EVENT_COUNT_DESC', 'Events count by descending order', value: :event_count_desc value 'SEVERITY_ASC', 'Severity by ascending order', value: :severity_asc value 'SEVERITY_DESC', 'Severity by descending order', value: :severity_desc value 'STATUS_ASC', 'Status by ascending order', value: :status_asc diff --git a/app/graphql/types/alert_management/alert_type.rb b/app/graphql/types/alert_management/alert_type.rb index a766fb3236d..8215ccb152c 100644 --- a/app/graphql/types/alert_management/alert_type.rb +++ b/app/graphql/types/alert_management/alert_type.rb @@ -6,6 +6,8 @@ module Types graphql_name 'AlertManagementAlert' description "Describes an alert from the project's Alert Management" + implements(Types::Notes::NoteableType) + authorize :read_alert_management_alert field :iid, @@ -83,6 +85,15 @@ module Types Types::TimeType, null: true, description: 'Timestamp the alert was last updated' + + field :assignees, + Types::UserType.connection_type, + null: true, + description: 'Assignees of the alert' + + def notes + object.ordered_notes + end end end end diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb index dad16898ba6..70e665f8fc3 100644 --- a/app/graphql/types/base_object.rb +++ b/app/graphql/types/base_object.rb @@ -12,5 +12,9 @@ module Types def id GitlabSchema.id_from_object(object) end + + def current_user + context[:current_user] + end end end diff --git a/app/graphql/types/board_type.rb b/app/graphql/types/board_type.rb index c0be782ed1e..f5dc9e08427 100644 --- a/app/graphql/types/board_type.rb +++ b/app/graphql/types/board_type.rb @@ -15,7 +15,7 @@ module Types field :lists, Types::BoardListType.connection_type, null: true, - description: 'Lists of the project board', + description: 'Lists of the board', resolver: Resolvers::BoardListsResolver, extras: [:lookahead] end diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb index d77b2a2ba32..32050766e5b 100644 --- a/app/graphql/types/ci/pipeline_type.rb +++ b/app/graphql/types/ci/pipeline_type.rb @@ -42,3 +42,5 @@ module Types end end end + +Types::Ci::PipelineType.prepend_if_ee('::EE::Types::Ci::PipelineType') diff --git a/app/graphql/types/commit_action_mode_enum.rb b/app/graphql/types/commit_action_mode_enum.rb new file mode 100644 index 00000000000..77658a85b51 --- /dev/null +++ b/app/graphql/types/commit_action_mode_enum.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Types + class CommitActionModeEnum < BaseEnum + graphql_name 'CommitActionMode' + description 'Mode of a commit action' + + value 'CREATE', description: 'Create command', value: :create + value 'DELETE', description: 'Delete command', value: :delete + value 'MOVE', description: 'Move command', value: :move + value 'UPDATE', description: 'Update command', value: :update + value 'CHMOD', description: 'Chmod command', value: :chmod + end +end diff --git a/app/graphql/types/commit_action_type.rb b/app/graphql/types/commit_action_type.rb new file mode 100644 index 00000000000..7674abb11eb --- /dev/null +++ b/app/graphql/types/commit_action_type.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Types + # rubocop: disable Graphql/AuthorizeTypes + class CommitActionType < BaseInputObject + argument :action, type: Types::CommitActionModeEnum, required: true, + description: 'The action to perform, create, delete, move, update, chmod' + argument :file_path, type: GraphQL::STRING_TYPE, required: true, + description: 'Full path to the file' + argument :content, type: GraphQL::STRING_TYPE, required: false, + description: 'Content of the file' + argument :previous_path, type: GraphQL::STRING_TYPE, required: false, + description: 'Original full path to the file being moved' + argument :last_commit_id, type: GraphQL::STRING_TYPE, required: false, + description: 'Last known file commit ID' + argument :execute_filemode, type: GraphQL::BOOLEAN_TYPE, required: false, + description: 'Enables/disables the execute flag on the file' + argument :encoding, type: Types::CommitEncodingEnum, required: false, + description: 'Encoding of the file. Default is text' + end + # rubocop: enable Graphql/AuthorizeTypes +end diff --git a/app/graphql/types/commit_encoding_enum.rb b/app/graphql/types/commit_encoding_enum.rb new file mode 100644 index 00000000000..0ea89b82db7 --- /dev/null +++ b/app/graphql/types/commit_encoding_enum.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Types + class CommitEncodingEnum < BaseEnum + graphql_name 'CommitEncoding' + + value 'TEXT', description: 'Text encoding', value: :text + value 'BASE64', description: 'Base64 encoding', value: :base64 + end +end diff --git a/app/graphql/types/container_expiration_policy_cadence_enum.rb b/app/graphql/types/container_expiration_policy_cadence_enum.rb new file mode 100644 index 00000000000..bb8bdf2197b --- /dev/null +++ b/app/graphql/types/container_expiration_policy_cadence_enum.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Types + class ContainerExpirationPolicyCadenceEnum < BaseEnum + OPTIONS_MAPPING = { + '1d': 'EVERY_DAY', + '7d': 'EVERY_WEEK', + '14d': 'EVERY_TWO_WEEKS', + '1month': 'EVERY_MONTH', + '3month': 'EVERY_THREE_MONTHS' + }.freeze + + ::ContainerExpirationPolicy.cadence_options.each do |option, description| + value OPTIONS_MAPPING[option], description, value: option.to_s + end + end +end diff --git a/app/graphql/types/container_expiration_policy_keep_enum.rb b/app/graphql/types/container_expiration_policy_keep_enum.rb new file mode 100644 index 00000000000..7632df61092 --- /dev/null +++ b/app/graphql/types/container_expiration_policy_keep_enum.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Types + class ContainerExpirationPolicyKeepEnum < BaseEnum + OPTIONS_MAPPING = { + 1 => 'ONE_TAG', + 5 => 'FIVE_TAGS', + 10 => 'TEN_TAGS', + 25 => 'TWENTY_FIVE_TAGS', + 50 => 'FIFTY_TAGS', + 100 => 'ONE_HUNDRED_TAGS' + }.freeze + + ::ContainerExpirationPolicy.keep_n_options.each do |option, description| + value OPTIONS_MAPPING[option], description, value: option + end + end +end diff --git a/app/graphql/types/container_expiration_policy_older_than_enum.rb b/app/graphql/types/container_expiration_policy_older_than_enum.rb new file mode 100644 index 00000000000..da70534b0d7 --- /dev/null +++ b/app/graphql/types/container_expiration_policy_older_than_enum.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Types + class ContainerExpirationPolicyOlderThanEnum < BaseEnum + OPTIONS_MAPPING = { + '7d': 'SEVEN_DAYS', + '14d': 'FOURTEEN_DAYS', + '30d': 'THIRTY_DAYS', + '90d': 'NINETY_DAYS' + }.freeze + + ::ContainerExpirationPolicy.older_than_options.each do |option, description| + value OPTIONS_MAPPING[option], description, value: option.to_s + end + end +end diff --git a/app/graphql/types/container_expiration_policy_type.rb b/app/graphql/types/container_expiration_policy_type.rb new file mode 100644 index 00000000000..da53dbcbd39 --- /dev/null +++ b/app/graphql/types/container_expiration_policy_type.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Types + class ContainerExpirationPolicyType < BaseObject + graphql_name 'ContainerExpirationPolicy' + + description 'A tag expiration policy designed to keep only the images that matter most' + + authorize :destroy_container_image + + field :created_at, Types::TimeType, null: false, description: 'Timestamp of when the container expiration policy was created' + field :updated_at, Types::TimeType, null: false, description: 'Timestamp of when the container expiration policy was updated' + field :enabled, GraphQL::BOOLEAN_TYPE, null: false, description: 'Indicates whether this container expiration policy is enabled' + field :older_than, Types::ContainerExpirationPolicyOlderThanEnum, null: true, description: 'Tags older that this will expire' + field :cadence, Types::ContainerExpirationPolicyCadenceEnum, null: false, description: 'This container expiration policy schedule' + field :keep_n, Types::ContainerExpirationPolicyKeepEnum, null: true, description: 'Number of tags to retain' + field :name_regex, GraphQL::STRING_TYPE, null: true, description: 'Tags with names matching this regex pattern will expire' + field :name_regex_keep, GraphQL::STRING_TYPE, null: true, description: 'Tags with names matching this regex pattern will be preserved' + field :next_run_at, Types::TimeType, null: true, description: 'Next time that this container expiration policy will get executed' + end +end diff --git a/app/graphql/types/evidence_type.rb b/app/graphql/types/evidence_type.rb new file mode 100644 index 00000000000..a2fc9953c67 --- /dev/null +++ b/app/graphql/types/evidence_type.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Types + class EvidenceType < BaseObject + graphql_name 'ReleaseEvidence' + description 'Evidence for a release' + + authorize :download_code + + present_using Releases::EvidencePresenter + + field :id, GraphQL::ID_TYPE, null: false, + description: 'ID of the evidence' + field :sha, GraphQL::STRING_TYPE, null: true, + description: 'SHA1 ID of the evidence hash' + field :filepath, GraphQL::STRING_TYPE, null: true, + description: 'URL from where the evidence can be downloaded' + field :collected_at, Types::TimeType, null: true, + description: 'Timestamp when the evidence was collected' + end +end diff --git a/app/graphql/types/group_member_type.rb b/app/graphql/types/group_member_type.rb new file mode 100644 index 00000000000..ffffa3247db --- /dev/null +++ b/app/graphql/types/group_member_type.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Types + class GroupMemberType < BaseObject + expose_permissions Types::PermissionTypes::Group + authorize :read_group + + implements MemberInterface + + graphql_name 'GroupMember' + description 'Represents a Group Member' + + field :group, Types::GroupType, null: true, + description: 'Group that a User is a member of', + resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.source_id).find } + end +end diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index 20b4c66ba95..fd7d9a9ba3d 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -65,6 +65,45 @@ module Types null: true, description: 'A single board of the group', resolver: Resolvers::BoardsResolver.single + + field :label, + Types::LabelType, + null: true, + description: 'A label available on this group' do + argument :title, GraphQL::STRING_TYPE, + required: true, + description: 'Title of the label' + end + + def label(title:) + BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args| + LabelsFinder + .new(current_user, group: args[:key], title: titles) + .execute + .each { |label| loader.call(label.title, label) } + end + end + + field :labels, + Types::LabelType.connection_type, + null: true, + description: 'Labels available on this group' do + argument :search_term, GraphQL::STRING_TYPE, + required: false, + description: 'A search term to find labels with' + end + + def labels(search_term: nil) + LabelsFinder + .new(current_user, group: group, search: search_term) + .execute + end + + private + + def group + object.respond_to?(:sync) ? object.sync : object + end end end diff --git a/app/graphql/types/jira_import_type.rb b/app/graphql/types/jira_import_type.rb index 4a124566ffb..cf58a53b40d 100644 --- a/app/graphql/types/jira_import_type.rb +++ b/app/graphql/types/jira_import_type.rb @@ -15,6 +15,12 @@ module Types description: 'User that started the Jira import' field :jira_project_key, GraphQL::STRING_TYPE, null: false, description: 'Project key for the imported Jira project' + field :imported_issues_count, GraphQL::INT_TYPE, null: false, + description: 'Count of issues that were successfully imported' + field :failed_to_import_count, GraphQL::INT_TYPE, null: false, + description: 'Count of issues that failed to import' + field :total_issue_count, GraphQL::INT_TYPE, null: false, + description: 'Total count of issues that were attempted to import' end # rubocop: enable Graphql/AuthorizeTypes end diff --git a/app/graphql/types/jira_user_type.rb b/app/graphql/types/jira_user_type.rb new file mode 100644 index 00000000000..8aa21ce669b --- /dev/null +++ b/app/graphql/types/jira_user_type.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Types + # rubocop: disable Graphql/AuthorizeTypes + # Authorization is at project level for owners or admins on mutation level + class JiraUserType < BaseObject + graphql_name 'JiraUser' + + field :jira_account_id, GraphQL::STRING_TYPE, null: false, + description: 'Account id of the Jira user' + field :jira_display_name, GraphQL::STRING_TYPE, null: false, + description: 'Display name of the Jira user' + field :jira_email, GraphQL::STRING_TYPE, null: true, + description: 'Email of the Jira user, returned only for users with public emails' + field :gitlab_id, GraphQL::INT_TYPE, null: true, + description: 'Id of the matched GitLab user' + end + # rubocop: enable Graphql/AuthorizeTypes +end diff --git a/app/graphql/types/member_interface.rb b/app/graphql/types/member_interface.rb new file mode 100644 index 00000000000..976836221bc --- /dev/null +++ b/app/graphql/types/member_interface.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Types + module MemberInterface + include BaseInterface + + field :access_level, Types::AccessLevelType, null: true, + description: 'GitLab::Access level' + + field :created_by, Types::UserType, null: true, + description: 'User that authorized membership' + + field :created_at, Types::TimeType, null: true, + description: 'Date and time the membership was created' + + field :updated_at, Types::TimeType, null: true, + description: 'Date and time the membership was last updated' + + field :expires_at, Types::TimeType, null: true, + description: 'Date and time the membership expires' + end +end diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index cd4c6b4d46a..cb4ff7ea0c5 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -28,6 +28,8 @@ module Types description: 'Timestamp of when the merge request was created' field :updated_at, Types::TimeType, null: false, description: 'Timestamp of when the merge request was last updated' + field :merged_at, Types::TimeType, null: true, complexity: 5, + description: 'Timestamp of when the merge request was merged, null if not merged' field :source_project, Types::ProjectType, null: true, description: 'Source project of the merge request' field :target_project, Types::ProjectType, null: false, @@ -81,8 +83,14 @@ module Types description: 'Default merge commit message of the merge request' field :merge_ongoing, GraphQL::BOOLEAN_TYPE, method: :merge_ongoing?, null: false, description: 'Indicates if a merge is currently occurring' - field :source_branch_exists, GraphQL::BOOLEAN_TYPE, method: :source_branch_exists?, null: false, + field :source_branch_exists, GraphQL::BOOLEAN_TYPE, + null: false, calls_gitaly: true, + method: :source_branch_exists?, description: 'Indicates if the source branch of the merge request exists' + field :target_branch_exists, GraphQL::BOOLEAN_TYPE, + null: false, calls_gitaly: true, + method: :target_branch_exists?, + description: 'Indicates if the target branch of the merge request exists' field :mergeable_discussions_state, GraphQL::BOOLEAN_TYPE, null: true, description: 'Indicates if all discussions in the merge request have been resolved, allowing the merge request to be merged' field :web_url, GraphQL::STRING_TYPE, null: true, @@ -103,6 +111,8 @@ module Types resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find } field :assignees, Types::UserType.connection_type, null: true, complexity: 5, description: 'Assignees of the merge request' + field :author, Types::UserType, null: true, + description: 'User who created this merge request' field :participants, Types::UserType.connection_type, null: true, complexity: 5, description: 'Participants in the merge request' field :subscribed, GraphQL::BOOLEAN_TYPE, method: :subscribed?, null: false, complexity: 5, diff --git a/app/graphql/types/metrics/dashboard_type.rb b/app/graphql/types/metrics/dashboard_type.rb index d684533ff94..bbcce2d9596 100644 --- a/app/graphql/types/metrics/dashboard_type.rb +++ b/app/graphql/types/metrics/dashboard_type.rb @@ -10,6 +10,9 @@ module Types field :path, GraphQL::STRING_TYPE, null: true, description: 'Path to a file with the dashboard definition' + field :schema_validation_warnings, [GraphQL::STRING_TYPE], null: true, + description: 'Dashboard schema validation warnings' + field :annotations, Types::Metrics::Dashboards::AnnotationType.connection_type, null: true, description: 'Annotations added to the dashboard', resolver: Resolvers::Metrics::Dashboards::AnnotationResolver diff --git a/app/graphql/types/milestone_type.rb b/app/graphql/types/milestone_type.rb index 900f8c6f01d..99bd6e819d6 100644 --- a/app/graphql/types/milestone_type.rb +++ b/app/graphql/types/milestone_type.rb @@ -35,5 +35,17 @@ module Types field :updated_at, Types::TimeType, null: false, description: 'Timestamp of last milestone update' + + field :project_milestone, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Indicates if milestone is at project level', + method: :project_milestone? + + field :group_milestone, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Indicates if milestone is at group level', + method: :group_milestone? + + field :subgroup_milestone, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Indicates if milestone is at subgroup level', + method: :subgroup_milestone? end end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index aeff84b83b8..8874c56dfdb 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -9,13 +9,17 @@ module Types mount_mutation Mutations::Admin::SidekiqQueues::DeleteJobs mount_mutation Mutations::AlertManagement::CreateAlertIssue mount_mutation Mutations::AlertManagement::UpdateAlertStatus + mount_mutation Mutations::AlertManagement::Alerts::SetAssignees mount_mutation Mutations::AwardEmojis::Add mount_mutation Mutations::AwardEmojis::Remove mount_mutation Mutations::AwardEmojis::Toggle 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::SetConfidential mount_mutation Mutations::Issues::SetDueDate mount_mutation Mutations::Issues::Update + mount_mutation Mutations::MergeRequests::Create mount_mutation Mutations::MergeRequests::SetLabels mount_mutation Mutations::MergeRequests::SetLocked mount_mutation Mutations::MergeRequests::SetMilestone @@ -23,6 +27,7 @@ module Types mount_mutation Mutations::MergeRequests::SetWip, calls_gitaly: true mount_mutation Mutations::MergeRequests::SetAssignees mount_mutation Mutations::Metrics::Dashboard::Annotations::Create + mount_mutation Mutations::Metrics::Dashboard::Annotations::Delete mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true mount_mutation Mutations::Notes::Create::DiffNote, calls_gitaly: true mount_mutation Mutations::Notes::Create::ImageDiffNote, calls_gitaly: true @@ -44,8 +49,10 @@ module Types mount_mutation Mutations::Snippets::Create mount_mutation Mutations::Snippets::MarkAsSpam mount_mutation Mutations::JiraImport::Start + mount_mutation Mutations::JiraImport::ImportUsers mount_mutation Mutations::DesignManagement::Upload, calls_gitaly: true mount_mutation Mutations::DesignManagement::Delete, calls_gitaly: true + mount_mutation Mutations::ContainerExpirationPolicies::Update end end diff --git a/app/graphql/types/notes/discussion_type.rb b/app/graphql/types/notes/discussion_type.rb index 74a233e9d26..a51d253097d 100644 --- a/app/graphql/types/notes/discussion_type.rb +++ b/app/graphql/types/notes/discussion_type.rb @@ -7,6 +7,8 @@ module Types authorize :read_note + implements(Types::ResolvableInterface) + field :id, GraphQL::ID_TYPE, null: false, description: "ID of this discussion" field :reply_id, GraphQL::ID_TYPE, null: false, diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb index d48cc868434..8755b4ccad5 100644 --- a/app/graphql/types/notes/note_type.rb +++ b/app/graphql/types/notes/note_type.rb @@ -9,6 +9,8 @@ module Types expose_permissions Types::PermissionTypes::Note + implements(Types::ResolvableInterface) + field :id, GraphQL::ID_TYPE, null: false, description: 'ID of the note' @@ -22,11 +24,6 @@ module Types description: 'User who wrote this note', resolve: -> (note, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, note.author_id).find } - field :resolved_by, Types::UserType, - null: true, - description: 'User that resolved the discussion', - resolve: -> (note, _args, _context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, note.resolved_by_id).find } - field :system, GraphQL::BOOLEAN_TYPE, null: false, description: 'Indicates whether this note was created by the system or by a user' @@ -44,11 +41,6 @@ module Types description: "Timestamp of the note's last activity" field :discussion, Types::Notes::DiscussionType, null: true, description: 'The discussion this note is a part of' - field :resolvable, GraphQL::BOOLEAN_TYPE, null: false, - description: 'Indicates if this note can be resolved. That is, if it is a resolvable discussion or simply a standalone note', - method: :resolvable? - field :resolved_at, Types::TimeType, null: true, - description: "Timestamp of the note's resolution" field :position, Types::Notes::DiffPositionType, null: true, description: 'The position of this note on a diff' field :confidential, GraphQL::BOOLEAN_TYPE, null: true, diff --git a/app/graphql/types/notes/noteable_type.rb b/app/graphql/types/notes/noteable_type.rb index 187c9109f8c..3a16d54f9cd 100644 --- a/app/graphql/types/notes/noteable_type.rb +++ b/app/graphql/types/notes/noteable_type.rb @@ -19,6 +19,8 @@ module Types Types::SnippetType when ::DesignManagement::Design Types::DesignManagement::DesignType + when ::AlertManagement::Alert + Types::AlertManagement::AlertType else raise "Unknown GraphQL type for #{object}" end diff --git a/app/graphql/types/permission_types/ci/pipeline.rb b/app/graphql/types/permission_types/ci/pipeline.rb index 73e44a33eba..cfd68380005 100644 --- a/app/graphql/types/permission_types/ci/pipeline.rb +++ b/app/graphql/types/permission_types/ci/pipeline.rb @@ -6,7 +6,8 @@ module Types class Pipeline < BasePermissionType graphql_name 'PipelinePermissions' - abilities :update_pipeline, :admin_pipeline, :destroy_pipeline + abilities :admin_pipeline, :destroy_pipeline + ability_field :update_pipeline, calls_gitaly: true end end end diff --git a/app/graphql/types/permission_types/merge_request.rb b/app/graphql/types/permission_types/merge_request.rb index d877fc177d2..28b7ebd2af6 100644 --- a/app/graphql/types/permission_types/merge_request.rb +++ b/app/graphql/types/permission_types/merge_request.rb @@ -3,6 +3,11 @@ module Types module PermissionTypes class MergeRequest < BasePermissionType + PERMISSION_FIELDS = %i[push_to_source_branch + remove_source_branch + cherry_pick_on_current_merge_request + revert_on_current_merge_request].freeze + present_using MergeRequestPresenter description 'Check permissions for the current user on a merge request' graphql_name 'MergeRequestPermissions' @@ -10,10 +15,9 @@ module Types abilities :read_merge_request, :admin_merge_request, :update_merge_request, :create_note - permission_field :push_to_source_branch, method: :can_push_to_source_branch?, calls_gitaly: true - permission_field :remove_source_branch, method: :can_remove_source_branch?, calls_gitaly: true - permission_field :cherry_pick_on_current_merge_request, method: :can_cherry_pick_on_current_merge_request? - permission_field :revert_on_current_merge_request, method: :can_revert_on_current_merge_request? + PERMISSION_FIELDS.each do |field_name| + permission_field field_name, method: :"can_#{field_name}?", calls_gitaly: true + end end end end diff --git a/app/graphql/types/project_member_type.rb b/app/graphql/types/project_member_type.rb new file mode 100644 index 00000000000..e9ccb51886b --- /dev/null +++ b/app/graphql/types/project_member_type.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Types + class ProjectMemberType < BaseObject + graphql_name 'ProjectMember' + description 'Represents a Project Member' + + expose_permissions Types::PermissionTypes::Project + + implements MemberInterface + + authorize :read_project + + field :id, GraphQL::ID_TYPE, null: false, + description: 'ID of the member' + + field :user, Types::UserType, null: false, + description: 'User that is associated with the member object', + resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.user_id).find } + + 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 } + end +end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 4e438ed2576..bbfb7fc4f20 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -95,6 +95,8 @@ module Types description: 'Status of Jira import background job of the project' field :only_allow_merge_if_pipeline_succeeds, GraphQL::BOOLEAN_TYPE, null: true, description: 'Indicates if merge requests of the project can only be merged with successful jobs' + field :allow_merge_on_skipped_pipeline, GraphQL::BOOLEAN_TYPE, null: true, + description: 'If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of the project can also be merged with skipped jobs' field :request_access_enabled, GraphQL::BOOLEAN_TYPE, null: true, description: 'Indicates if users can request member access to the project' field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::BOOLEAN_TYPE, null: true, @@ -125,6 +127,7 @@ module Types Types::MergeRequestType.connection_type, null: true, description: 'Merge requests of the project', + extras: [:lookahead], resolver: Resolvers::MergeRequestsResolver field :merge_request, @@ -139,6 +142,11 @@ module Types description: 'Issues of the project', resolver: Resolvers::IssuesResolver + field :project_members, + Types::ProjectMemberType.connection_type, + description: 'Members of the project', + resolver: Resolvers::ProjectMembersResolver + field :environments, Types::EnvironmentType.connection_type, null: true, @@ -157,6 +165,12 @@ module Types description: 'Build pipelines of the project', resolver: Resolvers::ProjectPipelinesResolver + field :pipeline, + Types::Ci::PipelineType, + null: true, + description: 'Build pipeline of the project', + resolver: Resolvers::ProjectPipelineResolver + field :sentry_detailed_error, Types::ErrorTracking::SentryDetailedErrorType, null: true, @@ -210,13 +224,14 @@ module Types Types::AlertManagement::AlertType.connection_type, null: true, description: 'Alert Management alerts of the project', - resolver: Resolvers::AlertManagementAlertResolver + extras: [:lookahead], + resolver: Resolvers::AlertManagement::AlertResolver field :alert_management_alert, Types::AlertManagement::AlertType, null: true, description: 'A single Alert Management alert of the project', - resolver: Resolvers::AlertManagementAlertResolver.single + resolver: Resolvers::AlertManagement::AlertResolver.single field :alert_management_alert_status_counts, Types::AlertManagement::AlertStatusCountsType, @@ -237,6 +252,50 @@ module Types description: 'A single release of the project', resolver: Resolvers::ReleasesResolver.single, feature_flag: :graphql_release_data + + field :container_expiration_policy, + Types::ContainerExpirationPolicyType, + null: true, + description: 'The container expiration policy of the project' + + field :label, + Types::LabelType, + null: true, + description: 'A label available on this project' do + argument :title, GraphQL::STRING_TYPE, + required: true, + description: 'Title of the label' + end + + def label(title:) + BatchLoader::GraphQL.for(title).batch(key: project) do |titles, loader, args| + LabelsFinder + .new(current_user, project: args[:key], title: titles) + .execute + .each { |label| loader.call(label.title, label) } + end + end + + field :labels, + Types::LabelType.connection_type, + null: true, + description: 'Labels available on this project' do + argument :search_term, GraphQL::STRING_TYPE, + required: false, + description: 'A search term to find labels with' + end + + def labels(search_term: nil) + LabelsFinder + .new(current_user, project: project, search: search_term) + .execute + end + + private + + def project + @project ||= object.respond_to?(:sync) ? object.sync : object + end end end diff --git a/app/graphql/types/projects/service_type.rb b/app/graphql/types/projects/service_type.rb index 55dd828d4b8..4ae7cb77904 100644 --- a/app/graphql/types/projects/service_type.rb +++ b/app/graphql/types/projects/service_type.rb @@ -6,7 +6,7 @@ module Types include Types::BaseInterface graphql_name 'Service' - # TODO: Add all the fields that we want to expose for the project services intergrations + # TODO: Add all the fields that we want to expose for the project services integrations # https://gitlab.com/gitlab-org/gitlab/-/issues/213088 field :type, GraphQL::STRING_TYPE, null: true, description: 'Class name of the service' diff --git a/app/graphql/types/projects/services/jira_project_type.rb b/app/graphql/types/projects/services/jira_project_type.rb new file mode 100644 index 00000000000..ccf9107f398 --- /dev/null +++ b/app/graphql/types/projects/services/jira_project_type.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Types + module Projects + module Services + # rubocop:disable Graphql/AuthorizeTypes + class JiraProjectType < BaseObject + graphql_name 'JiraProject' + + field :key, GraphQL::STRING_TYPE, null: false, + description: 'Key of the Jira project' + field :project_id, GraphQL::INT_TYPE, null: false, + description: 'ID of the Jira project', + method: :id + field :name, GraphQL::STRING_TYPE, null: true, + description: 'Name of the Jira project' + end + # rubocop:enable Graphql/AuthorizeTypes + end + end +end diff --git a/app/graphql/types/projects/services/jira_service_type.rb b/app/graphql/types/projects/services/jira_service_type.rb index 4fd9e61f5a4..e81963f752d 100644 --- a/app/graphql/types/projects/services/jira_service_type.rb +++ b/app/graphql/types/projects/services/jira_service_type.rb @@ -9,9 +9,14 @@ module Types implements(Types::Projects::ServiceType) authorize :admin_project - # This is a placeholder for now for the actuall implementation of the JiraServiceType - # Here we will want to expose a field with jira_projects fetched through Jira Rest API - # MR implementing it https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28190 + + field :projects, + Types::Projects::Services::JiraProjectType.connection_type, + null: true, + connection: false, + extensions: [Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension], + description: 'List of Jira projects fetched through Jira REST API', + resolver: Resolvers::Projects::JiraProjectsResolver end end end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 70cdcb62bc6..362e4004b73 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -47,10 +47,24 @@ module Types null: false, description: 'Fields related to design management' + field :user, Types::UserType, + null: true, + description: 'Find a user', + resolver: Resolvers::UserResolver + + field :users, Types::UserType.connection_type, + null: true, + description: 'Find users', + resolver: Resolvers::UsersResolver + field :echo, GraphQL::STRING_TYPE, null: false, description: 'Text to echo back', resolver: Resolvers::EchoResolver + field :user, Types::UserType, null: true, + description: 'Find a user on this instance', + resolver: Resolvers::UserResolver + def design_management DesignManagementObject.new(nil) end diff --git a/app/graphql/types/release_assets_type.rb b/app/graphql/types/release_assets_type.rb new file mode 100644 index 00000000000..58ad05b5365 --- /dev/null +++ b/app/graphql/types/release_assets_type.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Types + class ReleaseAssetsType < BaseObject + graphql_name 'ReleaseAssets' + + authorize :read_release + + alias_method :release, :object + + present_using ReleasePresenter + + field :assets_count, GraphQL::INT_TYPE, null: true, + description: 'Number of assets of the release' + field :links, Types::ReleaseLinkType.connection_type, null: true, + description: 'Asset links of the release' + field :sources, Types::ReleaseSourceType.connection_type, null: true, + description: 'Sources of the release' + end +end diff --git a/app/graphql/types/release_link_type.rb b/app/graphql/types/release_link_type.rb new file mode 100644 index 00000000000..070f14a90df --- /dev/null +++ b/app/graphql/types/release_link_type.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Types + class ReleaseLinkType < BaseObject + graphql_name 'ReleaseLink' + + authorize :read_release + + field :id, GraphQL::ID_TYPE, null: false, + description: 'ID of the link' + field :name, GraphQL::STRING_TYPE, null: true, + description: 'Name of the link' + field :url, GraphQL::STRING_TYPE, null: true, + description: 'URL of the link' + field :link_type, Types::ReleaseLinkTypeEnum, null: true, + description: 'Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`' + field :external, GraphQL::BOOLEAN_TYPE, null: true, method: :external?, + description: 'Indicates the link points to an external resource' + end +end diff --git a/app/graphql/types/release_link_type_enum.rb b/app/graphql/types/release_link_type_enum.rb new file mode 100644 index 00000000000..b364855833f --- /dev/null +++ b/app/graphql/types/release_link_type_enum.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Types + class ReleaseLinkTypeEnum < BaseEnum + graphql_name 'ReleaseLinkType' + description 'Type of the link: `other`, `runbook`, `image`, `package`; defaults to `other`' + + ::Releases::Link.link_types.keys.each do |link_type| + value link_type.upcase, value: link_type, description: "#{link_type.titleize} link type" + end + end +end diff --git a/app/graphql/types/release_source_type.rb b/app/graphql/types/release_source_type.rb new file mode 100644 index 00000000000..0ec1ad85a39 --- /dev/null +++ b/app/graphql/types/release_source_type.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Types + class ReleaseSourceType < BaseObject + graphql_name 'ReleaseSource' + + authorize :read_release_sources + + field :format, GraphQL::STRING_TYPE, null: true, + description: 'Format of the source' + field :url, GraphQL::STRING_TYPE, null: true, + description: 'Download URL of the source' + end +end diff --git a/app/graphql/types/release_type.rb b/app/graphql/types/release_type.rb index 632351be5d3..3d8e5a93c68 100644 --- a/app/graphql/types/release_type.rb +++ b/app/graphql/types/release_type.rb @@ -23,8 +23,12 @@ module Types description: 'Timestamp of when the release was created' field :released_at, Types::TimeType, null: true, description: 'Timestamp of when the release was released' + field :assets, Types::ReleaseAssetsType, null: true, method: :itself, + description: 'Assets of the release' field :milestones, Types::MilestoneType.connection_type, null: true, description: 'Milestones associated to the release' + field :evidences, Types::EvidenceType.connection_type, null: true, + description: 'Evidence for the release' field :author, Types::UserType, null: true, description: 'User that created the release' diff --git a/app/graphql/types/resolvable_interface.rb b/app/graphql/types/resolvable_interface.rb new file mode 100644 index 00000000000..a39092c70ca --- /dev/null +++ b/app/graphql/types/resolvable_interface.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Types + # This Interface contains fields that are shared between objects that include either + # the `ResolvableNote` or `ResolvableDiscussion` modules. + module ResolvableInterface + include Types::BaseInterface + + field :resolved_by, Types::UserType, + null: true, + description: 'User who resolved the object' + + def resolved_by + return unless object.resolved_by_id + + Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.resolved_by_id).find + end + + field :resolved, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Indicates if the object is resolved', + method: :resolved? + field :resolvable, GraphQL::BOOLEAN_TYPE, null: false, + description: 'Indicates if the object can be resolved', + method: :resolvable? + field :resolved_at, Types::TimeType, null: true, + description: 'Timestamp of when the object was resolved' + end +end diff --git a/app/graphql/types/snippet_type.rb b/app/graphql/types/snippet_type.rb index b23c4f71ffa..73ca3425ded 100644 --- a/app/graphql/types/snippet_type.rb +++ b/app/graphql/types/snippet_type.rb @@ -27,9 +27,12 @@ module Types authorize: :read_project, resolve: -> (snippet, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, snippet.project_id).find } + # 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: false, + null: true, resolve: -> (snippet, args, context) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, snippet.author_id).find } field :file_name, GraphQL::STRING_TYPE, @@ -65,6 +68,11 @@ module Types calls_gitaly: true, null: false + field :blobs, type: [Types::Snippets::BlobType], + description: 'Snippet blobs', + calls_gitaly: true, + null: false + field :ssh_url_to_repo, type: GraphQL::STRING_TYPE, description: 'SSH URL to the snippet repository', calls_gitaly: true, diff --git a/app/graphql/types/snippets/file_input_action_enum.rb b/app/graphql/types/snippets/file_input_action_enum.rb new file mode 100644 index 00000000000..7785853f3a8 --- /dev/null +++ b/app/graphql/types/snippets/file_input_action_enum.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Snippets + class FileInputActionEnum < BaseEnum + graphql_name 'SnippetFileInputActionEnum' + description 'Type of a snippet file input action' + + value 'create', value: :create + value 'update', value: :update + value 'delete', value: :delete + value 'move', value: :move + end + end +end diff --git a/app/graphql/types/snippets/file_input_type.rb b/app/graphql/types/snippets/file_input_type.rb new file mode 100644 index 00000000000..85a02c8f493 --- /dev/null +++ b/app/graphql/types/snippets/file_input_type.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Types + module Snippets + class FileInputType < BaseInputObject # rubocop:disable Graphql/AuthorizeTypes + graphql_name 'SnippetFileInputType' + description 'Represents an action to perform over a snippet file' + + argument :action, Types::Snippets::FileInputActionEnum, + description: 'Type of input action', + required: true + + argument :previous_path, GraphQL::STRING_TYPE, + description: 'Previous path of the snippet file', + required: false + + argument :file_path, GraphQL::STRING_TYPE, + description: 'Path of the snippet file', + required: true + + argument :content, GraphQL::STRING_TYPE, + description: 'Snippet file content', + required: false + end + end +end diff --git a/app/graphql/types/user_state_enum.rb b/app/graphql/types/user_state_enum.rb new file mode 100644 index 00000000000..d34936b4c48 --- /dev/null +++ b/app/graphql/types/user_state_enum.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Types + class UserStateEnum < BaseEnum + graphql_name 'UserState' + description 'Possible states of a user' + + value 'active', 'The user is active and is able to use the system', value: 'active' + value 'blocked', 'The user has been blocked and is prevented from using the system', value: 'blocked' + value 'deactivated', 'The user is no longer active and is unable to use the system', value: 'deactivated' + end +end diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb index 29a3f5d452f..ab3c84ea539 100644 --- a/app/graphql/types/user_type.rb +++ b/app/graphql/types/user_type.rb @@ -12,12 +12,12 @@ module Types field :id, GraphQL::ID_TYPE, null: false, description: 'ID of the user' - field :name, GraphQL::STRING_TYPE, null: false, - description: 'Human-readable name of the user' - field :state, GraphQL::STRING_TYPE, null: false, - description: 'State of the issue' field :username, GraphQL::STRING_TYPE, null: false, description: 'Username of the user. Unique within this instance of GitLab' + field :name, GraphQL::STRING_TYPE, null: false, + description: 'Human-readable name of the user' + field :state, Types::UserStateEnum, null: false, + description: 'State of the user' field :avatar_url, GraphQL::STRING_TYPE, null: true, description: "URL of the user's avatar" field :web_url, GraphQL::STRING_TYPE, null: false, @@ -25,6 +25,20 @@ module Types field :todos, Types::TodoType.connection_type, null: false, resolver: Resolvers::TodoResolver, description: 'Todos of the user' + field :group_memberships, Types::GroupMemberType.connection_type, null: true, + description: 'Group memberships of the user', + method: :group_members + field :project_memberships, Types::ProjectMemberType.connection_type, null: true, + description: 'Project memberships of the user', + method: :project_members + + # Merge request field: MRs can be either authored or assigned: + field :authored_merge_requests, Types::MergeRequestType.connection_type, null: true, + resolver: Resolvers::AuthoredMergeRequestsResolver, + description: 'Merge Requests authored by the user' + field :assigned_merge_requests, Types::MergeRequestType.connection_type, null: true, + resolver: Resolvers::AssignedMergeRequestsResolver, + description: 'Merge Requests assigned to the user' field :snippets, Types::SnippetType.connection_type, |