diff options
author | Bob Van Landuyt <bob@vanlanduyt.co> | 2018-06-26 18:31:05 +0200 |
---|---|---|
committer | Bob Van Landuyt <bob@vanlanduyt.co> | 2018-07-04 10:53:39 +0200 |
commit | 04b046587fe609cd889f3fa518f08299abc61267 (patch) | |
tree | 634fc7d5e2a416950868700a82c171ff4f54239b /lib | |
parent | cd5789415b6e561564073693243e890e79596ed2 (diff) | |
download | gitlab-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.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/graphql/connections.rb | 12 | ||||
-rw-r--r-- | lib/gitlab/graphql/connections/keyset_connection.rb | 73 | ||||
-rw-r--r-- | lib/gitlab/graphql/errors.rb | 8 | ||||
-rw-r--r-- | lib/gitlab/graphql/present/instrumentation.rb | 2 |
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 |