diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 18:42:06 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-08-20 18:42:06 +0000 |
commit | 6e4e1050d9dba2b7b2523fdd1768823ab85feef4 (patch) | |
tree | 78be5963ec075d80116a932011d695dd33910b4e /app/graphql/resolvers | |
parent | 1ce776de4ae122aba3f349c02c17cebeaa8ecf07 (diff) | |
download | gitlab-ce-6e4e1050d9dba2b7b2523fdd1768823ab85feef4.tar.gz |
Add latest changes from gitlab-org/gitlab@13-3-stable-ee
Diffstat (limited to 'app/graphql/resolvers')
18 files changed, 300 insertions, 160 deletions
diff --git a/app/graphql/resolvers/board_list_issues_resolver.rb b/app/graphql/resolvers/board_list_issues_resolver.rb new file mode 100644 index 00000000000..a7cc367379d --- /dev/null +++ b/app/graphql/resolvers/board_list_issues_resolver.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Resolvers + class BoardListIssuesResolver < BaseResolver + type Types::IssueType, null: true + + alias_method :list, :object + + def resolve(**args) + service = Boards::Issues::ListService.new(list.board.resource_parent, context[:current_user], { board_id: list.board.id, id: list.id }) + Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection.new(service.execute) + end + + # https://gitlab.com/gitlab-org/gitlab/-/issues/235681 + def self.complexity_multiplier(args) + 0.005 + end + end +end diff --git a/app/graphql/resolvers/board_lists_resolver.rb b/app/graphql/resolvers/board_lists_resolver.rb index f8d62ba86af..b1d43934f24 100644 --- a/app/graphql/resolvers/board_lists_resolver.rb +++ b/app/graphql/resolvers/board_lists_resolver.rb @@ -6,12 +6,16 @@ module Resolvers type Types::BoardListType, null: true + argument :id, GraphQL::ID_TYPE, + required: false, + description: 'Find a list by its global ID' + alias_method :board, :object - def resolve(lookahead: nil) + def resolve(lookahead: nil, id: nil) authorize!(board) - lists = board_lists + lists = board_lists(id) if load_preferences?(lookahead) List.preload_preferences_for_user(lists, context[:current_user]) @@ -22,8 +26,13 @@ module Resolvers private - def board_lists - service = Boards::Lists::ListService.new(board.resource_parent, context[:current_user]) + def board_lists(id) + service = Boards::Lists::ListService.new( + board.resource_parent, + context[:current_user], + list_id: extract_list_id(id) + ) + service.execute(board, create_default_lists: false) end @@ -34,5 +43,11 @@ module Resolvers def load_preferences?(lookahead) lookahead&.selection(:edges)&.selection(:node)&.selects?(:collapsed) end + + def extract_list_id(gid) + return unless gid.present? + + GitlabSchema.parse_gid(gid, expected_type: ::List).model_id + end end end diff --git a/app/graphql/resolvers/ci/pipeline_stages_resolver.rb b/app/graphql/resolvers/ci/pipeline_stages_resolver.rb new file mode 100644 index 00000000000..f9817d8b97b --- /dev/null +++ b/app/graphql/resolvers/ci/pipeline_stages_resolver.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + class PipelineStagesResolver < BaseResolver + include LooksAhead + + alias_method :pipeline, :object + + def resolve_with_lookahead + apply_lookahead(pipeline.stages) + end + + def preloads + { + statuses: [:needs] + } + end + end + end +end diff --git a/app/graphql/resolvers/ci_configuration/sast_resolver.rb b/app/graphql/resolvers/ci_configuration/sast_resolver.rb deleted file mode 100644 index e8c42076ea2..00000000000 --- a/app/graphql/resolvers/ci_configuration/sast_resolver.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require "json" - -module Resolvers - module CiConfiguration - class SastResolver < BaseResolver - SAST_UI_SCHEMA_PATH = 'app/validators/json_schemas/security_ci_configuration_schemas/sast_ui_schema.json' - - type ::Types::CiConfiguration::Sast::Type, null: true - - def resolve(**args) - Gitlab::Json.parse(File.read(Rails.root.join(SAST_UI_SCHEMA_PATH))) - end - end - end -end diff --git a/app/graphql/resolvers/concerns/issue_resolver_fields.rb b/app/graphql/resolvers/concerns/issue_resolver_fields.rb new file mode 100644 index 00000000000..bf2f510dd89 --- /dev/null +++ b/app/graphql/resolvers/concerns/issue_resolver_fields.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +module IssueResolverFields + extend ActiveSupport::Concern + + prepended do + argument :iid, GraphQL::STRING_TYPE, + required: false, + description: 'IID of the issue. For example, "1"' + argument :iids, [GraphQL::STRING_TYPE], + required: false, + description: 'List of IIDs of issues. For example, [1, 2]' + argument :label_name, GraphQL::STRING_TYPE.to_list_type, + required: false, + description: 'Labels applied to this issue' + argument :milestone_title, GraphQL::STRING_TYPE.to_list_type, + required: false, + description: 'Milestone applied to this issue' + argument :assignee_username, GraphQL::STRING_TYPE, + required: false, + description: 'Username of a user assigned to the issue' + argument :assignee_id, GraphQL::STRING_TYPE, + required: false, + description: 'ID of a user assigned to the issues, "none" and "any" values supported' + argument :created_before, Types::TimeType, + required: false, + description: 'Issues created before this date' + argument :created_after, Types::TimeType, + required: false, + description: 'Issues created after this date' + argument :updated_before, Types::TimeType, + required: false, + description: 'Issues updated before this date' + argument :updated_after, Types::TimeType, + required: false, + description: 'Issues updated after this date' + argument :closed_before, Types::TimeType, + required: false, + description: 'Issues closed before this date' + argument :closed_after, Types::TimeType, + required: false, + description: 'Issues closed after this date' + argument :search, GraphQL::STRING_TYPE, + required: false, + description: 'Search query for issue title or description' + argument :types, [Types::IssueTypeEnum], + as: :issue_types, + description: 'Filter issues by the given issue types', + required: false + end + + def resolve(**args) + # The project could have been loaded in batch by `BatchLoader`. + # At this point we need the `id` of the project to query for issues, so + # make sure it's loaded and not `nil` before continuing. + parent = object.respond_to?(:sync) ? object.sync : object + return Issue.none if parent.nil? + + # Will need to be made group & namespace aware with + # https://gitlab.com/gitlab-org/gitlab-foss/issues/54520 + args[:iids] ||= [args.delete(:iid)].compact if args[:iid] + args[:attempt_project_search_optimizations] = true if args[:search].present? + + finder = IssuesFinder.new(current_user, args) + + continue_issue_resolve(parent, finder, **args) + end + + class_methods do + def resolver_complexity(args, child_complexity:) + complexity = super + complexity += 2 if args[:labelName] + + complexity + end + end +end diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb index 7ed88be52b9..0c01efd4f9a 100644 --- a/app/graphql/resolvers/concerns/resolves_merge_requests.rb +++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb @@ -38,6 +38,9 @@ module ResolvesMergeRequests assignees: [:assignees], labels: [:labels], author: [:author], + merged_at: [:metrics], + commit_count: [:metrics], + approved_by: [:approver_users], milestone: [:milestone], head_pipeline: [:merge_request_diff, { head_pipeline: [:merge_request] }] } diff --git a/app/graphql/resolvers/design_management/designs_resolver.rb b/app/graphql/resolvers/design_management/designs_resolver.rb index 81f94d5cb30..955ea6304e0 100644 --- a/app/graphql/resolvers/design_management/designs_resolver.rb +++ b/app/graphql/resolvers/design_management/designs_resolver.rb @@ -27,19 +27,20 @@ module Resolvers current_user, ids: design_ids(ids), filenames: filenames, - visible_at_version: version(at_version), - order: :id + visible_at_version: version(at_version) ).execute end private def version(at_version) - GitlabSchema.object_from_id(at_version)&.sync if at_version + return unless at_version + + GitlabSchema.object_from_id(at_version, expected_type: ::DesignManagement::Version)&.sync end def design_ids(ids) - ids&.map { |id| GlobalID.parse(id).model_id } + ids&.map { |id| GlobalID.parse(id, expected_type: ::DesignManagement::Design).model_id } end def issue diff --git a/app/graphql/resolvers/group_issues_resolver.rb b/app/graphql/resolvers/group_issues_resolver.rb new file mode 100644 index 00000000000..ac51011eea8 --- /dev/null +++ b/app/graphql/resolvers/group_issues_resolver.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Resolvers + class GroupIssuesResolver < IssuesResolver + argument :include_subgroups, GraphQL::BOOLEAN_TYPE, + required: false, + default_value: false, + description: 'Include issues belonging to subgroups.' + end +end diff --git a/app/graphql/resolvers/group_milestones_resolver.rb b/app/graphql/resolvers/group_milestones_resolver.rb new file mode 100644 index 00000000000..8d34cea4fa1 --- /dev/null +++ b/app/graphql/resolvers/group_milestones_resolver.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Resolvers + class GroupMilestonesResolver < MilestonesResolver + argument :include_descendants, GraphQL::BOOLEAN_TYPE, + required: false, + description: 'Also return milestones in all subgroups and subprojects' + + private + + def parent_id_parameters(args) + return { group_ids: parent.id } unless args[:include_descendants].present? + + { + group_ids: parent.self_and_descendants.public_or_visible_to_user(current_user).select(:id), + project_ids: group_projects.with_issues_or_mrs_available_for_user(current_user) + } + end + + def group_projects + GroupProjectsFinder.new( + group: parent, + current_user: current_user, + options: { include_subgroups: true } + ).execute + end + end +end diff --git a/app/graphql/resolvers/issue_status_counts_resolver.rb b/app/graphql/resolvers/issue_status_counts_resolver.rb new file mode 100644 index 00000000000..466ca538467 --- /dev/null +++ b/app/graphql/resolvers/issue_status_counts_resolver.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Resolvers + class IssueStatusCountsResolver < BaseResolver + prepend IssueResolverFields + + type Types::IssueStatusCountsType, null: true + + def continue_issue_resolve(parent, finder, **args) + Gitlab::IssuablesCountForState.new(finder, parent) + end + end +end diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb index 9d0535a208f..e2874f6643c 100644 --- a/app/graphql/resolvers/issues_resolver.rb +++ b/app/graphql/resolvers/issues_resolver.rb @@ -2,49 +2,11 @@ module Resolvers class IssuesResolver < BaseResolver - argument :iid, GraphQL::STRING_TYPE, - required: false, - description: 'IID of the issue. For example, "1"' + prepend IssueResolverFields - argument :iids, [GraphQL::STRING_TYPE], - required: false, - description: 'List of IIDs of issues. For example, [1, 2]' argument :state, Types::IssuableStateEnum, required: false, description: 'Current state of this issue' - argument :label_name, GraphQL::STRING_TYPE.to_list_type, - required: false, - description: 'Labels applied to this issue' - argument :milestone_title, GraphQL::STRING_TYPE.to_list_type, - required: false, - description: 'Milestones applied to this issue' - argument :assignee_username, GraphQL::STRING_TYPE, - required: false, - description: 'Username of a user assigned to the issues' - argument :assignee_id, GraphQL::STRING_TYPE, - required: false, - description: 'ID of a user assigned to the issues, "none" and "any" values supported' - argument :created_before, Types::TimeType, - required: false, - description: 'Issues created before this date' - argument :created_after, Types::TimeType, - required: false, - description: 'Issues created after this date' - argument :updated_before, Types::TimeType, - required: false, - description: 'Issues updated before this date' - argument :updated_after, Types::TimeType, - required: false, - description: 'Issues updated after this date' - argument :closed_before, Types::TimeType, - required: false, - description: 'Issues closed before this date' - argument :closed_after, Types::TimeType, - required: false, - description: 'Issues closed after this date' - argument :search, GraphQL::STRING_TYPE, - required: false, - description: 'Search query for issue title or description' argument :sort, Types::IssueSortEnum, description: 'Sort issues by this criteria', required: false, @@ -56,19 +18,7 @@ module Resolvers label_priority_asc label_priority_desc milestone_due_asc milestone_due_desc].freeze - def resolve(**args) - # The project could have been loaded in batch by `BatchLoader`. - # At this point we need the `id` of the project to query for issues, so - # make sure it's loaded and not `nil` before continuing. - parent = object.respond_to?(:sync) ? object.sync : object - return Issue.none if parent.nil? - - # Will need to be be made group & namespace aware with - # https://gitlab.com/gitlab-org/gitlab-foss/issues/54520 - args[:iids] ||= [args.delete(:iid)].compact if args[:iid] - args[:attempt_project_search_optimizations] = true if args[:search].present? - - finder = IssuesFinder.new(current_user, args) + def continue_issue_resolve(parent, finder, **args) issues = Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).batching_find_all if non_stable_cursor_sort?(args[:sort]) @@ -80,13 +30,6 @@ module Resolvers end end - def self.resolver_complexity(args, child_complexity:) - complexity = super - complexity += 2 if args[:labelName] - - complexity - end - def non_stable_cursor_sort?(sort) NON_STABLE_CURSOR_SORTS.include?(sort) end diff --git a/app/graphql/resolvers/merge_requests_resolver.rb b/app/graphql/resolvers/merge_requests_resolver.rb index 3aa52341eec..d15a1ede6fe 100644 --- a/app/graphql/resolvers/merge_requests_resolver.rb +++ b/app/graphql/resolvers/merge_requests_resolver.rb @@ -28,6 +28,12 @@ module Resolvers required: false, as: :label_name, description: 'Array of label names. All resolved merge requests will have all of these labels.' + argument :merged_after, Types::TimeType, + required: false, + description: 'Merge requests merged after this date' + argument :merged_before, Types::TimeType, + required: false, + description: 'Merge requests merged before this date' def self.single ::Resolvers::MergeRequestResolver diff --git a/app/graphql/resolvers/milestone_resolver.rb b/app/graphql/resolvers/milestone_resolver.rb deleted file mode 100644 index bcfbc63c31f..00000000000 --- a/app/graphql/resolvers/milestone_resolver.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -module Resolvers - class MilestoneResolver < BaseResolver - include Gitlab::Graphql::Authorize::AuthorizeResource - include TimeFrameArguments - - argument :state, Types::MilestoneStateEnum, - required: false, - description: 'Filter milestones by state' - - argument :include_descendants, GraphQL::BOOLEAN_TYPE, - required: false, - description: 'Return also milestones in all subgroups and subprojects' - - type Types::MilestoneType, null: true - - def resolve(**args) - validate_timeframe_params!(args) - - authorize! - - MilestonesFinder.new(milestones_finder_params(args)).execute - end - - private - - def milestones_finder_params(args) - { - state: args[:state] || 'all', - start_date: args[:start_date], - end_date: args[:end_date] - }.merge(parent_id_parameter(args)) - end - - def parent - @parent ||= object.respond_to?(:sync) ? object.sync : object - end - - def parent_id_parameter(args) - if parent.is_a?(Group) - group_parameters(args) - elsif parent.is_a?(Project) - { project_ids: parent.id } - end - end - - # MilestonesFinder does not check for current_user permissions, - # so for now we need to keep it here. - def authorize! - Ability.allowed?(context[:current_user], :read_milestone, parent) || raise_resource_not_available_error! - end - - def group_parameters(args) - return { group_ids: parent.id } unless args[:include_descendants].present? - - { - group_ids: parent.self_and_descendants.public_or_visible_to_user(current_user).select(:id), - project_ids: group_projects.with_issues_or_mrs_available_for_user(current_user) - } - end - - def group_projects - GroupProjectsFinder.new( - group: parent, - current_user: current_user, - options: { include_subgroups: true } - ).execute - end - end -end diff --git a/app/graphql/resolvers/milestones_resolver.rb b/app/graphql/resolvers/milestones_resolver.rb new file mode 100644 index 00000000000..5f80506c01b --- /dev/null +++ b/app/graphql/resolvers/milestones_resolver.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Resolvers + class MilestonesResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + include TimeFrameArguments + + argument :ids, [GraphQL::ID_TYPE], + required: false, + description: 'Array of global milestone IDs, e.g., "gid://gitlab/Milestone/1"' + + argument :state, Types::MilestoneStateEnum, + required: false, + description: 'Filter milestones by state' + + type Types::MilestoneType, null: true + + def resolve(**args) + validate_timeframe_params!(args) + + authorize! + + MilestonesFinder.new(milestones_finder_params(args)).execute + end + + private + + def milestones_finder_params(args) + { + ids: parse_gids(args[:ids]), + state: args[:state] || 'all', + start_date: args[:start_date], + end_date: args[:end_date] + }.merge(parent_id_parameters(args)) + end + + def parent + synchronized_object + end + + def parent_id_parameters(args) + raise NotImplementedError + end + + # MilestonesFinder does not check for current_user permissions, + # so for now we need to keep it here. + def authorize! + Ability.allowed?(context[:current_user], :read_milestone, parent) || raise_resource_not_available_error! + end + + def parse_gids(gids) + gids&.map { |gid| GitlabSchema.parse_gid(gid, expected_type: Milestone).model_id } + end + end +end diff --git a/app/graphql/resolvers/project_milestones_resolver.rb b/app/graphql/resolvers/project_milestones_resolver.rb new file mode 100644 index 00000000000..976fc300b87 --- /dev/null +++ b/app/graphql/resolvers/project_milestones_resolver.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Resolvers + class ProjectMilestonesResolver < MilestonesResolver + argument :include_ancestors, GraphQL::BOOLEAN_TYPE, + required: false, + description: "Also return milestones in the project's parent group and its ancestors" + + private + + def parent_id_parameters(args) + return { project_ids: parent.id } unless args[:include_ancestors].present? && parent.group.present? + + { + group_ids: parent.group.self_and_ancestors.select(:id), + project_ids: parent.id + } + end + end +end diff --git a/app/graphql/resolvers/project_pipeline_resolver.rb b/app/graphql/resolvers/project_pipeline_resolver.rb index 5bafe3dd140..181c1e77109 100644 --- a/app/graphql/resolvers/project_pipeline_resolver.rb +++ b/app/graphql/resolvers/project_pipeline_resolver.rb @@ -10,7 +10,7 @@ module Resolvers def resolve(iid:) BatchLoader::GraphQL.for(iid).batch(key: project) do |iids, loader, args| - args[:key].ci_pipelines.for_iid(iids).each { |pl| loader.call(pl.iid.to_s, pl) } + args[:key].all_pipelines.for_iid(iids).each { |pl| loader.call(pl.iid.to_s, pl) } end end end diff --git a/app/graphql/resolvers/projects/jira_projects_resolver.rb b/app/graphql/resolvers/projects/jira_projects_resolver.rb index 2dc712128cc..ed382ac82d0 100644 --- a/app/graphql/resolvers/projects/jira_projects_resolver.rb +++ b/app/graphql/resolvers/projects/jira_projects_resolver.rb @@ -16,7 +16,14 @@ module Resolvers response = jira_projects(name: name) if response.success? - response.payload[:projects] + projects_array = response.payload[:projects] + + GraphQL::Pagination::ArrayConnection.new( + projects_array, + # override default max_page_size to whatever the size of the response is, + # see https://gitlab.com/gitlab-org/gitlab/-/issues/231394 + args.merge({ max_page_size: projects_array.size }) + ) else raise Gitlab::Graphql::Errors::BaseError, response.message end diff --git a/app/graphql/resolvers/todo_resolver.rb b/app/graphql/resolvers/todo_resolver.rb index cff65321dc0..bd5f8f274cd 100644 --- a/app/graphql/resolvers/todo_resolver.rb +++ b/app/graphql/resolvers/todo_resolver.rb @@ -4,7 +4,7 @@ module Resolvers class TodoResolver < BaseResolver type Types::TodoType, null: true - alias_method :user, :object + alias_method :target, :object argument :action, [Types::TodoActionEnum], required: false, @@ -31,9 +31,10 @@ module Resolvers description: 'The type of the todo' def resolve(**args) - return Todo.none if user != context[:current_user] + return Todo.none unless current_user.present? && target.present? + return Todo.none if target.is_a?(User) && target != current_user - TodosFinder.new(user, todo_finder_params(args)).execute + TodosFinder.new(current_user, todo_finder_params(args)).execute end private @@ -46,6 +47,15 @@ module Resolvers author_id: args[:author_id], action_id: args[:action], project_id: args[:project_id] + }.merge(target_params) + end + + def target_params + return {} unless TodosFinder::TODO_TYPES.include?(target.class.name) + + { + type: target.class.name, + target_id: target.id } end end |