Commit 9df28826 authored by Alex Kalderimis's avatar Alex Kalderimis

Merge branch '347220-graphql-connection-and-packages-finders-updates' into 'master'

GraphQL connection class and Packages finders updates

See merge request gitlab-org/gitlab!84387
parents a22bc367 cb751a9a
......@@ -22,11 +22,11 @@ module Packages
def packages_for_group_projects(installable_only: false)
packages = ::Packages::Package
.preload_pipelines
.including_project_route
.including_tags
.for_projects(group_projects_visible_to_current_user.select(:id))
.sort_by_attribute("#{params[:order_by]}_#{params[:sort]}")
packages = packages.preload_pipelines if preload_pipelines
packages = filter_with_version(packages)
packages = filter_by_package_type(packages)
......@@ -59,5 +59,9 @@ module Packages
def exclude_subgroups?
params[:exclude_subgroups]
end
def preload_pipelines
params.fetch(:preload_pipelines, true)
end
end
end
......@@ -14,9 +14,9 @@ module Packages
def execute
packages = project.packages
.preload_pipelines
.including_project_route
.including_tags
packages = packages.preload_pipelines if preload_pipelines
packages = filter_with_version(packages)
packages = filter_by_package_type(packages)
......@@ -32,5 +32,9 @@ module Packages
def order_packages(packages)
packages.sort_by_attribute("#{params[:order_by]}_#{params[:sort]}")
end
def preload_pipelines
params.fetch(:preload_pipelines, true)
end
end
end
# frozen_string_literal: true
# Connection for an array of Active Record instances.
# Resolvers needs to handle cursors (before and after).
# This connection will handle (first and last).
# Supports batch loaded items.
# Expects the array to use a fixed DESC order. This is similar to
# ExternallyPaginatedArrayConnection.
module Gitlab
module Graphql
module Pagination
class ActiveRecordArrayConnection < GraphQL::Pagination::ArrayConnection
include ::Gitlab::Graphql::ConnectionCollectionMethods
prepend ::Gitlab::Graphql::ConnectionRedaction
delegate :<<, to: :items
def nodes
load_nodes
@nodes
end
def next_page?
load_nodes
if before
true
elsif first
limit_value < items.size
else
false
end
end
def previous_page?
load_nodes
if after
true
elsif last
limit_value < items.size
else
false
end
end
# see https://graphql-ruby.org/pagination/custom_connections#connection-wrapper
alias_method :has_next_page, :next_page?
alias_method :has_previous_page, :previous_page?
def cursor_for(item)
# item could be a batch loaded item. Sync it to have the id.
cursor = { 'id' => Gitlab::Graphql::Lazy.force(item).id.to_s }
encode(cursor.to_json)
end
# Part of the implied interface for default objects for BatchLoader: objects must be clonable
def dup
self.class.new(
items.dup,
first: first,
after: after,
max_page_size: max_page_size,
last: last,
before: before
)
end
private
def limit_value
# note: only first _or_ last can be specified, not both
@limit_value ||= [first, last, max_page_size].compact.min
end
def load_nodes
@nodes ||= begin
limited_nodes = items
limited_nodes = limited_nodes.first(first) if first
limited_nodes = limited_nodes.last(last) if last
limited_nodes
end
end
end
end
end
end
......@@ -149,6 +149,22 @@ RSpec.describe Packages::GroupPackagesFinder do
it { is_expected.to match_array([package1, package2]) }
end
context 'preload_pipelines' do
it 'preloads pipelines by default' do
expect(Packages::Package).to receive(:preload_pipelines).and_call_original
expect(subject).to match_array([package1, package2])
end
context 'set to false' do
let(:params) { { preload_pipelines: false } }
it 'does not preload pipelines' do
expect(Packages::Package).not_to receive(:preload_pipelines)
expect(subject).to match_array([package1, package2])
end
end
end
context 'with package_name' do
let_it_be(:named_package) { create(:maven_package, project: project, name: 'maven') }
......
......@@ -81,6 +81,22 @@ RSpec.describe ::Packages::PackagesFinder do
it { is_expected.to match_array([conan_package, maven_package]) }
end
context 'preload_pipelines' do
it 'preloads pipelines by default' do
expect(Packages::Package).to receive(:preload_pipelines).and_call_original
expect(subject).to match_array([maven_package, conan_package])
end
context 'set to false' do
let(:params) { { preload_pipelines: false } }
it 'does not preload pipelines' do
expect(Packages::Package).not_to receive(:preload_pipelines)
expect(subject).to match_array([maven_package, conan_package])
end
end
end
it_behaves_like 'concerning versionless param'
it_behaves_like 'concerning package statuses'
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Graphql::Pagination::ActiveRecordArrayConnection do
using RSpec::Parameterized::TableSyntax
let_it_be(:items) { create_list(:package_build_info, 3) }
let_it_be(:context) do
GraphQL::Query::Context.new(
query: GraphQL::Query.new(GitlabSchema, document: nil, context: {}, variables: {}),
values: {},
object: nil
)
end
let(:first) { nil }
let(:last) { nil }
let(:after) { nil }
let(:before) { nil }
let(:max_page_size) { nil }
let(:connection) do
described_class.new(
items,
context: context,
first: first,
last: last,
after: after,
before: before,
max_page_size: max_page_size
)
end
it_behaves_like 'a connection with collection methods'
it_behaves_like 'a redactable connection' do
let(:unwanted) { items[1] }
end
describe '#nodes' do
subject { connection.nodes }
it { is_expected.to match_array(items) }
context 'with first set' do
let(:first) { 2 }
it { is_expected.to match_array([items[0], items[1]]) }
end
context 'with last set' do
let(:last) { 2 }
it { is_expected.to match_array([items[1], items[2]]) }
end
end
describe '#next_page?' do
subject { connection.next_page? }
where(:before, :first, :max_page_size, :result) do
nil | nil | nil | false
1 | nil | nil | true
nil | 1 | nil | true
nil | 10 | nil | false
nil | 1 | 1 | true
nil | 1 | 10 | true
nil | 10 | 10 | false
end
with_them do
it { is_expected.to eq(result) }
end
end
describe '#previous_page?' do
subject { connection.previous_page? }
where(:after, :last, :max_page_size, :result) do
nil | nil | nil | false
1 | nil | nil | true
nil | 1 | nil | true
nil | 10 | nil | false
nil | 1 | 1 | true
nil | 1 | 10 | true
nil | 10 | 10 | false
end
with_them do
it { is_expected.to eq(result) }
end
end
describe '#cursor_for' do
let(:item) { items[0] }
let(:expected_result) do
GitlabSchema.cursor_encoder.encode(
Gitlab::Json.dump(id: item.id.to_s),
nonce: true
)
end
subject { connection.cursor_for(item) }
it { is_expected.to eq(expected_result) }
context 'with a BatchLoader::GraphQL item' do
let_it_be(:user) { create(:user) }
let(:item) { ::Gitlab::Graphql::Loaders::BatchModelLoader.new(::User, user.id).find }
let(:expected_result) do
GitlabSchema.cursor_encoder.encode(
Gitlab::Json.dump(id: user.id.to_s),
nonce: true
)
end
it { is_expected.to eq(expected_result) }
end
end
describe '#dup' do
subject { connection.dup }
it 'properly handles items duplication' do
connection2 = subject
connection2 << create(:package_build_info)
expect(connection.items).not_to eq(connection2.items)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment