summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBob Van Landuyt <bob@vanlanduyt.co>2018-06-26 18:31:05 +0200
committerBob Van Landuyt <bob@vanlanduyt.co>2018-07-04 10:53:39 +0200
commit04b046587fe609cd889f3fa518f08299abc61267 (patch)
tree634fc7d5e2a416950868700a82c171ff4f54239b /lib
parentcd5789415b6e561564073693243e890e79596ed2 (diff)
downloadgitlab-ce-04b046587fe609cd889f3fa518f08299abc61267.tar.gz
Add pipeline lists to GraphQL
This adds Keyset pagination to GraphQL lists. PoC for that is pipelines on merge requests and projects. When paginating a list, the base-64 encoded id of the ordering field (in most cases the primary key) can be passed in the `before` or `after` GraphQL argument.
Diffstat (limited to 'lib')
-rw-r--r--lib/api/pipelines.rb2
-rw-r--r--lib/gitlab/graphql/connections.rb12
-rw-r--r--lib/gitlab/graphql/connections/keyset_connection.rb73
-rw-r--r--lib/gitlab/graphql/errors.rb8
-rw-r--r--lib/gitlab/graphql/present/instrumentation.rb2
5 files changed, 96 insertions, 1 deletions
diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb
index 8374a57edfa..5d33a13d035 100644
--- a/lib/api/pipelines.rb
+++ b/lib/api/pipelines.rb
@@ -31,7 +31,7 @@ module API
get ':id/pipelines' do
authorize! :read_pipeline, user_project
- pipelines = PipelinesFinder.new(user_project, params).execute
+ pipelines = PipelinesFinder.new(user_project, current_user, params).execute
present paginate(pipelines), with: Entities::PipelineBasic
end
diff --git a/lib/gitlab/graphql/connections.rb b/lib/gitlab/graphql/connections.rb
new file mode 100644
index 00000000000..2582ffeb2a8
--- /dev/null
+++ b/lib/gitlab/graphql/connections.rb
@@ -0,0 +1,12 @@
+module Gitlab
+ module Graphql
+ module Connections
+ def self.use(_schema)
+ GraphQL::Relay::BaseConnection.register_connection_implementation(
+ ActiveRecord::Relation,
+ Gitlab::Graphql::Connections::KeysetConnection
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/connections/keyset_connection.rb b/lib/gitlab/graphql/connections/keyset_connection.rb
new file mode 100644
index 00000000000..abee2afe144
--- /dev/null
+++ b/lib/gitlab/graphql/connections/keyset_connection.rb
@@ -0,0 +1,73 @@
+module Gitlab
+ module Graphql
+ module Connections
+ class KeysetConnection < GraphQL::Relay::BaseConnection
+ def cursor_from_node(node)
+ encode(node[order_field].to_s)
+ end
+
+ def sliced_nodes
+ @sliced_nodes ||=
+ begin
+ sliced = nodes
+
+ sliced = sliced.where(before_slice) if before.present?
+ sliced = sliced.where(after_slice) if after.present?
+
+ sliced
+ end
+ end
+
+ def paged_nodes
+ if first && last
+ raise Gitlab::Graphql::Errors::ArgumentError.new("Can only provide either `first` or `last`, not both")
+ end
+
+ if last
+ sliced_nodes.last(limit_value)
+ else
+ sliced_nodes.limit(limit_value)
+ end
+ end
+
+ private
+
+ def before_slice
+ if sort_direction == :asc
+ table[order_field].lt(decode(before))
+ else
+ table[order_field].gt(decode(before))
+ end
+ end
+
+ def after_slice
+ if sort_direction == :asc
+ table[order_field].gt(decode(after))
+ else
+ table[order_field].lt(decode(after))
+ end
+ end
+
+ def limit_value
+ @limit_value ||= [first, last, max_page_size].compact.min
+ end
+
+ def table
+ nodes.arel_table
+ end
+
+ def order_info
+ @order_info ||= nodes.order_values.first
+ end
+
+ def order_field
+ @order_field ||= order_info&.expr&.name || nodes.primary_key
+ end
+
+ def sort_direction
+ @order_direction ||= order_info&.direction || :desc
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/errors.rb b/lib/gitlab/graphql/errors.rb
new file mode 100644
index 00000000000..1d8e8307ab9
--- /dev/null
+++ b/lib/gitlab/graphql/errors.rb
@@ -0,0 +1,8 @@
+module Gitlab
+ module Graphql
+ module Errors
+ BaseError = Class.new(GraphQL::ExecutionError)
+ ArgumentError = Class.new(BaseError)
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/present/instrumentation.rb b/lib/gitlab/graphql/present/instrumentation.rb
index 6f2b26c9676..f87fd147b15 100644
--- a/lib/gitlab/graphql/present/instrumentation.rb
+++ b/lib/gitlab/graphql/present/instrumentation.rb
@@ -3,6 +3,8 @@ module Gitlab
module Present
class Instrumentation
def instrument(type, field)
+ return field unless field.metadata[:type_class]
+
presented_in = field.metadata[:type_class].owner
return field unless presented_in.respond_to?(:presenter_class)
return field unless presented_in.presenter_class