diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-14 11:41:52 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-14 11:41:52 +0300 |
commit | 585826cb22ecea5998a2c2a4675735c94bdeedac (patch) | |
tree | 5b05f0b30d33cef48963609e8a18a4dff260eab3 /app/graphql | |
parent | df221d036e5d0c6c0ee4d55b9c97f481ee05dee8 (diff) |
Add latest changes from gitlab-org/gitlab@16-6-stable-eev16.6.0-rc42
Diffstat (limited to 'app/graphql')
85 files changed, 1068 insertions, 166 deletions
diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb index 994668b5f8f..8419f7d5eae 100644 --- a/app/graphql/mutations/base_mutation.rb +++ b/app/graphql/mutations/base_mutation.rb @@ -30,12 +30,6 @@ module Mutations def ready?(**args) raise_resource_not_available_error!(ERROR_MESSAGE) if read_only? - missing_args = self.class.arguments.values - .reject { |arg| arg.accepts?(args.fetch(arg.keyword, :not_given)) } - .map(&:graphql_name) - - raise ArgumentError, "Arguments must be provided: #{missing_args.join(", ")}" if missing_args.any? - true end diff --git a/app/graphql/mutations/ci/catalog/resources/create.rb b/app/graphql/mutations/ci/catalog/resources/create.rb new file mode 100644 index 00000000000..7f934e101c8 --- /dev/null +++ b/app/graphql/mutations/ci/catalog/resources/create.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module Catalog + module Resources + class Create < BaseMutation + graphql_name 'CatalogResourcesCreate' + + argument :project_path, GraphQL::Types::ID, + required: true, + description: 'Project to convert to a catalog resource.' + + authorize :add_catalog_resource + + def resolve(project_path:) + project = authorized_find!(project_path: project_path) + response = ::Ci::Catalog::Resources::CreateService.new(project, current_user).execute + + errors = response.success? ? [] : [response.message] + + { + errors: errors + } + end + + private + + def find_object(project_path:) + Project.find_by_full_path(project_path) + end + end + end + end + end +end diff --git a/app/graphql/mutations/ci/catalog/resources/unpublish.rb b/app/graphql/mutations/ci/catalog/resources/unpublish.rb new file mode 100644 index 00000000000..e45e9646147 --- /dev/null +++ b/app/graphql/mutations/ci/catalog/resources/unpublish.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Mutations + module Ci + module Catalog + module Resources + class Unpublish < BaseMutation + graphql_name 'CatalogResourceUnpublish' + + authorize :add_catalog_resource + + argument :id, ::Types::GlobalIDType[::Ci::Catalog::Resource], + required: true, + description: 'Global ID of the catalog resource to unpublish.' + + def resolve(id:) + catalog_resource = ::Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(id)) + authorize!(catalog_resource&.project) + + catalog_resource.unpublish! + + { + errors: [] + } + end + end + end + end + end +end diff --git a/app/graphql/mutations/ci/job/cancel.rb b/app/graphql/mutations/ci/job/cancel.rb index dc9f4d19779..44a7772019d 100644 --- a/app/graphql/mutations/ci/job/cancel.rb +++ b/app/graphql/mutations/ci/job/cancel.rb @@ -11,7 +11,7 @@ module Mutations null: true, description: 'Job after the mutation.' - authorize :update_build + authorize :cancel_build def resolve(id:) job = authorized_find!(id: id) diff --git a/app/graphql/mutations/ci/pipeline/cancel.rb b/app/graphql/mutations/ci/pipeline/cancel.rb index 810f458fd75..1014462d0b1 100644 --- a/app/graphql/mutations/ci/pipeline/cancel.rb +++ b/app/graphql/mutations/ci/pipeline/cancel.rb @@ -6,7 +6,7 @@ module Mutations class Cancel < Base graphql_name 'PipelineCancel' - authorize :update_pipeline + authorize :cancel_pipeline def resolve(id:) pipeline = authorized_find!(id: id) diff --git a/app/graphql/mutations/commits/create.rb b/app/graphql/mutations/commits/create.rb index 02e1e4c78bf..cbe2c49e950 100644 --- a/app/graphql/mutations/commits/create.rb +++ b/app/graphql/mutations/commits/create.rb @@ -64,7 +64,7 @@ module Mutations result = ::Files::MultiService.new(project, current_user, attributes).execute { - content: actions.pluck(:content), # rubocop:disable CodeReuse/ActiveRecord because actions is an Array, not a Relation + content: actions.pluck(:content), # rubocop:disable CodeReuse/ActiveRecord -- Array#pluck commit: (project.repository.commit(result[:result]) if result[:status] == :success), commit_pipeline_path: UrlHelpers.new.graphql_etag_pipeline_sha_path(result[:result]), errors: Array.wrap(result[:message]) diff --git a/app/graphql/mutations/container_registry/protection/rule/create.rb b/app/graphql/mutations/container_registry/protection/rule/create.rb new file mode 100644 index 00000000000..cf8416480a2 --- /dev/null +++ b/app/graphql/mutations/container_registry/protection/rule/create.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Mutations + module ContainerRegistry + module Protection + module Rule + class Create < ::Mutations::BaseMutation + graphql_name 'CreateContainerRegistryProtectionRule' + description 'Creates a protection rule to restrict access to a project\'s container registry. ' \ + 'Available only when feature flag `container_registry_protected_containers` is enabled.' + + include FindsProject + + authorize :admin_container_image + + argument :project_path, + GraphQL::Types::ID, + required: true, + description: 'Full path of the project where a protection rule is located.' + + argument :container_path_pattern, + GraphQL::Types::String, + required: true, + description: + 'ContainerRegistryname protected by the protection rule. For example `@my-scope/my-container-*`. ' \ + 'Wildcard character `*` allowed.' + + argument :push_protected_up_to_access_level, + Types::ContainerRegistry::Protection::RuleAccessLevelEnum, + required: true, + description: + 'Max GitLab access level to prevent from pushing container images to the container registry. ' \ + 'For example `DEVELOPER`, `MAINTAINER`, `OWNER`.' + + argument :delete_protected_up_to_access_level, + Types::ContainerRegistry::Protection::RuleAccessLevelEnum, + required: true, + description: + 'Max GitLab access level to prevent from deleting container images in the container registry. ' \ + 'For example `DEVELOPER`, `MAINTAINER`, `OWNER`.' + + field :container_registry_protection_rule, + Types::ContainerRegistry::Protection::RuleType, + null: true, + description: 'Container registry protection rule after mutation.' + + def resolve(project_path:, **kwargs) + project = authorized_find!(project_path) + + if Feature.disabled?(:container_registry_protected_containers, project) + raise_resource_not_available_error!("'container_registry_protected_containers' feature flag is disabled") + end + + response = ::ContainerRegistry::Protection::CreateRuleService.new(project, current_user, kwargs).execute + + { container_registry_protection_rule: response.payload[:container_registry_protection_rule], + errors: response.errors } + end + end + end + end + end +end diff --git a/app/graphql/mutations/merge_requests/accept.rb b/app/graphql/mutations/merge_requests/accept.rb index 220ebea22c7..604fdd49f45 100644 --- a/app/graphql/mutations/merge_requests/accept.rb +++ b/app/graphql/mutations/merge_requests/accept.rb @@ -9,6 +9,10 @@ module Mutations Accepts a merge request. When accepted, the source branch will be scheduled to merge into the target branch, either immediately if possible, or using one of the automatic merge strategies. + + [In GitLab 16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/421510), the merging happens asynchronously. + This results in `mergeRequest` and `state` not updating after a mutation request, + because the merging may not have happened yet. DESC NOT_MERGEABLE = 'This branch cannot be merged' diff --git a/app/graphql/mutations/namespace/package_settings/update.rb b/app/graphql/mutations/namespace/package_settings/update.rb index 4e71bed52c6..97c16ee79fe 100644 --- a/app/graphql/mutations/namespace/package_settings/update.rb +++ b/app/graphql/mutations/namespace/package_settings/update.rb @@ -8,8 +8,6 @@ module Mutations include Mutations::ResolvesNamespace - NUGET_DUPLICATES_FF_ERROR = '`nuget_duplicates_option` feature flag is disabled.' - description <<~DESC These settings can be adjusted by the group Owner or Maintainer. [Issue 370471](https://gitlab.com/gitlab-org/gitlab/-/issues/370471) proposes limiting @@ -91,10 +89,6 @@ module Mutations def resolve(namespace_path:, **args) namespace = authorized_find!(namespace_path: namespace_path) - if nuget_duplicate_settings_present?(args) && Feature.disabled?(:nuget_duplicates_option, namespace) - raise_resource_not_available_error! NUGET_DUPLICATES_FF_ERROR - end - result = ::Namespaces::PackageSettings::UpdateService .new(container: namespace, current_user: current_user, params: args) .execute @@ -110,10 +104,6 @@ module Mutations def find_object(namespace_path:) resolve_namespace(full_path: namespace_path) end - - def nuget_duplicate_settings_present?(args) - args.key?(:nuget_duplicates_allowed) || args.key?(:nuget_duplicate_exception_regex) - end end end end diff --git a/app/graphql/mutations/organizations/create.rb b/app/graphql/mutations/organizations/create.rb new file mode 100644 index 00000000000..0d1b204a4c1 --- /dev/null +++ b/app/graphql/mutations/organizations/create.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Mutations + module Organizations + class Create < BaseMutation + graphql_name 'OrganizationCreate' + + authorize :create_organization + + field :organization, + ::Types::Organizations::OrganizationType, + null: true, + description: 'Organization created.' + + argument :name, GraphQL::Types::String, + required: true, + description: 'Name for the organization.' + + argument :path, GraphQL::Types::String, + required: true, + description: 'Path for the organization.' + + def resolve(args) + authorize!(:global) + + result = ::Organizations::CreateService.new( + current_user: current_user, + params: args + ).execute + + { organization: result.payload, errors: result.errors } + end + end + end +end diff --git a/app/graphql/mutations/packages/protection/rule/delete.rb b/app/graphql/mutations/packages/protection/rule/delete.rb new file mode 100644 index 00000000000..bd0159d3c23 --- /dev/null +++ b/app/graphql/mutations/packages/protection/rule/delete.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Mutations + module Packages + module Protection + module Rule + class Delete < ::Mutations::BaseMutation + graphql_name 'DeletePackagesProtectionRule' + description 'Deletes a protection rule for packages. ' \ + 'Available only when feature flag `packages_protected_packages` is enabled.' + + authorize :admin_package + + argument :id, + ::Types::GlobalIDType[::Packages::Protection::Rule], + required: true, + description: 'Global ID of the package protection rule to delete.' + + field :package_protection_rule, + Types::Packages::Protection::RuleType, + null: true, + description: 'Packages protection rule that was deleted successfully.' + + def resolve(id:, **_kwargs) + if Feature.disabled?(:packages_protected_packages) + raise_resource_not_available_error!("'packages_protected_packages' feature flag is disabled") + end + + package_protection_rule = authorized_find!(id: id) + + response = ::Packages::Protection::DeleteRuleService.new(package_protection_rule, + current_user: current_user).execute + + { package_protection_rule: response.payload[:package_protection_rule], errors: response.errors } + end + end + end + end + end +end diff --git a/app/graphql/mutations/saved_replies/base.rb b/app/graphql/mutations/saved_replies/base.rb index 4923fcb7851..79761645eb7 100644 --- a/app/graphql/mutations/saved_replies/base.rb +++ b/app/graphql/mutations/saved_replies/base.rb @@ -23,10 +23,6 @@ module Mutations end end - def feature_enabled? - Feature.enabled?(:saved_replies, current_user) - end - def find_object(id) GitlabSchema.find_by_gid(id) end diff --git a/app/graphql/mutations/saved_replies/create.rb b/app/graphql/mutations/saved_replies/create.rb index d97461a1c2a..25c02b79cb8 100644 --- a/app/graphql/mutations/saved_replies/create.rb +++ b/app/graphql/mutations/saved_replies/create.rb @@ -16,8 +16,6 @@ module Mutations description: copy_field_description(Types::SavedReplyType, :content) def resolve(name:, content:) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled? - result = ::Users::SavedReplies::CreateService.new(current_user: current_user, name: name, content: content).execute present_result(result) end diff --git a/app/graphql/mutations/saved_replies/destroy.rb b/app/graphql/mutations/saved_replies/destroy.rb index 7cd0f21ad45..655ed9cb798 100644 --- a/app/graphql/mutations/saved_replies/destroy.rb +++ b/app/graphql/mutations/saved_replies/destroy.rb @@ -12,8 +12,6 @@ module Mutations description: copy_field_description(Types::SavedReplyType, :id) def resolve(id:) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled? - saved_reply = authorized_find!(id) result = ::Users::SavedReplies::DestroyService.new(saved_reply: saved_reply).execute present_result(result) diff --git a/app/graphql/mutations/saved_replies/update.rb b/app/graphql/mutations/saved_replies/update.rb index d9368de7547..f5dc81614d2 100644 --- a/app/graphql/mutations/saved_replies/update.rb +++ b/app/graphql/mutations/saved_replies/update.rb @@ -20,8 +20,6 @@ module Mutations description: copy_field_description(Types::SavedReplyType, :content) def resolve(id:, name:, content:) - raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Feature disabled' unless feature_enabled? - saved_reply = authorized_find!(id) result = ::Users::SavedReplies::UpdateService.new(saved_reply: saved_reply, name: name, content: content).execute present_result(result) diff --git a/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb b/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb index 51a1afdd5ab..2d722b02bf1 100644 --- a/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb +++ b/app/graphql/resolvers/analytics/cycle_analytics/deployment_count_resolver.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# rubocop:disable Graphql/ResolverType (inherited from Resolvers::Analytics::CycleAnalytics::BaseCountResolver) +# rubocop:disable Graphql/ResolverType -- inherited from Resolvers::Analytics::CycleAnalytics::BaseCountResolver module Resolvers module Analytics module CycleAnalytics diff --git a/app/graphql/resolvers/analytics/cycle_analytics/issue_count_resolver.rb b/app/graphql/resolvers/analytics/cycle_analytics/issue_count_resolver.rb index fd20800ee16..32b884df84f 100644 --- a/app/graphql/resolvers/analytics/cycle_analytics/issue_count_resolver.rb +++ b/app/graphql/resolvers/analytics/cycle_analytics/issue_count_resolver.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# rubocop:disable Graphql/ResolverType (inherited from Resolvers::Analytics::CycleAnalytics::BaseIssueResolver) +# rubocop:disable Graphql/ResolverType -- inherited from Resolvers::Analytics::CycleAnalytics::BaseIssueResolver module Resolvers module Analytics module CycleAnalytics diff --git a/app/graphql/resolvers/ci/catalog/resource_resolver.rb b/app/graphql/resolvers/ci/catalog/resource_resolver.rb new file mode 100644 index 00000000000..4b722bd3ec7 --- /dev/null +++ b/app/graphql/resolvers/ci/catalog/resource_resolver.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + module Catalog + class ResourceResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + authorize :read_code + + type ::Types::Ci::Catalog::ResourceType, null: true + + argument :id, ::Types::GlobalIDType[::Ci::Catalog::Resource], + required: false, + description: 'CI/CD Catalog resource global ID.' + + argument :full_path, GraphQL::Types::ID, + required: false, + description: 'CI/CD Catalog resource full path.' + + def ready?(**args) + unless args[:id].present? ^ args[:full_path].present? + raise Gitlab::Graphql::Errors::ArgumentError, + "Exactly one of 'id' or 'full_path' arguments is required." + end + + super + end + + def resolve(id: nil, full_path: nil) + if full_path.present? + project = Project.find_by_full_path(full_path) + authorize!(project) + + raise_resource_not_available_error! unless project.catalog_resource + + project.catalog_resource + else + catalog_resource = ::Gitlab::Graphql::Lazy.force(GitlabSchema.find_by_gid(id)) + authorize!(catalog_resource&.project) + + catalog_resource + end + end + end + end + end +end diff --git a/app/graphql/resolvers/ci/catalog/resources_resolver.rb b/app/graphql/resolvers/ci/catalog/resources_resolver.rb new file mode 100644 index 00000000000..c6904dcd7f6 --- /dev/null +++ b/app/graphql/resolvers/ci/catalog/resources_resolver.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + module Catalog + class ResourcesResolver < BaseResolver + include LooksAhead + + type ::Types::Ci::Catalog::ResourceType.connection_type, null: true + + argument :scope, ::Types::Ci::Catalog::ResourceScopeEnum, + required: false, + default_value: :all, + description: 'Scope of the returned catalog resources.' + + argument :search, GraphQL::Types::String, + required: false, + description: 'Search term to filter the catalog resources by name or description.' + + argument :sort, ::Types::Ci::Catalog::ResourceSortEnum, + required: false, + description: 'Sort catalog resources by given criteria.' + + # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/429636 + argument :project_path, GraphQL::Types::ID, + required: false, + description: 'Project with the namespace catalog.' + + def resolve_with_lookahead(scope:, project_path: nil, search: nil, sort: nil) + if project_path.present? + project = Project.find_by_full_path(project_path) + + apply_lookahead( + ::Ci::Catalog::Listing + .new(context[:current_user]) + .resources(namespace: project.root_namespace, sort: sort, search: search) + ) + elsif scope == :all + apply_lookahead(::Ci::Catalog::Listing.new(context[:current_user]).resources(sort: sort, search: search)) + end + end + + private + + def preloads + { + web_path: { project: { namespace: :route } }, + readme_html: { project: :route } + } + end + end + end + end +end diff --git a/app/graphql/resolvers/ci/catalog/versions_resolver.rb b/app/graphql/resolvers/ci/catalog/versions_resolver.rb new file mode 100644 index 00000000000..046adeb7a67 --- /dev/null +++ b/app/graphql/resolvers/ci/catalog/versions_resolver.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + module Catalog + class VersionsResolver < ::Resolvers::ReleasesResolver + type Types::ReleaseType.connection_type, null: true + + # This allows a maximum of 1 call to the field that uses this resolver. If the + # field is evaluated on more than one node, it causes performance degradation. + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + + private + + def get_project + object.respond_to?(:project) ? object.project : object + end + + # Override the aliased method in ReleasesResolver + alias_method :project, :get_project + end + end + end +end diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb index 3289f1d0056..9121c413b1f 100644 --- a/app/graphql/resolvers/ci/runners_resolver.rb +++ b/app/graphql/resolvers/ci/runners_resolver.rb @@ -41,6 +41,17 @@ module Resolvers required: false, description: 'Filter by upgrade status.' + argument :creator_id, ::Types::GlobalIDType[::User].as('UserID'), + required: false, + description: 'Filter runners by creator ID.' + + argument :version_prefix, GraphQL::Types::String, + required: false, + description: "Filter runners by version. Runners that contain runner managers with the version at " \ + "the start of the search term are returned. For example, the search term '14.' returns " \ + "runner managers with versions '14.11.1' and '14.2.3'.", + alpha: { milestone: '16.6' } + def resolve_with_lookahead(**args) apply_lookahead( ::Ci::RunnersFinder @@ -68,6 +79,9 @@ module Resolvers upgrade_status: params[:upgrade_status], search: params[:search], sort: params[:sort]&.to_s, + creator_id: + params[:creator_id] ? ::GitlabSchema.parse_gid(params[:creator_id], expected_type: ::User).model_id : nil, + version_prefix: params[:version_prefix], preload: false # we'll handle preloading ourselves }.compact .merge(parent_param) diff --git a/app/graphql/resolvers/concerns/caching_array_resolver.rb b/app/graphql/resolvers/concerns/caching_array_resolver.rb index 15bf9a90e46..f678e02533d 100644 --- a/app/graphql/resolvers/concerns/caching_array_resolver.rb +++ b/app/graphql/resolvers/concerns/caching_array_resolver.rb @@ -132,7 +132,7 @@ module CachingArrayResolver model_class.arel_table[Arel.star] end - # rubocop: disable Graphql/Descriptions (false positive!) + # rubocop: disable Graphql/Descriptions -- false positive def query_limit field&.max_page_size.presence || context.schema.default_max_page_size end diff --git a/app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb b/app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb index ecb105a64d0..1982b458143 100644 --- a/app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb +++ b/app/graphql/resolvers/concerns/work_items/shared_filter_arguments.rb @@ -17,7 +17,12 @@ module WorkItems argument :state, Types::IssuableStateEnum, required: false, - description: 'Current state of the work item.' + description: 'Current state of the work item.', + prepare: ->(state, _ctx) { + return state unless state == 'locked' + + raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE + } argument :types, [Types::IssueTypeEnum], as: :issue_types, diff --git a/app/graphql/resolvers/container_repository_tags_resolver.rb b/app/graphql/resolvers/container_repository_tags_resolver.rb index 55a83dd49da..bc5006ae06c 100644 --- a/app/graphql/resolvers/container_repository_tags_resolver.rb +++ b/app/graphql/resolvers/container_repository_tags_resolver.rb @@ -14,21 +14,61 @@ module Resolvers required: false, default_value: nil + alias_method :container_repository, :object + def resolve(sort:, **filters) - result = tags + if container_repository.migrated? && Feature.enabled?(:use_repository_list_tags_on_graphql, container_repository.project) + page_size = [filters[:first], filters[:last]].map(&:to_i).max + + result = container_repository.tags_page( + before: filters[:before], + last: filters[:after], + sort: map_sort_field(sort), + name: filters[:name], + page_size: page_size + ) - if filters[:name] - result = tags.filter do |tag| - tag.name.include?(filters[:name]) + Gitlab::Graphql::ExternallyPaginatedArray.new( + parse_pagination_cursor(result, :previous), + parse_pagination_cursor(result, :next), + *result[:tags] + ) + else + result = tags + + if filters[:name] + result = tags.filter do |tag| + tag.name.include?(filters[:name]) + end end - end - result = sort_tags(result, sort) if sort - result + result = sort_tags(result, sort) if sort + result + end end private + def parse_pagination_cursor(result, direction) + pagination_uri = result.dig(:pagination, direction, :uri) + + return unless pagination_uri + + query_params = CGI.parse(pagination_uri.query) + key = direction == :previous ? 'before' : 'last' + + query_params[key]&.first + end + + def map_sort_field(sort) + return unless sort + + sort_field, direction = sort.to_s.split('_') + return sort_field if direction == 'asc' + + "-#{sort_field}" + end + def sort_tags(to_be_sorted, sort) raise StandardError unless Types::ContainerRepositoryTagsSortEnum.enum.include?(sort) @@ -41,7 +81,7 @@ module Resolvers end def tags - object.tags + container_repository.tags rescue Faraday::Error raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, "Can't connect to the Container Registry. If this error persists, please review the troubleshooting documentation." end diff --git a/app/graphql/resolvers/data_transfer/group_data_transfer_resolver.rb b/app/graphql/resolvers/data_transfer/group_data_transfer_resolver.rb index 83bb144017c..133b86623f1 100644 --- a/app/graphql/resolvers/data_transfer/group_data_transfer_resolver.rb +++ b/app/graphql/resolvers/data_transfer/group_data_transfer_resolver.rb @@ -16,16 +16,12 @@ module Resolvers def resolve(**args) return { egress_nodes: [] } unless Feature.enabled?(:data_transfer_monitoring, group) - results = if Feature.enabled?(:data_transfer_monitoring_mock_data, group) - ::DataTransfer::MockedTransferFinder.new.execute - else - ::DataTransfer::GroupDataTransferFinder.new( - group: group, - from: args[:from], - to: args[:to], - user: current_user - ).execute.map(&:attributes) - end + results = ::DataTransfer::GroupDataTransferFinder.new( + group: group, + from: args[:from], + to: args[:to], + user: current_user + ).execute.map(&:attributes) { egress_nodes: results.to_a } end diff --git a/app/graphql/resolvers/data_transfer/project_data_transfer_resolver.rb b/app/graphql/resolvers/data_transfer/project_data_transfer_resolver.rb index c3296f7d4c3..d711f837251 100644 --- a/app/graphql/resolvers/data_transfer/project_data_transfer_resolver.rb +++ b/app/graphql/resolvers/data_transfer/project_data_transfer_resolver.rb @@ -16,16 +16,12 @@ module Resolvers def resolve(**args) return { egress_nodes: [] } unless Feature.enabled?(:data_transfer_monitoring, project.group) - results = if Feature.enabled?(:data_transfer_monitoring_mock_data, project.group) - ::DataTransfer::MockedTransferFinder.new.execute - else - ::DataTransfer::ProjectDataTransferFinder.new( - project: project, - from: args[:from], - to: args[:to], - user: current_user - ).execute - end + results = ::DataTransfer::ProjectDataTransferFinder.new( + project: project, + from: args[:from], + to: args[:to], + user: current_user + ).execute { egress_nodes: results } end diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb index 5e0fb27bafa..5a6a3d678b9 100644 --- a/app/graphql/resolvers/group_issues_resolver.rb +++ b/app/graphql/resolvers/group_issues_resolver.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# rubocop:disable Graphql/ResolverType (inherited from Issues::BaseParentResolver) +# rubocop:disable Graphql/ResolverType -- inherited from Issues::BaseParentResolver module Resolvers class GroupIssuesResolver < Issues::BaseParentResolver def self.issuable_collection_name diff --git a/app/graphql/resolvers/issues/base_parent_resolver.rb b/app/graphql/resolvers/issues/base_parent_resolver.rb index 6308e56f049..78ef4132baf 100644 --- a/app/graphql/resolvers/issues/base_parent_resolver.rb +++ b/app/graphql/resolvers/issues/base_parent_resolver.rb @@ -7,8 +7,13 @@ module Resolvers include ::Issues::SortArguments argument :state, Types::IssuableStateEnum, - required: false, - description: 'Current state of this issue.' + required: false, + description: 'Current state of this issue.', + prepare: ->(state, _ctx) { + return state unless state == 'locked' + + raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE + } # see app/graphql/types/issue_connection.rb type 'Types::IssueConnection', null: true diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb index 34f14eee0e5..bc0e7334303 100644 --- a/app/graphql/resolvers/issues_resolver.rb +++ b/app/graphql/resolvers/issues_resolver.rb @@ -14,7 +14,12 @@ module Resolvers description: 'Whether to include issues from archived projects. Defaults to `false`.' argument :state, Types::IssuableStateEnum, required: false, - description: 'Current state of this issue.' + description: 'Current state of this issue.', + prepare: ->(state, _ctx) { + return state unless state == 'locked' + + raise Gitlab::Graphql::Errors::ArgumentError, Types::IssuableStateEnum::INVALID_LOCKED_MESSAGE + } # see app/graphql/types/issue_connection.rb type 'Types::IssueConnection', null: true diff --git a/app/graphql/resolvers/namespaces/work_items_resolver.rb b/app/graphql/resolvers/namespaces/work_items_resolver.rb index 6985a7a898a..671788668b1 100644 --- a/app/graphql/resolvers/namespaces/work_items_resolver.rb +++ b/app/graphql/resolvers/namespaces/work_items_resolver.rb @@ -2,7 +2,7 @@ module Resolvers module Namespaces - # rubocop:disable Graphql/ResolverType (inherited from Resolvers::WorkItemsResolver) + # rubocop:disable Graphql/ResolverType -- inherited from Resolvers::WorkItemsResolver class WorkItemsResolver < ::Resolvers::WorkItemsResolver def ready?(**args) return false if Feature.disabled?(:namespace_level_work_items, resource_parent) diff --git a/app/graphql/resolvers/packages_base_resolver.rb b/app/graphql/resolvers/packages_base_resolver.rb index 7d153d16910..7e5d89a7897 100644 --- a/app/graphql/resolvers/packages_base_resolver.rb +++ b/app/graphql/resolvers/packages_base_resolver.rb @@ -19,6 +19,12 @@ module Resolvers required: false, default_value: nil + argument :package_version, GraphQL::Types::String, + description: 'Filter a package by version. If used in combination with `include_versionless`, + then no versionless packages are returned.', + required: false, + default_value: nil + argument :status, Types::Packages::PackageStatusEnum, description: 'Filter a package by status.', required: false, diff --git a/app/graphql/resolvers/project_issues_resolver.rb b/app/graphql/resolvers/project_issues_resolver.rb index f869d8f11c6..2bc610e8266 100644 --- a/app/graphql/resolvers/project_issues_resolver.rb +++ b/app/graphql/resolvers/project_issues_resolver.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# rubocop:disable Graphql/ResolverType (inherited from Issues::BaseParentResolver) +# rubocop:disable Graphql/ResolverType -- inherited from Issues::BaseParentResolver module Resolvers class ProjectIssuesResolver < Issues::BaseParentResolver accept_release_tag diff --git a/app/graphql/resolvers/project_members_resolver.rb b/app/graphql/resolvers/project_members_resolver.rb index e889b47c000..a27183438cd 100644 --- a/app/graphql/resolvers/project_members_resolver.rb +++ b/app/graphql/resolvers/project_members_resolver.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true -# rubocop:disable Graphql/ResolverType (inherited from MembersResolver) + +# rubocop:disable Graphql/ResolverType -- inherited from MembersResolver module Resolvers class ProjectMembersResolver < MembersResolver @@ -17,3 +18,4 @@ module Resolvers end end end +# rubocop:enable Graphql/ResolverType diff --git a/app/graphql/resolvers/project_milestones_resolver.rb b/app/graphql/resolvers/project_milestones_resolver.rb index 567a55aa09b..cb4e9a5cdf7 100644 --- a/app/graphql/resolvers/project_milestones_resolver.rb +++ b/app/graphql/resolvers/project_milestones_resolver.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true -# rubocop:disable Graphql/ResolverType (inherited from MilestonesResolver) module Resolvers class ProjectMilestonesResolver < MilestonesResolver diff --git a/app/graphql/resolvers/projects/snippets_resolver.rb b/app/graphql/resolvers/projects/snippets_resolver.rb index 448918be2f5..9ab9db21e89 100644 --- a/app/graphql/resolvers/projects/snippets_resolver.rb +++ b/app/graphql/resolvers/projects/snippets_resolver.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true -# rubocop:disable Graphql/ResolverType (inherited from ResolvesSnippets) + +# rubocop:disable Graphql/ResolverType -- inherited from ResolvesSnippets module Resolvers module Projects @@ -27,3 +28,4 @@ module Resolvers end end end +# rubocop:enable Graphql/ResolverType diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb index 8dd409a8173..450caa9aff6 100644 --- a/app/graphql/resolvers/projects_resolver.rb +++ b/app/graphql/resolvers/projects_resolver.rb @@ -3,6 +3,7 @@ module Resolvers class ProjectsResolver < BaseResolver include ProjectSearchArguments + include LooksAhead type Types::ProjectType.connection_type, null: true @@ -10,6 +11,10 @@ module Resolvers required: false, description: 'Filter projects by IDs.' + argument :full_paths, [GraphQL::Types::String], + required: false, + description: 'Filter projects by full paths. You cannot provide more than 50 full paths.' + argument :sort, GraphQL::Types::String, required: false, description: "Sort order of results. Format: `<field_name>_<sort_direction>`, " \ @@ -23,19 +28,48 @@ module Resolvers required: false, description: "Return only projects with merge requests enabled." - def resolve(**args) - ProjectsFinder + def resolve_with_lookahead(**args) + validate_args!(args) + + projects = ProjectsFinder .new(current_user: current_user, params: finder_params(args), project_ids_relation: parse_gids(args[:ids])) .execute + + apply_lookahead(projects) end private + def validate_args!(args) + return unless args[:full_paths].present? && args[:full_paths].length > 50 + + raise Gitlab::Graphql::Errors::ArgumentError, 'You cannot provide more than 50 full_paths' + end + + def unconditional_includes + [:creator, :group, :invited_groups, :project_setting] + end + + def preloads + { + full_path: [:route], + topics: [:topics], + import_status: [:import_state], + service_desk_address: [:project_feature, :service_desk_setting], + jira_import_status: [:jira_imports], + container_repositories: [:container_repositories], + container_repositories_count: [:container_repositories], + web_url: { namespace: [:route] }, + is_catalog_resource: [:catalog_resource] + } + end + def finder_params(args) { **project_finder_params(args), with_issues_enabled: args[:with_issues_enabled], - with_merge_requests_enabled: args[:with_merge_requests_enabled] + with_merge_requests_enabled: args[:with_merge_requests_enabled], + full_paths: args[:full_paths] } end @@ -44,3 +78,5 @@ module Resolvers end end end + +Resolvers::ProjectsResolver.prepend_mod_with('Resolvers::ProjectsResolver') diff --git a/app/graphql/resolvers/saved_reply_resolver.rb b/app/graphql/resolvers/saved_reply_resolver.rb index 96bbc139c96..1a5f2c9be78 100644 --- a/app/graphql/resolvers/saved_reply_resolver.rb +++ b/app/graphql/resolvers/saved_reply_resolver.rb @@ -11,8 +11,6 @@ module Resolvers description: 'ID of a saved reply.' def resolve(id:) - return unless Feature.enabled?(:saved_replies, current_user) - saved_reply = ::Users::SavedReply.find_saved_reply(user_id: current_user.id, id: id.model_id) return unless saved_reply diff --git a/app/graphql/resolvers/snippets_resolver.rb b/app/graphql/resolvers/snippets_resolver.rb index 90f5f2cb534..759cc61a8a7 100644 --- a/app/graphql/resolvers/snippets_resolver.rb +++ b/app/graphql/resolvers/snippets_resolver.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true -# rubocop:disable Graphql/ResolverType (inherited from ResolvesSnippets) + +# rubocop:disable Graphql/ResolverType -- inherited from ResolvesSnippets module Resolvers class SnippetsResolver < BaseResolver @@ -45,3 +46,4 @@ module Resolvers end end end +# rubocop:enable Graphql/ResolverType diff --git a/app/graphql/resolvers/users/frecent_groups_resolver.rb b/app/graphql/resolvers/users/frecent_groups_resolver.rb new file mode 100644 index 00000000000..2fc757e31ab --- /dev/null +++ b/app/graphql/resolvers/users/frecent_groups_resolver.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Resolvers + module Users + class FrecentGroupsResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type [Types::GroupType], null: true + + def resolve + return unless current_user.present? + + if Feature.disabled?(:frecent_namespaces_suggestions, current_user) + raise_resource_not_available_error!("'frecent_namespaces_suggestions' feature flag is disabled") + end + + return unless Feature.enabled?(:frecent_namespaces_suggestions, current_user) + + ::Users::GroupVisit.frecent_groups(user_id: current_user.id) + end + end + end +end diff --git a/app/graphql/resolvers/users/frecent_projects_resolver.rb b/app/graphql/resolvers/users/frecent_projects_resolver.rb new file mode 100644 index 00000000000..397d4ca0cfd --- /dev/null +++ b/app/graphql/resolvers/users/frecent_projects_resolver.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Resolvers + module Users + class FrecentProjectsResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type [Types::ProjectType], null: true + + def resolve + return unless current_user.present? + + if Feature.disabled?(:frecent_namespaces_suggestions, current_user) + raise_resource_not_available_error!("'frecent_namespaces_suggestions' feature flag is disabled") + end + + ::Users::ProjectVisit.frecent_projects(user_id: current_user.id) + end + end + end +end diff --git a/app/graphql/resolvers/users/organizations_resolver.rb b/app/graphql/resolvers/users/organizations_resolver.rb new file mode 100644 index 00000000000..ffc1a141eb6 --- /dev/null +++ b/app/graphql/resolvers/users/organizations_resolver.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Resolvers + module Users + class OrganizationsResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::Organizations::OrganizationType.connection_type, null: true + + authorize :read_user_organizations + authorizes_object! + + def resolve(**args) + ::Organizations::UserOrganizationsFinder.new(current_user, object, args).execute + end + end + end +end diff --git a/app/graphql/resolvers/users/snippets_resolver.rb b/app/graphql/resolvers/users/snippets_resolver.rb index 75bba8debab..ea5f6b7b8c9 100644 --- a/app/graphql/resolvers/users/snippets_resolver.rb +++ b/app/graphql/resolvers/users/snippets_resolver.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true -# rubocop:disable Graphql/ResolverType (inherited from ResolvesSnippets) + +# rubocop:disable Graphql/ResolverType -- inherited from ResolvesSnippets module Resolvers module Users @@ -27,3 +28,4 @@ module Resolvers end end end +# rubocop:enable Graphql/ResolverType diff --git a/app/graphql/resolvers/work_items/linked_items_resolver.rb b/app/graphql/resolvers/work_items/linked_items_resolver.rb index 108d5d41b62..f2ff1205d3a 100644 --- a/app/graphql/resolvers/work_items/linked_items_resolver.rb +++ b/app/graphql/resolvers/work_items/linked_items_resolver.rb @@ -3,6 +3,8 @@ module Resolvers module WorkItems class LinkedItemsResolver < BaseResolver + prepend ::WorkItems::LookAheadPreloads + alias_method :linked_items_widget, :object argument :filter, Types::WorkItems::RelatedLinkTypeEnum, @@ -13,30 +15,28 @@ module Resolvers type Types::WorkItems::LinkedItemType.connection_type, null: true - def resolve(filter: nil) - related_work_items(filter).map do |related_work_item| - { - link_id: related_work_item.issue_link_id, - link_type: related_work_item.issue_link_type, - link_created_at: related_work_item.issue_link_created_at, - link_updated_at: related_work_item.issue_link_updated_at, - work_item: related_work_item - } - end + def resolve_with_lookahead(**args) + apply_lookahead(related_work_items(args)) end private - def related_work_items(type) - return [] unless work_item.resource_parent.linked_work_items_feature_flag_enabled? + def related_work_items(args) + return WorkItem.none unless work_item.resource_parent.linked_work_items_feature_flag_enabled? - work_item.linked_work_items(current_user, preload: { project: [:project_feature, :group] }, link_type: type) + offset_pagination( + work_item.linked_work_items(authorize: false, link_type: args[:filter]) + ) end def work_item linked_items_widget.work_item end strong_memoize_attr :work_item + + def node_selection(selection = lookahead) + super.selection(:work_item) + end end end end diff --git a/app/graphql/types/abuse_report_type.rb b/app/graphql/types/abuse_report_type.rb index 012e709cdb5..2532530cfa9 100644 --- a/app/graphql/types/abuse_report_type.rb +++ b/app/graphql/types/abuse_report_type.rb @@ -3,9 +3,18 @@ module Types class AbuseReportType < BaseObject graphql_name 'AbuseReport' + + implements Types::Notes::NoteableInterface + description 'An abuse report' + authorize :read_abuse_report + expose_permissions Types::PermissionTypes::AbuseReport + + field :id, Types::GlobalIDType[::AbuseReport], + null: false, description: 'Global ID of the abuse report.' + field :labels, ::Types::LabelType.connection_type, null: true, description: 'Labels of the abuse report.' end diff --git a/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb b/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb new file mode 100644 index 00000000000..16ce9b82718 --- /dev/null +++ b/app/graphql/types/analytics/cycle_analytics/value_stream_type.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Types + module Analytics + module CycleAnalytics + class ValueStreamType < BaseObject + graphql_name 'ValueStream' + + authorize :read_cycle_analytics + + field :id, + type: ::Types::GlobalIDType[::Analytics::CycleAnalytics::ValueStream], + null: false, + description: "ID of the value stream." + + field :name, + GraphQL::Types::String, + null: false, + description: 'Name of the value stream.' + + field :namespace, Types::NamespaceType, + null: false, + description: 'Namespace the value stream belongs to.' + + field :project, Types::ProjectType, + null: true, + description: 'Project the value stream belongs to, returns empty if it belongs to a group.', + alpha: { milestone: '15.6' } + end + end + end +end diff --git a/app/graphql/types/base_argument.rb b/app/graphql/types/base_argument.rb index cda7fa4a5df..3b4223c3ba1 100644 --- a/app/graphql/types/base_argument.rb +++ b/app/graphql/types/base_argument.rb @@ -9,29 +9,7 @@ module Types def initialize(*args, **kwargs, &block) @doc_reference = kwargs.delete(:see) - # our custom addition `nullable` which allows us to declare - # an argument that must be provided, even if its value is null. - # When `required: true` then required arguments must not be null. - @gl_required = !!kwargs[:required] - @gl_nullable = kwargs[:required] == :nullable - - # Only valid if an argument is also required. - if @gl_nullable - # Since the framework asserts that "required" means "cannot be null" - # we have to switch off "required" but still do the check in `ready?` behind the scenes - kwargs[:required] = false - end - super(*args, **kwargs, &block) end - - def accepts?(value) - # if the argument is declared as required, it must be included - return false if @gl_required && value == :not_given - # if the argument is declared as required, the value can only be null IF it is also nullable. - return false if @gl_required && value.nil? && !@gl_nullable - - true - end end end diff --git a/app/graphql/types/base_input_object.rb b/app/graphql/types/base_input_object.rb index 90a29b0cfb8..d14da9ac878 100644 --- a/app/graphql/types/base_input_object.rb +++ b/app/graphql/types/base_input_object.rb @@ -3,5 +3,7 @@ module Types class BaseInputObject < GraphQL::Schema::InputObject prepend Gitlab::Graphql::CopyFieldDescription + + argument_class ::Types::BaseArgument end end diff --git a/app/graphql/types/ci/catalog/resource_scope_enum.rb b/app/graphql/types/ci/catalog/resource_scope_enum.rb new file mode 100644 index 00000000000..b825c3a7925 --- /dev/null +++ b/app/graphql/types/ci/catalog/resource_scope_enum.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Types + module Ci + module Catalog + class ResourceScopeEnum < BaseEnum + graphql_name 'CiCatalogResourceScope' + description 'Values for scoping catalog resources' + + value 'ALL', 'All catalog resources visible to the current user.', value: :all + end + end + end +end diff --git a/app/graphql/types/ci/catalog/resource_sort_enum.rb b/app/graphql/types/ci/catalog/resource_sort_enum.rb new file mode 100644 index 00000000000..bb0b5a6e0eb --- /dev/null +++ b/app/graphql/types/ci/catalog/resource_sort_enum.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Types + module Ci + module Catalog + class ResourceSortEnum < BaseEnum + graphql_name 'CiCatalogResourceSort' + description 'Values for sorting catalog resources' + + value 'NAME_ASC', 'Name by ascending order.', value: :name_asc + value 'NAME_DESC', 'Name by descending order.', value: :name_desc + value 'LATEST_RELEASED_AT_ASC', 'Latest release date by ascending order.', value: :latest_released_at_asc + value 'LATEST_RELEASED_AT_DESC', 'Latest release date by descending order.', value: :latest_released_at_desc + value 'CREATED_ASC', 'Created date by ascending order.', value: :created_at_asc + value 'CREATED_DESC', 'Created date by descending order.', value: :created_at_desc + end + end + end +end diff --git a/app/graphql/types/ci/catalog/resource_type.rb b/app/graphql/types/ci/catalog/resource_type.rb new file mode 100644 index 00000000000..119313ae52b --- /dev/null +++ b/app/graphql/types/ci/catalog/resource_type.rb @@ -0,0 +1,120 @@ +# frozen_string_literal: true + +module Types + module Ci + module Catalog + # rubocop: disable Graphql/AuthorizeTypes + class ResourceType < BaseObject + graphql_name 'CiCatalogResource' + + connection_type_class Types::CountableConnectionType + + field :open_issues_count, GraphQL::Types::Int, null: false, + description: 'Count of open issues that belong to the the catalog resource.', + alpha: { milestone: '16.3' } + + field :open_merge_requests_count, GraphQL::Types::Int, null: false, + description: 'Count of open merge requests that belong to the the catalog resource.', + alpha: { milestone: '16.3' } + + field :id, GraphQL::Types::ID, null: false, description: 'ID of the catalog resource.', + alpha: { milestone: '15.11' } + + field :name, GraphQL::Types::String, null: true, description: 'Name of the catalog resource.', + alpha: { milestone: '15.11' } + + field :description, GraphQL::Types::String, null: true, description: 'Description of the catalog resource.', + alpha: { milestone: '15.11' } + + field :icon, GraphQL::Types::String, null: true, description: 'Icon for the catalog resource.', + method: :avatar_path, alpha: { milestone: '15.11' } + + field :web_path, GraphQL::Types::String, null: true, description: 'Web path of the catalog resource.', + alpha: { milestone: '16.1' } + + field :versions, Types::ReleaseType.connection_type, null: true, + description: 'Versions of the catalog resource. This field can only be ' \ + 'resolved for one catalog resource in any single request.', + resolver: Resolvers::Ci::Catalog::VersionsResolver, + alpha: { milestone: '16.2' } + + field :latest_version, Types::ReleaseType, null: true, description: 'Latest version of the catalog resource.', + alpha: { milestone: '16.1' } + + field :latest_released_at, Types::TimeType, null: true, + description: "Release date of the catalog resource's latest version.", + alpha: { milestone: '16.5' } + + field :star_count, GraphQL::Types::Int, null: false, + description: 'Number of times the catalog resource has been starred.', + alpha: { milestone: '16.1' } + + field :forks_count, GraphQL::Types::Int, null: false, calls_gitaly: true, + description: 'Number of times the catalog resource has been forked.', + alpha: { milestone: '16.1' } + + field :root_namespace, Types::NamespaceType, null: true, + description: 'Root namespace of the catalog resource.', + alpha: { milestone: '16.1' } + + markdown_field :readme_html, null: false, + alpha: { milestone: '16.1' } + + def open_issues_count + BatchLoader::GraphQL.wrap(object.project.open_issues_count) + end + + def open_merge_requests_count + BatchLoader::GraphQL.wrap(object.project.open_merge_requests_count) + end + + def web_path + ::Gitlab::Routing.url_helpers.project_path(object.project) + end + + def latest_version + BatchLoader::GraphQL.for(object.project).batch do |projects, loader| + latest_releases = ReleasesFinder.new(projects, current_user, latest: true).execute + + latest_releases.index_by(&:project).each do |project, latest_release| + loader.call(project, latest_release) + end + end + end + + def forks_count + BatchLoader::GraphQL.wrap(object.forks_count) + end + + def root_namespace + BatchLoader::GraphQL.for(object.project_id).batch do |project_ids, loader| + projects = Project.id_in(project_ids) + + # This preloader uses traversal_ids to obtain Group-type root namespaces. + # It also preloads each project's immediate parent namespace, which effectively + # preloads the User-type root namespaces since they cannot be nested (parent == root). + Preloaders::ProjectRootAncestorPreloader.new(projects, :group).execute + root_namespaces = projects.map(&:root_ancestor) + + # NamespaceType requires the `:read_namespace` ability. We must preload the policy for + # Group-type namespaces to avoid N+1 queries caused by the authorization requests. + group_root_namespaces = root_namespaces.select { |n| n.type == ::Group.sti_name } + Preloaders::GroupPolicyPreloader.new(group_root_namespaces, current_user).execute + + # For User-type namespaces, the authorization request requires preloading the owner objects. + user_root_namespaces = root_namespaces.select { |n| n.type == ::Namespaces::UserNamespace.sti_name } + ActiveRecord::Associations::Preloader.new(records: user_root_namespaces, associations: :owner).call + + projects.each { |project| loader.call(project.id, project.root_ancestor) } + end + end + + def readme_html_resolver + markdown_context = context.to_h.dup.merge(project: object.project) + ::MarkupHelper.markdown(object.project.repository.readme&.data, markdown_context) + end + end + # rubocop: enable Graphql/AuthorizeTypes + end + end +end diff --git a/app/graphql/types/ci/pipeline_status_enum.rb b/app/graphql/types/ci/pipeline_status_enum.rb index c8e031e18ea..17cf48bb5cf 100644 --- a/app/graphql/types/ci/pipeline_status_enum.rb +++ b/app/graphql/types/ci/pipeline_status_enum.rb @@ -7,6 +7,7 @@ module Types created: 'Pipeline has been created.', waiting_for_resource: 'A resource (for example, a runner) that the pipeline requires to run is unavailable.', preparing: 'Pipeline is preparing to run.', + waiting_for_callback: 'Pipeline is waiting for an external action.', pending: 'Pipeline has not started running yet.', running: 'Pipeline is running.', failed: 'At least one stage of the pipeline failed.', diff --git a/app/graphql/types/container_registry/protection/rule_access_level_enum.rb b/app/graphql/types/container_registry/protection/rule_access_level_enum.rb new file mode 100644 index 00000000000..31e8cbe2e49 --- /dev/null +++ b/app/graphql/types/container_registry/protection/rule_access_level_enum.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Types + module ContainerRegistry + module Protection + class RuleAccessLevelEnum < BaseEnum + graphql_name 'ContainerRegistryProtectionRuleAccessLevel' + description 'Access level of a container registry protection rule resource' + + ::ContainerRegistry::Protection::Rule.push_protected_up_to_access_levels.each_key do |access_level_key| + value access_level_key.upcase, value: access_level_key.to_s, + description: "#{access_level_key.capitalize} access." + end + end + end + end +end diff --git a/app/graphql/types/container_registry/protection/rule_type.rb b/app/graphql/types/container_registry/protection/rule_type.rb new file mode 100644 index 00000000000..387f0202d2d --- /dev/null +++ b/app/graphql/types/container_registry/protection/rule_type.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Types + module ContainerRegistry + module Protection + class RuleType < ::Types::BaseObject + graphql_name 'ContainerRegistryProtectionRule' + description 'A container registry protection rule designed to prevent users with a certain ' \ + 'access level or lower from altering the container registry.' + + authorize :admin_container_image + + field :id, + ::Types::GlobalIDType[::ContainerRegistry::Protection::Rule], + null: false, + description: 'ID of the container registry protection rule.' + + field :container_path_pattern, + GraphQL::Types::String, + null: false, + description: + 'Container repository path pattern protected by the protection rule. ' \ + 'For example `@my-scope/my-container-*`. Wildcard character `*` allowed.' + + field :push_protected_up_to_access_level, + Types::ContainerRegistry::Protection::RuleAccessLevelEnum, + null: false, + description: + 'Max GitLab access level to prevent from pushing container images to the container registry. ' \ + 'For example `DEVELOPER`, `MAINTAINER`, `OWNER`.' + + field :delete_protected_up_to_access_level, + Types::ContainerRegistry::Protection::RuleAccessLevelEnum, + null: false, + description: + 'Max GitLab access level to prevent from pushing container images to the container registry. ' \ + 'For example `DEVELOPER`, `MAINTAINER`, `OWNER`.' + end + end + end +end diff --git a/app/graphql/types/container_repository_details_type.rb b/app/graphql/types/container_repository_details_type.rb index 1ee9e76a1c8..b043a7c9d8d 100644 --- a/app/graphql/types/container_repository_details_type.rb +++ b/app/graphql/types/container_repository_details_type.rb @@ -13,7 +13,8 @@ module Types null: true, description: 'Tags of the container repository.', max_page_size: 20, - resolver: Resolvers::ContainerRepositoryTagsResolver + resolver: Resolvers::ContainerRepositoryTagsResolver, + connection_extension: Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension field :size, GraphQL::Types::Float, diff --git a/app/graphql/types/data_transfer/project_data_transfer_type.rb b/app/graphql/types/data_transfer/project_data_transfer_type.rb index 36afa20194e..363b675209d 100644 --- a/app/graphql/types/data_transfer/project_data_transfer_type.rb +++ b/app/graphql/types/data_transfer/project_data_transfer_type.rb @@ -13,7 +13,6 @@ module Types def total_egress(parent:) return unless Feature.enabled?(:data_transfer_monitoring, parent.group) - return 40_000_000 if Feature.enabled?(:data_transfer_monitoring_mock_data, parent.group) object[:egress_nodes].sum('repository_egress + artifacts_egress + packages_egress + registry_egress') end diff --git a/app/graphql/types/group_member_type.rb b/app/graphql/types/group_member_type.rb index 2745853c9bb..d494c55369d 100644 --- a/app/graphql/types/group_member_type.rb +++ b/app/graphql/types/group_member_type.rb @@ -11,11 +11,11 @@ module Types implements MemberInterface field :group, Types::GroupType, null: true, - description: 'Group that a User is a member of.' + description: 'Group that a user is a member of.' field :notification_email, resolver: Resolvers::GroupMembers::NotificationEmailResolver, - description: "Group notification email for User. Only available for admins." + description: "Group notification email for user. Only available for admins." def group Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, object.source_id).find diff --git a/app/graphql/types/issuable_state_enum.rb b/app/graphql/types/issuable_state_enum.rb index 5a1b11b3bdc..8e3ed1d4bc8 100644 --- a/app/graphql/types/issuable_state_enum.rb +++ b/app/graphql/types/issuable_state_enum.rb @@ -1,10 +1,15 @@ # frozen_string_literal: true +# DO NOT use this ENUM with issues. We need to define a new enum in places where we +# need to filter by state. locked is not a valid state filter for issues. More info in +# https://gitlab.com/gitlab-org/gitlab/-/issues/420667#note_1605900474 module Types class IssuableStateEnum < BaseEnum graphql_name 'IssuableState' description 'State of a GitLab issue or merge request' + INVALID_LOCKED_MESSAGE = 'locked is not a valid state filter for issues.' + value 'opened', description: 'In open state.' value 'closed', description: 'In closed state.' value 'locked', description: 'Discussion has been locked.' diff --git a/app/graphql/types/merge_request_review_state_enum.rb b/app/graphql/types/merge_request_review_state_enum.rb index 45f97758425..c7c82de2906 100644 --- a/app/graphql/types/merge_request_review_state_enum.rb +++ b/app/graphql/types/merge_request_review_state_enum.rb @@ -5,7 +5,11 @@ module Types graphql_name 'MergeRequestReviewState' description 'State of a review of a GitLab merge request.' - from_rails_enum(::MergeRequestReviewer.states, - description: "The merge request is %{name}.") + value 'UNREVIEWED', value: 'unreviewed', + description: 'Awaiting review from merge request reviewer.' + value 'REVIEWED', value: 'reviewed', + description: 'Merge request reviewer has reviewed.' + value 'REQUESTED_CHANGES', value: 'requested_changes', + description: 'Merge request reviewer has requested changes.' end end diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index e6625e44508..9dca82f1750 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -106,7 +106,8 @@ module Types null: false, description: 'Status of all mergeability checks of the merge request.', method: :all_mergeability_checks_results, - alpha: { milestone: '16.5' } + alpha: { milestone: '16.5' }, + calls_gitaly: true field :mergeable_discussions_state, GraphQL::Types::Boolean, null: true, calls_gitaly: true, diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 3af7140aed3..e1bd1f603ad 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -106,6 +106,7 @@ module Types mount_mutation Mutations::Notes::Update::ImageDiffNote mount_mutation Mutations::Notes::RepositionImageDiffNote mount_mutation Mutations::Notes::Destroy + mount_mutation Mutations::Organizations::Create, alpha: { milestone: '16.6' } mount_mutation Mutations::Projects::SyncFork, calls_gitaly: true, alpha: { milestone: '15.9' } mount_mutation Mutations::Releases::Create mount_mutation Mutations::Releases::Update @@ -134,33 +135,36 @@ module Types mount_mutation Mutations::DesignManagement::Move mount_mutation Mutations::DesignManagement::Update mount_mutation Mutations::ContainerExpirationPolicies::Update + mount_mutation Mutations::ContainerRegistry::Protection::Rule::Create, alpha: { milestone: '16.6' } mount_mutation Mutations::ContainerRepositories::Destroy mount_mutation Mutations::ContainerRepositories::DestroyTags + mount_mutation Mutations::Ci::Catalog::Resources::Create, alpha: { milestone: '15.11' } + mount_mutation Mutations::Ci::Catalog::Resources::Unpublish, alpha: { milestone: '16.6' } + mount_mutation Mutations::Ci::Job::Cancel + mount_mutation Mutations::Ci::Job::Play + mount_mutation Mutations::Ci::Job::Retry + mount_mutation Mutations::Ci::Job::ArtifactsDestroy + mount_mutation Mutations::Ci::Job::Unschedule + mount_mutation Mutations::Ci::JobTokenScope::AddProject + mount_mutation Mutations::Ci::JobArtifact::BulkDestroy, alpha: { milestone: '15.10' } + mount_mutation Mutations::Ci::JobArtifact::Destroy + mount_mutation Mutations::Ci::JobTokenScope::RemoveProject mount_mutation Mutations::Ci::Pipeline::Cancel mount_mutation Mutations::Ci::Pipeline::Destroy mount_mutation Mutations::Ci::Pipeline::Retry + mount_mutation Mutations::Ci::PipelineSchedule::Create mount_mutation Mutations::Ci::PipelineSchedule::Delete - mount_mutation Mutations::Ci::PipelineSchedule::TakeOwnership mount_mutation Mutations::Ci::PipelineSchedule::Play - mount_mutation Mutations::Ci::PipelineSchedule::Create + mount_mutation Mutations::Ci::PipelineSchedule::TakeOwnership mount_mutation Mutations::Ci::PipelineSchedule::Update mount_mutation Mutations::Ci::PipelineTrigger::Create, alpha: { milestone: '16.3' } - mount_mutation Mutations::Ci::PipelineTrigger::Update, alpha: { milestone: '16.3' } mount_mutation Mutations::Ci::PipelineTrigger::Delete, alpha: { milestone: '16.3' } + mount_mutation Mutations::Ci::PipelineTrigger::Update, alpha: { milestone: '16.3' } mount_mutation Mutations::Ci::ProjectCiCdSettingsUpdate - mount_mutation Mutations::Ci::Job::ArtifactsDestroy - mount_mutation Mutations::Ci::Job::Play - mount_mutation Mutations::Ci::Job::Retry - mount_mutation Mutations::Ci::Job::Cancel - mount_mutation Mutations::Ci::Job::Unschedule - mount_mutation Mutations::Ci::JobArtifact::Destroy - mount_mutation Mutations::Ci::JobArtifact::BulkDestroy, alpha: { milestone: '15.10' } - mount_mutation Mutations::Ci::JobTokenScope::AddProject - mount_mutation Mutations::Ci::JobTokenScope::RemoveProject + mount_mutation Mutations::Ci::Runner::BulkDelete, alpha: { milestone: '15.3' } mount_mutation Mutations::Ci::Runner::Create, alpha: { milestone: '15.10' } - mount_mutation Mutations::Ci::Runner::Update mount_mutation Mutations::Ci::Runner::Delete - mount_mutation Mutations::Ci::Runner::BulkDelete, alpha: { milestone: '15.3' } + mount_mutation Mutations::Ci::Runner::Update mount_mutation Mutations::Ci::RunnersRegistrationToken::Reset mount_mutation Mutations::Namespace::PackageSettings::Update mount_mutation Mutations::Groups::Update @@ -171,6 +175,7 @@ module Types extensions: [::Gitlab::Graphql::Limit::FieldCallCount => { limit: 1 }] mount_mutation Mutations::Packages::DestroyFile mount_mutation Mutations::Packages::Protection::Rule::Create, alpha: { milestone: '16.5' } + mount_mutation Mutations::Packages::Protection::Rule::Delete, alpha: { milestone: '16.6' } mount_mutation Mutations::Packages::DestroyFiles mount_mutation Mutations::Packages::Cleanup::Policy::Update mount_mutation Mutations::Echo diff --git a/app/graphql/types/namespace/package_settings_type.rb b/app/graphql/types/namespace/package_settings_type.rb index 61240243b1f..6c6144f2357 100644 --- a/app/graphql/types/namespace/package_settings_type.rb +++ b/app/graphql/types/namespace/package_settings_type.rb @@ -20,21 +20,18 @@ module Types field :maven_duplicates_allowed, GraphQL::Types::Boolean, null: false, description: 'Indicates whether duplicate Maven packages are allowed for this namespace.' - field :nuget_duplicate_exception_regex, Types::UntrustedRegexp, - null: true, - description: 'When nuget_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. ' \ - 'Error is raised if `nuget_duplicates_option` feature flag is disabled.' - field :nuget_duplicates_allowed, GraphQL::Types::Boolean, - null: false, - description: 'Indicates whether duplicate NuGet packages are allowed for this namespace. ' \ - 'Error is raised if `nuget_duplicates_option` feature flag is disabled.' - field :maven_package_requests_forwarding, GraphQL::Types::Boolean, null: true, description: 'Indicates whether Maven package forwarding is allowed for this namespace.' field :npm_package_requests_forwarding, GraphQL::Types::Boolean, null: true, description: 'Indicates whether npm package forwarding is allowed for this namespace.' + field :nuget_duplicate_exception_regex, Types::UntrustedRegexp, + null: true, + description: 'When nuget_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect. ' + field :nuget_duplicates_allowed, GraphQL::Types::Boolean, + null: false, + description: 'Indicates whether duplicate NuGet packages are allowed for this namespace. ' field :pypi_package_requests_forwarding, GraphQL::Types::Boolean, null: true, description: 'Indicates whether PyPI package forwarding is allowed for this namespace.' diff --git a/app/graphql/types/notes/noteable_interface.rb b/app/graphql/types/notes/noteable_interface.rb index 9971511d6ce..7c75f213e24 100644 --- a/app/graphql/types/notes/noteable_interface.rb +++ b/app/graphql/types/notes/noteable_interface.rb @@ -21,6 +21,8 @@ module Types Types::DesignManagement::DesignType when ::AlertManagement::Alert Types::AlertManagement::AlertType + when AbuseReport + Types::AbuseReportType else raise "Unknown GraphQL type for #{object}" end diff --git a/app/graphql/types/organizations/organization_type.rb b/app/graphql/types/organizations/organization_type.rb index cae0ef2232e..e7ba8de527c 100644 --- a/app/graphql/types/organizations/organization_type.rb +++ b/app/graphql/types/organizations/organization_type.rb @@ -33,6 +33,10 @@ module Types null: false, description: 'Path of the organization.', alpha: { milestone: '16.4' } + field :web_url, GraphQL::Types::String, + null: false, + description: 'Web URL of the organization.', + alpha: { milestone: '16.6' } end end end diff --git a/app/graphql/types/organizations/organization_user_badge_type.rb b/app/graphql/types/organizations/organization_user_badge_type.rb new file mode 100644 index 00000000000..f4e18676dd1 --- /dev/null +++ b/app/graphql/types/organizations/organization_user_badge_type.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Types + module Organizations + # rubocop: disable Graphql/AuthorizeTypes -- Already authorized in parent OrganizationUserType. + class OrganizationUserBadgeType < BaseObject + graphql_name 'OrganizationUserBadge' + description 'An organization user badge.' + + field :text, + GraphQL::Types::String, + null: false, + description: 'Badge text.' + + field :variant, + GraphQL::Types::String, + null: false, + description: 'Badge variant.' + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/organizations/organization_user_type.rb b/app/graphql/types/organizations/organization_user_type.rb index 41924586f38..ce036c7dd4a 100644 --- a/app/graphql/types/organizations/organization_user_type.rb +++ b/app/graphql/types/organizations/organization_user_type.rb @@ -13,7 +13,7 @@ module Types alias_method :organization_user, :object field :badges, - [GraphQL::Types::String], + [::Types::Organizations::OrganizationUserBadgeType], null: true, description: 'Badges describing the user within the organization.', alpha: { milestone: '16.4' } @@ -29,7 +29,7 @@ module Types alpha: { milestone: '16.4' } def badges - user_badges_in_admin_section(organization_user.user).pluck(:text) # rubocop:disable CodeReuse/ActiveRecord + user_badges_in_admin_section(organization_user.user) end end end diff --git a/app/graphql/types/packages/package_base_type.rb b/app/graphql/types/packages/package_base_type.rb index aa580d48709..5102e4ebcd5 100644 --- a/app/graphql/types/packages/package_base_type.rb +++ b/app/graphql/types/packages/package_base_type.rb @@ -10,11 +10,19 @@ module Types authorize :read_package + expose_permissions Types::PermissionTypes::Package + field :id, ::Types::GlobalIDType[::Packages::Package], null: false, description: 'ID of the package.' field :_links, Types::Packages::PackageLinksType, null: false, method: :itself, description: 'Map of links to perform actions on the package.' - field :can_destroy, GraphQL::Types::Boolean, null: false, description: 'Whether the user can destroy the package.' + field :can_destroy, GraphQL::Types::Boolean, + null: false, + deprecated: { + reason: 'Superseded by `user_permissions` field. See `Types::PermissionTypes::Package` type', + milestone: '16.6' + }, + description: 'Whether the user can destroy the package.' field :created_at, Types::TimeType, null: false, description: 'Date of creation.' field :metadata, Types::Packages::MetadataType, null: true, diff --git a/app/graphql/types/packages/protection/rule_type.rb b/app/graphql/types/packages/protection/rule_type.rb index 1e969d39ce2..e2ea2d89d2d 100644 --- a/app/graphql/types/packages/protection/rule_type.rb +++ b/app/graphql/types/packages/protection/rule_type.rb @@ -10,6 +10,11 @@ module Types authorize :admin_package + field :id, + ::Types::GlobalIDType[::Packages::Protection::Rule], + null: false, + description: 'ID of the package protection rule.' + field :package_name_pattern, GraphQL::Types::String, null: false, diff --git a/app/graphql/types/packages/pypi/metadatum_type.rb b/app/graphql/types/packages/pypi/metadatum_type.rb index 63452d8ab6e..8ccdb592c52 100644 --- a/app/graphql/types/packages/pypi/metadatum_type.rb +++ b/app/graphql/types/packages/pypi/metadatum_type.rb @@ -9,8 +9,17 @@ module Types authorize :read_package + field :author_email, GraphQL::Types::String, null: true, + description: 'Author email address(es) in RFC-822 format.' + field :description, GraphQL::Types::String, null: true, + description: 'Longer description that can run to several paragraphs.' + field :description_content_type, GraphQL::Types::String, null: true, + description: 'Markup syntax used in the description field.' field :id, ::Types::GlobalIDType[::Packages::Pypi::Metadatum], null: false, description: 'ID of the metadatum.' + field :keywords, GraphQL::Types::String, null: true, description: 'List of keywords, separated by commas.' + field :metadata_version, GraphQL::Types::String, null: true, description: 'Metadata version.' field :required_python, GraphQL::Types::String, null: true, description: 'Required Python version of the Pypi package.' + field :summary, GraphQL::Types::String, null: true, description: 'One-line summary of the description.' end end end diff --git a/app/graphql/types/permission_types/abuse_report.rb b/app/graphql/types/permission_types/abuse_report.rb new file mode 100644 index 00000000000..abd5d545d02 --- /dev/null +++ b/app/graphql/types/permission_types/abuse_report.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Types + module PermissionTypes + class AbuseReport < BasePermissionType + graphql_name 'AbuseReportPermissions' + + abilities :read_abuse_report, :create_note + end + end +end diff --git a/app/graphql/types/permission_types/base_permission_type.rb b/app/graphql/types/permission_types/base_permission_type.rb index d45c61f489b..3c0e68bdaf2 100644 --- a/app/graphql/types/permission_types/base_permission_type.rb +++ b/app/graphql/types/permission_types/base_permission_type.rb @@ -21,7 +21,7 @@ module Types kword_args = kword_args.reverse_merge( name: name, type: GraphQL::Types::Boolean, - description: "Indicates the user can perform `#{name}` on this resource", + description: "If `true`, the user can perform `#{name}` on this resource", null: false) field(**kword_args, &block) # rubocop:disable Graphql/Descriptions diff --git a/app/graphql/types/permission_types/ci/job.rb b/app/graphql/types/permission_types/ci/job.rb index c9a85317e67..35904fb1fc3 100644 --- a/app/graphql/types/permission_types/ci/job.rb +++ b/app/graphql/types/permission_types/ci/job.rb @@ -8,6 +8,7 @@ module Types abilities :read_job_artifacts, :read_build ability_field :update_build, calls_gitaly: true + ability_field :cancel_build, calls_gitaly: true end end end diff --git a/app/graphql/types/permission_types/ci/pipeline.rb b/app/graphql/types/permission_types/ci/pipeline.rb index cfd68380005..94adbf7c59b 100644 --- a/app/graphql/types/permission_types/ci/pipeline.rb +++ b/app/graphql/types/permission_types/ci/pipeline.rb @@ -8,6 +8,7 @@ module Types abilities :admin_pipeline, :destroy_pipeline ability_field :update_pipeline, calls_gitaly: true + ability_field :cancel_pipeline, calls_gitaly: true end end end diff --git a/app/graphql/types/permission_types/package.rb b/app/graphql/types/permission_types/package.rb new file mode 100644 index 00000000000..debde3a1a8e --- /dev/null +++ b/app/graphql/types/permission_types/package.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Types + module PermissionTypes + class Package < BasePermissionType + graphql_name 'PackagePermissions' + + ability_field :destroy_package, + description: 'If `true`, the user can perform `destroy_package` on this resource' + end + end +end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 95caefc3825..ec87f133843 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -641,6 +641,12 @@ module Types resolver: Resolvers::AutocompleteUsersResolver, description: 'Search users for autocompletion' + field :detailed_import_status, + ::Types::Projects::DetailedImportStatusType, + null: true, + description: 'Detailed import status of the project.', + method: :import_state + def timelog_categories object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories) end diff --git a/app/graphql/types/projects/detailed_import_status_type.rb b/app/graphql/types/projects/detailed_import_status_type.rb new file mode 100644 index 00000000000..9cba176e097 --- /dev/null +++ b/app/graphql/types/projects/detailed_import_status_type.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Types + module Projects + class DetailedImportStatusType < BaseObject + graphql_name 'DetailedImportStatus' + description 'Details of the import status of a project.' + + authorize :read_project + + field :id, ::Types::GlobalIDType[::ProjectImportState], + description: 'ID of the import state.' + + field :status, GraphQL::Types::String, + description: 'Current status of the import.' + + field :url, GraphQL::Types::String, + description: 'Import url.' + + field :last_error, GraphQL::Types::String, + description: 'Last error of the import.', + null: true, + authorize: :read_import_error + + field :last_update_at, Types::TimeType, + description: 'Time of the last update.' + + field :last_update_started_at, Types::TimeType, + description: 'Time of the start of the last update.' + + field :last_successful_update_at, Types::TimeType, + description: 'Time of the last successful update.' + + def url + object.project.safe_import_url + end + end + end +end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index d185007f05b..173e877d86c 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -21,6 +21,20 @@ module Types required: true, description: 'Global ID of the CI stage.' end + field :ci_catalog_resources, + ::Types::Ci::Catalog::ResourceType.connection_type, + null: true, + alpha: { milestone: '15.11' }, + description: 'All CI/CD Catalog resources under a common namespace, visible to an authorized user', + resolver: ::Resolvers::Ci::Catalog::ResourcesResolver + + field :ci_catalog_resource, + ::Types::Ci::Catalog::ResourceType, + null: true, + alpha: { milestone: '16.1' }, + description: 'A single CI/CD Catalog resource visible to an authorized user', + resolver: ::Resolvers::Ci::Catalog::ResourceResolver + field :ci_variables, Types::Ci::InstanceVariableType.connection_type, null: true, @@ -41,6 +55,14 @@ module Types null: false, description: 'Fields related to design management.' field :echo, resolver: Resolvers::EchoResolver + field :frecent_groups, [Types::GroupType], + resolver: Resolvers::Users::FrecentGroupsResolver, + description: "A user's frecently visited groups. Requires the `frecent_namespaces_suggestions` feature flag to be enabled.", + alpha: { milestone: '16.6' } + field :frecent_projects, [Types::ProjectType], + resolver: Resolvers::Users::FrecentProjectsResolver, + description: "A user's frecently visited projects. Requires the `frecent_namespaces_suggestions` feature flag to be enabled.", + alpha: { milestone: '16.6' } field :gitpod_enabled, GraphQL::Types::Boolean, null: true, description: "Whether Gitpod is enabled in application settings." diff --git a/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb b/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb index fb7d722069f..7dd47611a2e 100644 --- a/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb +++ b/app/graphql/types/security/codequality_reports_comparer/degradation_type.rb @@ -3,10 +3,9 @@ module Types module Security module CodequalityReportsComparer - # rubocop: disable Graphql/AuthorizeTypes (The resolver authorizes the request) + # rubocop: disable Graphql/AuthorizeTypes -- The resolver authorizes the request class DegradationType < BaseObject graphql_name 'CodequalityReportsComparerReportDegradation' - description 'Represents a degradation on the compared codequality report.' field :description, GraphQL::Types::String, diff --git a/app/graphql/types/security/codequality_reports_comparer/report_generation_status_enum.rb b/app/graphql/types/security/codequality_reports_comparer/report_generation_status_enum.rb new file mode 100644 index 00000000000..dace3aec97c --- /dev/null +++ b/app/graphql/types/security/codequality_reports_comparer/report_generation_status_enum.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Types + module Security + module CodequalityReportsComparer + class ReportGenerationStatusEnum < BaseEnum + graphql_name 'CodequalityReportsComparerReportGenerationStatus' + description 'Represents the generation status of the compared codequality report.' + + value 'PARSED', value: :parsed, description: 'Report was generated.' + value 'PARSING', value: :parsing, description: 'Report is being generated.' + value 'ERROR', value: :error, description: 'An error happened while generating the report.' + end + end + end +end diff --git a/app/graphql/types/security/codequality_reports_comparer/report_type.rb b/app/graphql/types/security/codequality_reports_comparer/report_type.rb index 8a41160141a..d20c9dd9ab6 100644 --- a/app/graphql/types/security/codequality_reports_comparer/report_type.rb +++ b/app/graphql/types/security/codequality_reports_comparer/report_type.rb @@ -3,7 +3,7 @@ module Types module Security module CodequalityReportsComparer - # rubocop: disable Graphql/AuthorizeTypes (Parent node applies authorization) + # rubocop: disable Graphql/AuthorizeTypes -- Parent node applies authorization class ReportType < BaseObject graphql_name 'CodequalityReportsComparerReport' diff --git a/app/graphql/types/security/codequality_reports_comparer/status_enum.rb b/app/graphql/types/security/codequality_reports_comparer/status_enum.rb index 9cab2664db8..fdccfdc7e44 100644 --- a/app/graphql/types/security/codequality_reports_comparer/status_enum.rb +++ b/app/graphql/types/security/codequality_reports_comparer/status_enum.rb @@ -4,11 +4,11 @@ module Types module Security module CodequalityReportsComparer class StatusEnum < BaseEnum - graphql_name 'CodequalityReportsComparerReportStatus' - description 'Report comparison status' + graphql_name 'CodequalityReportsComparerStatus' + description 'Represents the state of the code quality report.' - value 'SUCCESS', value: 'success', description: 'Report successfully generated.' - value 'FAILED', value: 'failed', description: 'Report failed to generate.' + value 'SUCCESS', value: 'success', description: 'No degradations found in the head pipeline report.' + value 'FAILED', value: 'failed', description: 'Report generated and there are new code quality degradations.' value 'NOT_FOUND', value: 'not_found', description: 'Head report or base report not found.' end end diff --git a/app/graphql/types/security/codequality_reports_comparer/summary_type.rb b/app/graphql/types/security/codequality_reports_comparer/summary_type.rb index cd4a594c193..43037be5245 100644 --- a/app/graphql/types/security/codequality_reports_comparer/summary_type.rb +++ b/app/graphql/types/security/codequality_reports_comparer/summary_type.rb @@ -3,7 +3,7 @@ module Types module Security module CodequalityReportsComparer - # rubocop: disable Graphql/AuthorizeTypes (The resolver authorizes the request) + # rubocop: disable Graphql/AuthorizeTypes -- The resolver authorizes the request class SummaryType < BaseObject graphql_name 'CodequalityReportsComparerReportSummary' diff --git a/app/graphql/types/security/codequality_reports_comparer_type.rb b/app/graphql/types/security/codequality_reports_comparer_type.rb index 8088bf84627..32fe8c12330 100644 --- a/app/graphql/types/security/codequality_reports_comparer_type.rb +++ b/app/graphql/types/security/codequality_reports_comparer_type.rb @@ -2,12 +2,17 @@ module Types module Security - # rubocop: disable Graphql/AuthorizeTypes (The resolver authorizes the request) + # rubocop: disable Graphql/AuthorizeTypes -- The resolver authorizes the request class CodequalityReportsComparerType < BaseObject graphql_name 'CodequalityReportsComparer' description 'Represents reports comparison for code quality.' + field :status, + type: CodequalityReportsComparer::ReportGenerationStatusEnum, + null: true, + description: 'Compared codequality report generation status.' + field :report, type: CodequalityReportsComparer::ReportType, null: true, diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb index 47d486265b0..040711b5f58 100644 --- a/app/graphql/types/user_interface.rb +++ b/app/graphql/types/user_interface.rb @@ -71,6 +71,11 @@ module Types type: GraphQL::Types::String, null: false, description: 'Web path of the user.' + field :organizations, + resolver: Resolvers::Users::OrganizationsResolver, + null: true, + alpha: { milestone: '16.6' }, + description: 'Organizations where the user has access.' field :group_memberships, type: Types::GroupMemberType.connection_type, null: true, @@ -134,13 +139,11 @@ module Types field :saved_replies, Types::SavedReplyType.connection_type, null: true, - description: 'Saved replies authored by the user. ' \ - 'Will not return saved replies if `saved_replies` feature flag is disabled.' + description: 'Saved replies authored by the user.' field :saved_reply, resolver: Resolvers::SavedReplyResolver, - description: 'Saved reply authored by the user. ' \ - 'Will not return saved reply if `saved_replies` feature flag is disabled.' + description: 'Saved reply authored by the user.' field :gitpod_enabled, GraphQL::Types::Boolean, null: true, description: 'Whether Gitpod is enabled at the user level.' @@ -197,6 +200,11 @@ module Types null: true, description: 'Timestamp of when the user was created.' + field :last_activity_on, + type: Types::DateType, + null: true, + description: 'Date the user last performed any actions.' + field :pronouns, type: ::GraphQL::Types::String, null: true, diff --git a/app/graphql/types/work_items/linked_item_type.rb b/app/graphql/types/work_items/linked_item_type.rb index a4dbeed7480..1b989d78091 100644 --- a/app/graphql/types/work_items/linked_item_type.rb +++ b/app/graphql/types/work_items/linked_item_type.rb @@ -2,21 +2,29 @@ module Types module WorkItems - # rubocop:disable Graphql/AuthorizeTypes class LinkedItemType < BaseObject graphql_name 'LinkedWorkItemType' + authorize :read_work_item + field :link_created_at, Types::TimeType, - description: 'Timestamp the link was created.', null: false + description: 'Timestamp the link was created.', null: false, + method: :issue_link_created_at field :link_id, ::Types::GlobalIDType[::WorkItems::RelatedWorkItemLink], - description: 'Global ID of the link.', null: false + description: 'Global ID of the link.', null: false, + method: :issue_link_id field :link_type, GraphQL::Types::String, - description: 'Type of link.', null: false + description: 'Type of link.', null: false, + method: :issue_link_type field :link_updated_at, Types::TimeType, - description: 'Timestamp the link was updated.', null: false + description: 'Timestamp the link was updated.', null: false, + method: :issue_link_updated_at field :work_item, Types::WorkItemType, - description: 'Linked work item.', null: false + description: 'Linked work item.', null: true + + def work_item + object + end end - # rubocop:enable Graphql/AuthorizeTypes end end diff --git a/app/graphql/types/work_items/widgets/linked_items_type.rb b/app/graphql/types/work_items/widgets/linked_items_type.rb index 2611c2456c5..c541a12a050 100644 --- a/app/graphql/types/work_items/widgets/linked_items_type.rb +++ b/app/graphql/types/work_items/widgets/linked_items_type.rb @@ -13,6 +13,7 @@ module Types field :linked_items, Types::WorkItems::LinkedItemType.connection_type, null: true, complexity: 5, alpha: { milestone: '16.3' }, + extras: [:lookahead], description: 'Linked items for the work item. Returns `null` ' \ 'if `linked_work_items` feature flag is disabled.', resolver: Resolvers::WorkItems::LinkedItemsResolver |