diff options
Diffstat (limited to 'app/graphql')
48 files changed, 515 insertions, 64 deletions
diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb index 527eb50b644..52f3e56aec3 100644 --- a/app/graphql/graphql_triggers.rb +++ b/app/graphql/graphql_triggers.rb @@ -59,6 +59,12 @@ module GraphqlTriggers ) end + def self.merge_request_diff_generated(merge_request) + GitlabSchema.subscriptions.trigger( + :merge_request_diff_generated, { issuable_id: merge_request.to_gid }, merge_request + ) + end + def self.work_item_updated(work_item) # becomes is necessary here since this can be triggered with both a WorkItem and also an Issue # depending on the update service the call comes from diff --git a/app/graphql/mutations/branch_rules/create.rb b/app/graphql/mutations/branch_rules/create.rb new file mode 100644 index 00000000000..c478d981c33 --- /dev/null +++ b/app/graphql/mutations/branch_rules/create.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Mutations + module BranchRules + class Create < BaseMutation + graphql_name 'BranchRuleCreate' + + argument :project_path, GraphQL::Types::ID, + required: true, + description: 'Full path to the project that the branch is associated with.' + + argument :name, GraphQL::Types::String, + required: true, + description: 'Branch name, with wildcards, for the branch rules.' + + field :branch_rule, + Types::Projects::BranchRuleType, + null: true, + description: 'Branch rule after mutation.' + + def resolve(project_path:, name:) + project = Project.find_by_full_path(project_path) + + service_params = protected_branch_params(name) + protected_branch = ::ProtectedBranches::CreateService.new(project, current_user, service_params).execute + + if protected_branch.persisted? + { + branch_rule: ::Projects::BranchRule.new(project, protected_branch), + errors: [] + } + else + { errors: errors_on_object(protected_branch) } + end + rescue Gitlab::Access::AccessDeniedError + raise_resource_not_available_error! + end + + def protected_branch_params(name) + { + name: name, + push_access_levels_attributes: access_level_attributes(:push), + merge_access_levels_attributes: access_level_attributes(:merge) + } + end + + def access_level_attributes(type) + ::ProtectedRefs::AccessLevelParams.new( + type, + {}, + with_defaults: true + ).access_levels + end + end + end +end diff --git a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb index 7aa78509bea..a5d9014af17 100644 --- a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb +++ b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb @@ -48,6 +48,10 @@ module Mutations ::Types::WorkItems::Widgets::AwardEmojiUpdateInputType, required: false, description: 'Input for emoji reactions widget.' + argument :notes_widget, + ::Types::WorkItems::Widgets::NotesInputType, + required: false, + description: 'Input for notes widget.' end end end diff --git a/app/graphql/mutations/issues/set_assignees.rb b/app/graphql/mutations/issues/set_assignees.rb index 8413c89b010..1e55cdee0a8 100644 --- a/app/graphql/mutations/issues/set_assignees.rb +++ b/app/graphql/mutations/issues/set_assignees.rb @@ -8,7 +8,7 @@ module Mutations include Assignable def assign!(issue, users, mode) - permitted, forbidden = users.partition { |u| u.can?(:read_issue, issue) } + permitted, forbidden = users.partition { |u| u.can?(:read_issue, issue.resource_parent) } super(issue, permitted, mode) diff --git a/app/graphql/mutations/ml/models/base.rb b/app/graphql/mutations/ml/models/base.rb new file mode 100644 index 00000000000..e3c5a7a13a8 --- /dev/null +++ b/app/graphql/mutations/ml/models/base.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Mutations + module Ml + module Models + class Base < BaseMutation + authorize :write_model_registry + + argument :project_path, GraphQL::Types::ID, + required: true, + description: "Project the model to mutate is in." + + field :model, + Types::Ml::ModelType, + null: true, + description: 'Model after mutation.' + end + end + end +end diff --git a/app/graphql/mutations/ml/models/create.rb b/app/graphql/mutations/ml/models/create.rb new file mode 100644 index 00000000000..21570fc34b8 --- /dev/null +++ b/app/graphql/mutations/ml/models/create.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Mutations + module Ml + module Models + class Create < Base + graphql_name 'MlModelCreate' + + include FindsProject + + argument :name, GraphQL::Types::String, + required: true, + description: 'Name of the model.' + + argument :description, GraphQL::Types::String, + required: false, + description: 'Description of the model.' + + def resolve(**args) + project = authorized_find!(args[:project_path]) + + model = ::Ml::CreateModelService.new(project, args[:name], current_user, args[:description]).execute + + { + model: model.persisted? ? model : nil, + errors: errors_on_object(model) + } + end + end + end + end +end diff --git a/app/graphql/mutations/namespace/package_settings/update.rb b/app/graphql/mutations/namespace/package_settings/update.rb index 813c5687642..a429dd06a7c 100644 --- a/app/graphql/mutations/namespace/package_settings/update.rb +++ b/app/graphql/mutations/namespace/package_settings/update.rb @@ -51,6 +51,16 @@ module Mutations required: false, description: copy_field_description(Types::Namespace::PackageSettingsType, :nuget_duplicate_exception_regex) + argument :terraform_module_duplicates_allowed, + GraphQL::Types::Boolean, + required: false, + description: copy_field_description(Types::Namespace::PackageSettingsType, :terraform_module_duplicates_allowed) + + argument :terraform_module_duplicate_exception_regex, + Types::UntrustedRegexp, + required: false, + description: copy_field_description(Types::Namespace::PackageSettingsType, :terraform_module_duplicate_exception_regex) + argument :maven_package_requests_forwarding, GraphQL::Types::Boolean, required: false, diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb index 7ce508e5ef1..754b453ce5d 100644 --- a/app/graphql/mutations/work_items/create.rb +++ b/app/graphql/mutations/work_items/create.rb @@ -60,6 +60,7 @@ module Mutations def resolve(project_path: nil, namespace_path: nil, **attributes) container_path = project_path || namespace_path container = authorized_find!(container_path) + check_env_feature_available!(container) check_feature_available!(container) params = global_id_compatibility_params(attributes).merge(author_id: current_user.id) @@ -83,6 +84,15 @@ module Mutations private + # This is just a temporary measure while we migrate and backfill epic internal_ids + # More info in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139367 + def check_env_feature_available!(container) + return unless container.is_a?(::Group) && Rails.env.production? + + message = 'Group level work items are disabled. Only project paths allowed in `namespacePath`.' + raise Gitlab::Graphql::Errors::ArgumentError, message + end + def check_feature_available!(container) return unless container.is_a?(::Group) && Feature.disabled?(:namespace_level_work_items, container) diff --git a/app/graphql/resolvers/ci/catalog/resources/versions_resolver.rb b/app/graphql/resolvers/ci/catalog/resources/versions_resolver.rb index 9332076a493..899b407b180 100644 --- a/app/graphql/resolvers/ci/catalog/resources/versions_resolver.rb +++ b/app/graphql/resolvers/ci/catalog/resources/versions_resolver.rb @@ -11,12 +11,18 @@ module Resolvers # field is evaluated on more than one node, it causes performance degradation. extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + argument :name, GraphQL::Types::String, + required: false, + description: 'Name of the version.' + argument :sort, Types::Ci::Catalog::Resources::VersionSortEnum, required: false, description: 'Sort versions by given criteria.' - def resolve(sort: nil) - ::Ci::Catalog::Resources::VersionsFinder.new(object, current_user, sort: sort).execute + alias_method :catalog_resource, :object + + def resolve(name: nil, sort: nil) + ::Ci::Catalog::Resources::VersionsFinder.new(catalog_resource, current_user, name: name, sort: sort).execute end end end diff --git a/app/graphql/resolvers/ci/runner_owner_project_resolver.rb b/app/graphql/resolvers/ci/runner_owner_project_resolver.rb index f4e044b81c9..28c39427872 100644 --- a/app/graphql/resolvers/ci/runner_owner_project_resolver.rb +++ b/app/graphql/resolvers/ci/runner_owner_project_resolver.rb @@ -34,7 +34,7 @@ module Resolvers def resolve_owner return unless runner.project_type? - BatchLoader::GraphQL.for(runner.id).batch(key: :runner_owner_projects) do |runner_ids, loader| + BatchLoader::GraphQL.for(runner.id).batch do |runner_ids, loader| # rubocop: disable CodeReuse/ActiveRecord runner_and_projects_with_row_number = ::Ci::RunnerProject diff --git a/app/graphql/resolvers/ci/runner_projects_resolver.rb b/app/graphql/resolvers/ci/runner_projects_resolver.rb index c5037965e20..99c9bba1bd6 100644 --- a/app/graphql/resolvers/ci/runner_projects_resolver.rb +++ b/app/graphql/resolvers/ci/runner_projects_resolver.rb @@ -28,7 +28,7 @@ module Resolvers return unless runner.project_type? # rubocop:disable CodeReuse/ActiveRecord - BatchLoader::GraphQL.for(runner.id).batch(key: :runner_projects) do |runner_ids, loader| + BatchLoader::GraphQL.for(runner.id).batch do |runner_ids, loader| plucked_runner_and_project_ids = ::Ci::RunnerProject .select(:runner_id, :project_id) .where(runner_id: runner_ids) diff --git a/app/graphql/resolvers/ci/runner_resolver.rb b/app/graphql/resolvers/ci/runner_resolver.rb index 4250b069d20..60fb4163afe 100644 --- a/app/graphql/resolvers/ci/runner_resolver.rb +++ b/app/graphql/resolvers/ci/runner_resolver.rb @@ -6,13 +6,12 @@ module Resolvers include LooksAhead type Types::Ci::RunnerType, null: true - extras [:lookahead] description 'Runner information.' argument :id, - type: ::Types::GlobalIDType[::Ci::Runner], - required: true, - description: 'Runner ID.' + type: ::Types::GlobalIDType[::Ci::Runner], + required: true, + description: 'Runner ID.' def resolve_with_lookahead(id:) find_runner(id: id) @@ -21,19 +20,13 @@ module Resolvers private def find_runner(id:) - runner_id = GitlabSchema.parse_gid(id, expected_type: ::Ci::Runner).model_id.to_i - key = { - preload_tag_list: lookahead.selects?(:tag_list), - preload_creator: lookahead.selects?(:created_by) - } - - BatchLoader::GraphQL.for(runner_id).batch(key: key) do |ids, loader, batch| - results = ::Ci::Runner.id_in(ids) - results = results.with_tags if batch[:key][:preload_tag_list] - results = results.with_creator if batch[:key][:preload_creator] - - results.each { |record| loader.call(record.id, record) } - end + preloads = [] + preloads << :creator if lookahead.selects?(:created_by) + preloads << :tags if lookahead.selects?(:tag_list) + + runner_id = GitlabSchema.parse_gid(id, expected_type: ::Ci::Runner).model_id + + ::Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Runner, runner_id, preloads).find end end end diff --git a/app/graphql/resolvers/ci/runners_resolver.rb b/app/graphql/resolvers/ci/runners_resolver.rb index 9121c413b1f..38d2ebe046b 100644 --- a/app/graphql/resolvers/ci/runners_resolver.rb +++ b/app/graphql/resolvers/ci/runners_resolver.rb @@ -82,7 +82,7 @@ module Resolvers 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 + preload: {} # we'll handle preloading ourselves }.compact .merge(parent_param) end diff --git a/app/graphql/resolvers/concerns/resolves_groups.rb b/app/graphql/resolvers/concerns/resolves_groups.rb index 86dda5cb1cb..1673b1bd37f 100644 --- a/app/graphql/resolvers/concerns/resolves_groups.rb +++ b/app/graphql/resolvers/concerns/resolves_groups.rb @@ -5,6 +5,19 @@ module ResolvesGroups extend ActiveSupport::Concern include LooksAhead + PRELOADS = { + container_repositories_count: [:container_repositories], + custom_emoji: [:custom_emoji], + full_path: [:route], + path: [:route], + web_url: [:route], + dependency_proxy_blob_count: [:dependency_proxy_blobs], + dependency_proxy_blobs: [:dependency_proxy_blobs], + dependency_proxy_image_count: [:dependency_proxy_manifests], + dependency_proxy_image_ttl_policy: [:dependency_proxy_image_ttl_policy], + dependency_proxy_setting: [:dependency_proxy_setting] + }.freeze + def resolve_with_lookahead(...) apply_lookahead(resolve_groups(...)) end @@ -17,17 +30,8 @@ module ResolvesGroups end def preloads - { - container_repositories_count: [:container_repositories], - custom_emoji: [:custom_emoji], - full_path: [:route], - path: [:route], - web_url: [:route], - dependency_proxy_blob_count: [:dependency_proxy_blobs], - dependency_proxy_blobs: [:dependency_proxy_blobs], - dependency_proxy_image_count: [:dependency_proxy_manifests], - dependency_proxy_image_ttl_policy: [:dependency_proxy_image_ttl_policy], - dependency_proxy_setting: [:dependency_proxy_setting] - } + PRELOADS end end + +ResolvesGroups.prepend_mod diff --git a/app/graphql/resolvers/container_repository_tags_resolver.rb b/app/graphql/resolvers/container_repository_tags_resolver.rb index 50adf98fa07..d3929451bd0 100644 --- a/app/graphql/resolvers/container_repository_tags_resolver.rb +++ b/app/graphql/resolvers/container_repository_tags_resolver.rb @@ -14,6 +14,11 @@ module Resolvers required: false, default_value: nil + argument :referrers, GraphQL::Types::Boolean, + description: 'Include tag referrers.', + required: false, + default_value: nil + alias_method :container_repository, :object def resolve(sort:, **filters) @@ -25,7 +30,8 @@ module Resolvers last: filters[:after], sort: map_sort_field(sort), name: filters[:name], - page_size: page_size + page_size: page_size, + referrers: filters[:referrers] ) Gitlab::Graphql::ExternallyPaginatedArray.new( diff --git a/app/graphql/resolvers/full_path_resolver.rb b/app/graphql/resolvers/full_path_resolver.rb index b8df54f49ab..2c64d08a219 100644 --- a/app/graphql/resolvers/full_path_resolver.rb +++ b/app/graphql/resolvers/full_path_resolver.rb @@ -4,10 +4,10 @@ module Resolvers module FullPathResolver extend ActiveSupport::Concern - prepended do + included do argument :full_path, GraphQL::Types::ID, - required: true, - description: 'Full path of the project, group, or namespace. For example, `gitlab-org/gitlab-foss`.' + required: true, + description: "Full path of the #{target_type}. For example, `gitlab-org/gitlab-foss`." end def model_by_full_path(model, full_path) diff --git a/app/graphql/resolvers/group_resolver.rb b/app/graphql/resolvers/group_resolver.rb index 4260e18829e..e3b651b6493 100644 --- a/app/graphql/resolvers/group_resolver.rb +++ b/app/graphql/resolvers/group_resolver.rb @@ -2,7 +2,11 @@ module Resolvers class GroupResolver < BaseResolver - prepend FullPathResolver + def self.target_type + 'group' + end + + include FullPathResolver type Types::GroupType, null: true diff --git a/app/graphql/resolvers/ml/find_models_resolver.rb b/app/graphql/resolvers/ml/find_models_resolver.rb new file mode 100644 index 00000000000..b9901100e22 --- /dev/null +++ b/app/graphql/resolvers/ml/find_models_resolver.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Resolvers + module Ml + class FindModelsResolver < Resolvers::BaseResolver + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + + type ::Types::Ml::ModelType.connection_type, null: true + + argument :name, GraphQL::Types::String, + required: false, + description: 'Search for names that include the string.' + + argument :order_by, ::Types::Ml::ModelsOrderByEnum, + required: false, + description: 'Ordering column. Default is created_at.' + + argument :sort, ::Types::SortDirectionEnum, + required: false, + description: 'Ordering column. Default is desc.' + + def resolve(**args) + return unless current_user.can?(:read_model_registry, object) + + find_params = { + name: args[:name], + order_by: args[:order_by].to_s, + sort: args[:sort].to_s + } + + ::Projects::Ml::ModelFinder.new(object, find_params).execute + end + end + end +end diff --git a/app/graphql/resolvers/namespace_resolver.rb b/app/graphql/resolvers/namespace_resolver.rb index 17b3800d151..a0b16758625 100644 --- a/app/graphql/resolvers/namespace_resolver.rb +++ b/app/graphql/resolvers/namespace_resolver.rb @@ -2,7 +2,11 @@ module Resolvers class NamespaceResolver < BaseResolver - prepend FullPathResolver + def self.target_type + 'namespace' + end + + include FullPathResolver type Types::NamespaceType, null: true diff --git a/app/graphql/resolvers/organizations/organizations_resolver.rb b/app/graphql/resolvers/organizations/organizations_resolver.rb new file mode 100644 index 00000000000..ab21a84645b --- /dev/null +++ b/app/graphql/resolvers/organizations/organizations_resolver.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Resolvers + module Organizations + class OrganizationsResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::Organizations::OrganizationType.connection_type, null: true + authorize :read_organization + + def resolve + # For the Organization MVC, all the organizations are public. We need to change this to only accessible + # organizations once we start supporting private organizations. + # See https://gitlab.com/groups/gitlab-org/-/epics/10649. + ::Organizations::Organization.all + end + end + end +end diff --git a/app/graphql/resolvers/organizations/projects_resolver.rb b/app/graphql/resolvers/organizations/projects_resolver.rb new file mode 100644 index 00000000000..836fe0ae059 --- /dev/null +++ b/app/graphql/resolvers/organizations/projects_resolver.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Resolvers + module Organizations + class ProjectsResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::ProjectType, null: true + + authorize :read_project + + alias_method :organization, :object + + def resolve + ::ProjectsFinder.new(current_user: current_user, params: { organization: organization }).execute + end + end + end +end diff --git a/app/graphql/resolvers/project_resolver.rb b/app/graphql/resolvers/project_resolver.rb index 2132447da5e..931fefcea50 100644 --- a/app/graphql/resolvers/project_resolver.rb +++ b/app/graphql/resolvers/project_resolver.rb @@ -2,7 +2,11 @@ module Resolvers class ProjectResolver < BaseResolver - prepend FullPathResolver + def self.target_type + 'project' + end + + include FullPathResolver type Types::ProjectType, null: true diff --git a/app/graphql/resolvers/projects/fork_targets_resolver.rb b/app/graphql/resolvers/projects/fork_targets_resolver.rb index 5e8be325d43..27797b9f0af 100644 --- a/app/graphql/resolvers/projects/fork_targets_resolver.rb +++ b/app/graphql/resolvers/projects/fork_targets_resolver.rb @@ -3,7 +3,7 @@ module Resolvers module Projects class ForkTargetsResolver < BaseResolver - include ResolvesGroups + include LooksAhead include Gitlab::Graphql::Authorize::AuthorizeResource type Types::NamespaceType.connection_type, null: true @@ -17,10 +17,15 @@ module Resolvers required: false, description: 'Search query for path or name.' + def resolve_with_lookahead(**args) + fork_targets = ForkTargetsFinder.new(project, current_user).execute(args) + apply_lookahead(fork_targets) + end + private - def resolve_groups(**args) - ForkTargetsFinder.new(project, current_user).execute(args) + def preloads + ResolvesGroups::PRELOADS end end end diff --git a/app/graphql/resolvers/users_resolver.rb b/app/graphql/resolvers/users_resolver.rb index 90a6bd3e6b2..a512c6bafe1 100644 --- a/app/graphql/resolvers/users_resolver.rb +++ b/app/graphql/resolvers/users_resolver.rb @@ -28,10 +28,19 @@ module Resolvers default_value: false, description: 'Return only admin users.' - def resolve(ids: nil, usernames: nil, sort: nil, search: nil, admins: nil) + argument :group_id, ::Types::GlobalIDType[::Group], + required: false, + description: 'Return users member of a given group.' + + def resolve(ids: nil, usernames: nil, sort: nil, search: nil, admins: nil, group_id: nil) authorize!(usernames) - ::UsersFinder.new(context[:current_user], finder_params(ids, usernames, sort, search, admins)).execute + group = group_id ? find_authorized_group!(group_id) : nil + + ::UsersFinder.new( + context[:current_user], + finder_params(ids, usernames, sort, search, admins, group) + ).execute end def ready?(**args) @@ -52,16 +61,27 @@ module Resolvers private - def finder_params(ids, usernames, sort, search, admins) + def finder_params(ids, usernames, sort, search, admins, group) params = {} params[:sort] = sort if sort params[:username] = usernames if usernames params[:id] = parse_gids(ids) if ids params[:search] = search if search params[:admins] = admins if admins + params[:group] = group if group params end + def find_authorized_group!(group_id) + group = GitlabSchema.find_by_gid(group_id).sync + + unless Ability.allowed?(current_user, :read_group, group) + raise_resource_not_available_error! "Could not find a Group with ID #{group_id}" + end + + group + end + def parse_gids(gids) gids.map { |gid| GitlabSchema.parse_gid(gid, expected_type: ::User).model_id } end diff --git a/app/graphql/types/ci/catalog/resources/component_type.rb b/app/graphql/types/ci/catalog/resources/component_type.rb index 3b4771446cb..71ed31725a6 100644 --- a/app/graphql/types/ci/catalog/resources/component_type.rb +++ b/app/graphql/types/ci/catalog/resources/component_type.rb @@ -16,7 +16,8 @@ module Types description: 'Name of the component.', alpha: { milestone: '16.7' } - field :path, GraphQL::Types::String, null: true, + field :include_path, GraphQL::Types::String, null: true, + method: :path, description: 'Path used to include the component.', alpha: { milestone: '16.7' } diff --git a/app/graphql/types/ci/catalog/resources/version_type.rb b/app/graphql/types/ci/catalog/resources/version_type.rb index 689f649afc5..b52a1c6b13d 100644 --- a/app/graphql/types/ci/catalog/resources/version_type.rb +++ b/app/graphql/types/ci/catalog/resources/version_type.rb @@ -20,13 +20,13 @@ module Types field :released_at, Types::TimeType, null: true, description: 'Timestamp of when the version was released.', alpha: { milestone: '16.7' } - field :tag_name, GraphQL::Types::String, null: true, method: :name, - description: 'Name of the tag associated with the version.', - alpha: { milestone: '16.7' } + field :name, GraphQL::Types::String, null: true, + description: 'Name that uniquely identifies the version within the catalog resource.', + alpha: { milestone: '16.8' } - field :tag_path, GraphQL::Types::String, null: true, - description: 'Relative web path to the tag associated with the version.', - alpha: { milestone: '16.7' } + field :path, GraphQL::Types::String, null: true, + description: 'Relative web path to the version.', + alpha: { milestone: '16.8' } field :author, Types::UserType, null: true, description: 'User that created the version.', alpha: { milestone: '16.7' } @@ -39,12 +39,22 @@ module Types description: 'Components belonging to the catalog resource.', alpha: { milestone: '16.7' } + field :readme_html, GraphQL::Types::String, null: true, calls_gitaly: true, + description: 'GitLab Flavored Markdown rendering of README.md. This field ' \ + 'can only be resolved for one version in any single request.', + alpha: { milestone: '16.8' } do + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 # To avoid N+1 calls to Gitaly + end + def author Gitlab::Graphql::Loaders::BatchModelLoader.new(User, object.author_id).find end - def tag_path - Gitlab::Routing.url_helpers.project_tag_path(object.project, object.name) + def readme_html + return unless Ability.allowed?(current_user, :read_code, object.project) + + markdown_context = context.to_h.dup.merge(project: object.project) + ::MarkupHelper.markdown(object.readme&.data, markdown_context) end end # rubocop: enable Graphql/AuthorizeTypes diff --git a/app/graphql/types/ci/ci_cd_setting_type.rb b/app/graphql/types/ci/ci_cd_setting_type.rb index f01c63d717b..0c2d1b788af 100644 --- a/app/graphql/types/ci/ci_cd_setting_type.rb +++ b/app/graphql/types/ci/ci_cd_setting_type.rb @@ -27,7 +27,7 @@ module Types field :merge_pipelines_enabled, GraphQL::Types::Boolean, null: true, - description: 'Whether merge pipelines are enabled.', + description: 'Whether merged results pipelines are enabled.', method: :merge_pipelines_enabled? field :project, Types::ProjectType, diff --git a/app/graphql/types/ci/inherited_ci_variable_type.rb b/app/graphql/types/ci/inherited_ci_variable_type.rb index 2d8dcdaeefe..c90e34b25dd 100644 --- a/app/graphql/types/ci/inherited_ci_variable_type.rb +++ b/app/graphql/types/ci/inherited_ci_variable_type.rb @@ -15,6 +15,10 @@ module Types null: true, description: 'Name of the variable.' + field :description, GraphQL::Types::String, + null: true, + description: 'Description of the variable.' + field :raw, GraphQL::Types::Boolean, null: true, description: 'Indicates whether the variable is raw.' diff --git a/app/graphql/types/ci/instance_variable_type.rb b/app/graphql/types/ci/instance_variable_type.rb index e3230556769..457cd8a1ba2 100644 --- a/app/graphql/types/ci/instance_variable_type.rb +++ b/app/graphql/types/ci/instance_variable_type.rb @@ -13,6 +13,10 @@ module Types null: false, description: 'ID of the variable.' + field :description, GraphQL::Types::String, + null: true, + description: 'Description of the variable.' + field :environment_scope, GraphQL::Types::String, null: true, deprecated: { diff --git a/app/graphql/types/commit_signatures/verification_status_enum.rb b/app/graphql/types/commit_signatures/verification_status_enum.rb index 9df1b7abd82..d0d8f6670c3 100644 --- a/app/graphql/types/commit_signatures/verification_status_enum.rb +++ b/app/graphql/types/commit_signatures/verification_status_enum.rb @@ -6,10 +6,10 @@ module Types module CommitSignatures class VerificationStatusEnum < BaseEnum graphql_name 'VerificationStatus' - description 'Verification status of a GPG or X.509 signature for a commit.' + description 'Verification status of a GPG, X.509 or SSH signature for a commit.' - ::CommitSignatures::GpgSignature.verification_statuses.each do |status, _| - value status.upcase, value: status, description: "#{status} verification status." + ::Enums::CommitSignature.verification_statuses.each_key do |status| + value status.to_s.upcase, value: status.to_s, description: "#{status} verification status." end end end diff --git a/app/graphql/types/container_repository_referrer_type.rb b/app/graphql/types/container_repository_referrer_type.rb new file mode 100644 index 00000000000..d9d4d150b95 --- /dev/null +++ b/app/graphql/types/container_repository_referrer_type.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Types + class ContainerRepositoryReferrerType < BaseObject + graphql_name 'ContainerRepositoryReferrer' + + description 'A referrer for a container repository tag' + + authorize :read_container_image + + expose_permissions Types::PermissionTypes::ContainerRepositoryTag + + field :artifact_type, GraphQL::Types::String, description: 'Artifact type of the referrer.' + field :digest, GraphQL::Types::String, description: 'Digest of the referrer.' + end +end diff --git a/app/graphql/types/container_repository_tag_type.rb b/app/graphql/types/container_repository_tag_type.rb index cf8796410d3..7691844645a 100644 --- a/app/graphql/types/container_repository_tag_type.rb +++ b/app/graphql/types/container_repository_tag_type.rb @@ -22,6 +22,8 @@ module Types field :location, GraphQL::Types::String, null: false, description: 'URL of the tag.' field :name, GraphQL::Types::String, null: false, description: 'Name of the tag.' field :path, GraphQL::Types::String, null: false, description: 'Path of the tag.' + field :published_at, Types::TimeType, null: true, description: 'Timestamp when the tag was published.' + field :referrers, [Types::ContainerRepositoryReferrerType], null: true, description: 'Referrers for this tag.' field :revision, GraphQL::Types::String, null: true, description: 'Revision of the tag.' field :short_revision, GraphQL::Types::String, null: true, description: 'Short revision of the tag.' field :total_size, GraphQL::Types::BigInt, null: true, description: 'Size of the tag.' diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index 7234948033b..01b741b5a98 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -214,6 +214,21 @@ module Types complexity: 5, resolver: Resolvers::NestedGroupsResolver + field :descendant_groups_count, + GraphQL::Types::Int, + null: false, + description: 'Count of direct descendant groups of this group.' + + field :group_members_count, + GraphQL::Types::Int, + null: false, + description: 'Count of direct members of this group.' + + field :projects_count, + GraphQL::Types::Int, + null: false, + description: 'Count of direct projects in this group.' + field :ci_variables, Types::Ci::GroupVariableType.connection_type, null: true, @@ -339,6 +354,27 @@ module Types group.dependency_proxy_setting || group.create_dependency_proxy_setting end + def descendant_groups_count + BatchLoader::GraphQL.for(object.id).batch do |group_ids, loader| + descendants_counts = Group.id_in(group_ids).descendant_groups_counts + descendants_counts.each { |group_id, count| loader.call(group_id, count) } + end + end + + def projects_count + BatchLoader::GraphQL.for(object.id).batch do |group_ids, loader| + projects_counts = Group.id_in(group_ids).projects_counts + projects_counts.each { |group_id, count| loader.call(group_id, count) } + end + end + + def group_members_count + BatchLoader::GraphQL.for(object.id).batch do |group_ids, loader| + members_counts = Group.id_in(group_ids).group_members_counts + members_counts.each { |group_id, count| loader.call(group_id, count) } + end + end + private def group diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index d7c3b313f84..3572cfd346b 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -248,6 +248,18 @@ module Types 'if `sast_reports_in_inline_diff` feature flag is disabled.', resolver: ::Resolvers::CodequalityReportsComparerResolver + field :allows_multiple_assignees, + GraphQL::Types::Boolean, + method: :allows_multiple_assignees?, + description: 'Allows assigning multiple users to a merge request.', + null: false + + field :allows_multiple_reviewers, + GraphQL::Types::Boolean, + method: :allows_multiple_reviewers?, + description: 'Allows assigning multiple reviewers to a merge request.', + null: false + markdown_field :title_html, null: true markdown_field :description_html, null: true diff --git a/app/graphql/types/ml/model_links_type.rb b/app/graphql/types/ml/model_links_type.rb new file mode 100644 index 00000000000..9d18efb2e17 --- /dev/null +++ b/app/graphql/types/ml/model_links_type.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Types + module Ml + # rubocop: disable Graphql/AuthorizeTypes -- authorization in ModelDetailsResolver + class ModelLinksType < BaseObject + graphql_name 'MLModelLinks' + description 'Represents links to perform actions on the model' + + present_using ::Ml::ModelPresenter + + field :show_path, GraphQL::Types::String, + null: true, description: 'Path to the details page of the model.', method: :path + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/graphql/types/ml/model_type.rb b/app/graphql/types/ml/model_type.rb index ca63918b370..a26d50cbdc4 100644 --- a/app/graphql/types/ml/model_type.rb +++ b/app/graphql/types/ml/model_type.rb @@ -7,10 +7,25 @@ module Types graphql_name 'MlModel' description 'Machine learning model in the model registry' + connection_type_class Types::LimitedCountableConnectionType + + present_using ::Ml::ModelPresenter + field :id, ::Types::GlobalIDType[::Ml::Model], null: false, description: 'ID of the model.' field :name, ::GraphQL::Types::String, null: false, description: 'Name of the model.' + field :created_at, Types::TimeType, null: false, description: 'Date of creation.' + + field :description, ::GraphQL::Types::String, null: false, description: 'Description of the model.' + + field :latest_version, ::Types::Ml::ModelVersionType, null: true, description: 'Latest version of the model.' + + field :version_count, ::GraphQL::Types::Int, null: true, description: 'Count of versions in the model.' + + field :_links, ::Types::Ml::ModelLinksType, null: false, method: :itself, + description: 'Map of links to perform actions on the model.' + field :versions, ::Types::Ml::ModelVersionType.connection_type, null: true, description: 'Versions of the model.' diff --git a/app/graphql/types/ml/model_version_links_type.rb b/app/graphql/types/ml/model_version_links_type.rb index 142f62bfad2..a8497334fc6 100644 --- a/app/graphql/types/ml/model_version_links_type.rb +++ b/app/graphql/types/ml/model_version_links_type.rb @@ -11,6 +11,9 @@ module Types field :show_path, GraphQL::Types::String, null: true, description: 'Path to the details page of the model version.', method: :path + + field :package_path, GraphQL::Types::String, + null: true, description: 'Path to the package of the model version.', method: :package_path end # rubocop: enable Graphql/AuthorizeTypes end diff --git a/app/graphql/types/ml/models_order_by_enum.rb b/app/graphql/types/ml/models_order_by_enum.rb new file mode 100644 index 00000000000..db96a2e2d7d --- /dev/null +++ b/app/graphql/types/ml/models_order_by_enum.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module Ml + class ModelsOrderByEnum < BaseEnum + graphql_name 'MlModelsOrderBy' + description 'Values for ordering machine learning models by a specific field' + + value 'NAME', 'Ordered by name.', value: :name + value 'CREATED_AT', 'Ordered by creation time.', value: :created_at + value 'UPDATED_AT', 'Ordered by update time.', value: :updated_at + value 'ID', 'Ordered by id.', value: :id + end + end +end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 590bc0ed282..0a725c2e0a7 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -111,6 +111,7 @@ module Types mount_mutation Mutations::Projects::SyncFork, calls_gitaly: true, alpha: { milestone: '15.9' } mount_mutation Mutations::Projects::Star, alpha: { milestone: '16.7' } mount_mutation Mutations::BranchRules::Update, alpha: { milestone: '16.7' } + mount_mutation Mutations::BranchRules::Create, alpha: { milestone: '16.7' } mount_mutation Mutations::Releases::Create mount_mutation Mutations::Releases::Update mount_mutation Mutations::Releases::Delete @@ -202,6 +203,7 @@ module Types mount_mutation Mutations::Users::SetNamespaceCommitEmail mount_mutation Mutations::WorkItems::Subscribe, alpha: { milestone: '16.3' } mount_mutation Mutations::Admin::AbuseReportLabels::Create, alpha: { milestone: '16.4' } + mount_mutation Mutations::Ml::Models::Create, alpha: { milestone: '16.8' } end end diff --git a/app/graphql/types/namespace/package_settings_type.rb b/app/graphql/types/namespace/package_settings_type.rb index 7bf76ae7de5..621cb091019 100644 --- a/app/graphql/types/namespace/package_settings_type.rb +++ b/app/graphql/types/namespace/package_settings_type.rb @@ -35,6 +35,12 @@ module Types field :pypi_package_requests_forwarding, GraphQL::Types::Boolean, null: true, description: 'Indicates whether PyPI package forwarding is allowed for this namespace.' + field :terraform_module_duplicate_exception_regex, Types::UntrustedRegexp, + null: true, + description: 'When terraform_module_duplicates_allowed is false, you can publish duplicate packages with names that match this regex. Otherwise, this setting has no effect.' + field :terraform_module_duplicates_allowed, GraphQL::Types::Boolean, + null: false, + description: 'Indicates whether duplicate Terraform packages are allowed for this namespace.' field :lock_maven_package_requests_forwarding, GraphQL::Types::Boolean, null: false, diff --git a/app/graphql/types/namespace_type.rb b/app/graphql/types/namespace_type.rb index 85bda507ff7..3420f16213f 100644 --- a/app/graphql/types/namespace_type.rb +++ b/app/graphql/types/namespace_type.rb @@ -4,7 +4,7 @@ module Types class NamespaceType < BaseObject graphql_name 'Namespace' - authorize :read_namespace_via_membership + authorize :read_namespace field :id, GraphQL::Types::ID, null: false, description: 'ID of the namespace.' diff --git a/app/graphql/types/organizations/organization_type.rb b/app/graphql/types/organizations/organization_type.rb index 379bf9956a3..d36c92541ef 100644 --- a/app/graphql/types/organizations/organization_type.rb +++ b/app/graphql/types/organizations/organization_type.rb @@ -43,6 +43,10 @@ module Types null: false, description: 'Path of the organization.', alpha: { milestone: '16.4' } + field :projects, Types::ProjectType.connection_type, null: false, + description: 'Projects within this organization that the user has access to.', + alpha: { milestone: '16.8' }, + resolver: ::Resolvers::Organizations::ProjectsResolver field :web_url, GraphQL::Types::String, null: false, diff --git a/app/graphql/types/permission_types/issue.rb b/app/graphql/types/permission_types/issue.rb index a76dc88adfc..65586b384be 100644 --- a/app/graphql/types/permission_types/issue.rb +++ b/app/graphql/types/permission_types/issue.rb @@ -8,7 +8,7 @@ module Types abilities :read_issue, :admin_issue, :update_issue, :reopen_issue, :read_design, :create_design, :destroy_design, - :create_note, :update_design + :create_note, :update_design, :admin_issue_relation end end end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 8e84605cb05..7f49c717c78 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -663,6 +663,24 @@ module Types null: true, resolver: Resolvers::Analytics::CycleAnalytics::ValueStreamsResolver + field :ml_models, ::Types::Ml::ModelType.connection_type, + null: true, + alpha: { milestone: '16.8' }, + description: 'Finds machine learning models', + resolver: Resolvers::Ml::FindModelsResolver + + field :allows_multiple_merge_request_assignees, + GraphQL::Types::Boolean, + method: :allows_multiple_merge_request_assignees?, + description: 'Project allows assigning multiple users to a merge request.', + null: false + + field :allows_multiple_merge_request_reviewers, + GraphQL::Types::Boolean, + method: :allows_multiple_merge_request_reviewers?, + description: 'Project allows assigning multiple reviewers to a merge request.', + null: false + def timelog_categories object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories) end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 0e39ff2c030..47a049fe10c 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -86,7 +86,8 @@ module Types field :jobs, ::Types::Ci::JobType.connection_type, null: true, - description: 'All jobs on this GitLab instance.', + description: 'All jobs on this GitLab instance.' \ + ' Returns an empty result for users without administrator access.', resolver: ::Resolvers::Ci::AllJobsResolver field :merge_request, Types::MergeRequestType, null: true, @@ -122,6 +123,11 @@ module Types resolver: Resolvers::Organizations::OrganizationResolver, description: "Find an organization.", alpha: { milestone: '16.4' } + field :organizations, Types::Organizations::OrganizationType.connection_type, + null: true, + resolver: Resolvers::Organizations::OrganizationsResolver, + description: "List organizations.", + alpha: { milestone: '16.8' } field :package, description: 'Find a package. This field can only be resolved for one query in any single request. Returns `null` if a package has no `default` status.', resolver: Resolvers::PackageDetailsResolver diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb index 7f33f77ec14..5a90a65f50f 100644 --- a/app/graphql/types/subscription_type.rb +++ b/app/graphql/types/subscription_type.rb @@ -63,6 +63,10 @@ module Types field :merge_request_approval_state_updated, subscription: Subscriptions::IssuableUpdated, null: true, description: 'Triggered when approval state of a merge request is updated.' + + field :merge_request_diff_generated, + subscription: Subscriptions::IssuableUpdated, null: true, + description: 'Triggered when a merge request diff is generated.' end end diff --git a/app/graphql/types/work_items/widgets/notes_input_type.rb b/app/graphql/types/work_items/widgets/notes_input_type.rb new file mode 100644 index 00000000000..fc7f4c84658 --- /dev/null +++ b/app/graphql/types/work_items/widgets/notes_input_type.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module WorkItems + module Widgets + class NotesInputType < BaseInputObject + graphql_name 'WorkItemWidgetNotesInput' + + argument :discussion_locked, GraphQL::Types::Boolean, + required: true, + description: 'Discussion lock attribute for notes widget of the work item.' + end + end + end +end diff --git a/app/graphql/types/work_items/widgets/notes_type.rb b/app/graphql/types/work_items/widgets/notes_type.rb index 199001649bb..4f7f1c3b4cc 100644 --- a/app/graphql/types/work_items/widgets/notes_type.rb +++ b/app/graphql/types/work_items/widgets/notes_type.rb @@ -12,6 +12,10 @@ module Types implements Types::WorkItems::WidgetInterface + field :discussion_locked, GraphQL::Types::Boolean, + null: true, + description: 'Discussion lock attribute of the work item.' + # This field loads user comments, system notes and resource events as a discussion for an work item, # raising the complexity considerably. In order to discourage fetching this field as part of fetching # a list of issues we raise the complexity |