Skip to content
代码片段 群组 项目
未验证 提交 97bdf4ca 编辑于 作者: Alexandru Croitor's avatar Alexandru Croitor
浏览文件

Add support for sorting project and group members Graphql resolvers

Add sorting capabilities for project and group members
graphql resolvers. Sorting by user name, access level
and membership creation date.

Changelog: added
上级 bab3b208
No related branches found
No related tags found
无相关合并请求
...@@ -11,6 +11,10 @@ class MembersResolver < BaseResolver ...@@ -11,6 +11,10 @@ class MembersResolver < BaseResolver
required: false, required: false,
description: 'Search query.' description: 'Search query.'
argument :sort, ::Types::MemberSortEnum,
required: false,
description: 'sort query.'
def resolve_with_lookahead(**args) def resolve_with_lookahead(**args)
authorize!(object) authorize!(object)
......
# frozen_string_literal: true
module Types
class MemberSortEnum < SortEnum
graphql_name 'MemberSort'
description 'Values for sorting members'
value 'ACCESS_LEVEL_ASC', 'Access level ascending order.', value: :access_level_asc
value 'ACCESS_LEVEL_DESC', 'Access level descending order.', value: :access_level_desc
value 'USER_FULL_NAME_ASC', "User's full name ascending order.", value: :name_asc
value 'USER_FULL_NAME_DESC', "User's full name descending order.", value: :name_desc
end
end
...@@ -43,6 +43,33 @@ def simple_sorts ...@@ -43,6 +43,33 @@ def simple_sorts
} }
end end
def build_keyset_order_on_joined_column(scope:, attribute_name:, column:, direction:, nullable:)
reversed_direction = direction == :asc ? :desc : :asc
# rubocop: disable GitlabSecurity/PublicSend
order = ::Gitlab::Pagination::Keyset::Order.build(
[
::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: attribute_name,
column_expression: column,
order_expression: column.send(direction).send(nullable),
reversed_order_expression: column.send(reversed_direction).send(nullable),
order_direction: direction,
distinct: false,
add_to_projections: true,
nullable: nullable
),
::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: arel_table['id'].desc
)
]
)
# rubocop: enable GitlabSecurity/PublicSend
order.apply_cursor_conditions(scope).reorder(order)
end
private private
def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: []) def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
......
...@@ -254,32 +254,6 @@ class << self ...@@ -254,32 +254,6 @@ class << self
alias_method :with_state, :with_state_id alias_method :with_state, :with_state_id
alias_method :with_states, :with_state_ids alias_method :with_states, :with_state_ids
def build_keyset_order_on_joined_column(scope:, attribute_name:, column:, direction:, nullable:)
reversed_direction = direction == :asc ? :desc : :asc
# rubocop: disable GitlabSecurity/PublicSend
order = ::Gitlab::Pagination::Keyset::Order.build(
[
::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: attribute_name,
column_expression: column,
order_expression: column.send(direction).send(nullable),
reversed_order_expression: column.send(reversed_direction).send(nullable),
order_direction: direction,
distinct: false,
add_to_projections: true,
nullable: nullable
),
::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: arel_table['id'].desc
)
])
# rubocop: enable GitlabSecurity/PublicSend
order.apply_cursor_conditions(scope).order(order)
end
override :order_upvotes_desc override :order_upvotes_desc
def order_upvotes_desc def order_upvotes_desc
reorder(upvotes_count: :desc) reorder(upvotes_count: :desc)
......
...@@ -184,14 +184,85 @@ class Member < ApplicationRecord ...@@ -184,14 +184,85 @@ class Member < ApplicationRecord
unscoped.from(distinct_members, :members) unscoped.from(distinct_members, :members)
end end
scope :order_name_asc, -> { left_join_users.reorder(User.arel_table[:name].asc.nulls_last) } scope :order_name_asc, -> do
scope :order_name_desc, -> { left_join_users.reorder(User.arel_table[:name].desc.nulls_last) } build_keyset_order_on_joined_column(
scope :order_recent_sign_in, -> { left_join_users.reorder(User.arel_table[:last_sign_in_at].desc.nulls_last) } scope: left_join_users,
scope :order_oldest_sign_in, -> { left_join_users.reorder(User.arel_table[:last_sign_in_at].asc.nulls_last) } attribute_name: 'member_user_full_name',
scope :order_recent_last_activity, -> { left_join_users.reorder(User.arel_table[:last_activity_on].desc.nulls_last) } column: User.arel_table[:name],
scope :order_oldest_last_activity, -> { left_join_users.reorder(User.arel_table[:last_activity_on].asc.nulls_first) } direction: :asc,
scope :order_recent_created_user, -> { left_join_users.reorder(User.arel_table[:created_at].desc.nulls_last) } nullable: :nulls_last
scope :order_oldest_created_user, -> { left_join_users.reorder(User.arel_table[:created_at].asc.nulls_first) } )
end
scope :order_name_desc, -> do
build_keyset_order_on_joined_column(
scope: left_join_users,
attribute_name: 'member_user_full_name',
column: User.arel_table[:name],
direction: :desc,
nullable: :nulls_last
)
end
scope :order_oldest_sign_in, -> do
build_keyset_order_on_joined_column(
scope: left_join_users,
attribute_name: 'member_user_last_sign_in_at',
column: User.arel_table[:last_sign_in_at],
direction: :asc,
nullable: :nulls_last
)
end
scope :order_recent_sign_in, -> do
build_keyset_order_on_joined_column(
scope: left_join_users,
attribute_name: 'member_user_last_sign_in_at',
column: User.arel_table[:last_sign_in_at],
direction: :desc,
nullable: :nulls_last
)
end
scope :order_oldest_last_activity, -> do
build_keyset_order_on_joined_column(
scope: left_join_users,
attribute_name: 'member_user_last_activity_on',
column: User.arel_table[:last_activity_on],
direction: :asc,
nullable: :nulls_first
)
end
scope :order_recent_last_activity, -> do
build_keyset_order_on_joined_column(
scope: left_join_users,
attribute_name: 'member_user_last_activity_on',
column: User.arel_table[:last_activity_on],
direction: :desc,
nullable: :nulls_last
)
end
scope :order_oldest_created_user, -> do
build_keyset_order_on_joined_column(
scope: left_join_users,
attribute_name: 'member_user_created_at',
column: User.arel_table[:created_at],
direction: :asc,
nullable: :nulls_first
)
end
scope :order_recent_created_user, -> do
build_keyset_order_on_joined_column(
scope: left_join_users,
attribute_name: 'member_user_created_at',
column: User.arel_table[:created_at],
direction: :desc,
nullable: :nulls_last
)
end
scope :on_project_and_ancestors, ->(project) { where(source: [project] + project.ancestors) } scope :on_project_and_ancestors, ->(project) { where(source: [project] + project.ancestors) }
......
...@@ -12679,6 +12679,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -12679,6 +12679,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupgroupmembersaccesslevels"></a>`accessLevels` | [`[AccessLevelEnum!]`](#accesslevelenum) | Filter members by the given access levels. | | <a id="groupgroupmembersaccesslevels"></a>`accessLevels` | [`[AccessLevelEnum!]`](#accesslevelenum) | Filter members by the given access levels. |
| <a id="groupgroupmembersrelations"></a>`relations` | [`[GroupMemberRelation!]`](#groupmemberrelation) | Filter members by the given member relations. | | <a id="groupgroupmembersrelations"></a>`relations` | [`[GroupMemberRelation!]`](#groupmemberrelation) | Filter members by the given member relations. |
| <a id="groupgroupmemberssearch"></a>`search` | [`String`](#string) | Search query. | | <a id="groupgroupmemberssearch"></a>`search` | [`String`](#string) | Search query. |
| <a id="groupgroupmemberssort"></a>`sort` | [`MemberSort`](#membersort) | sort query. |
   
##### `Group.issues` ##### `Group.issues`
   
...@@ -16594,6 +16595,7 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -16594,6 +16595,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="projectprojectmembersrelations"></a>`relations` | [`[ProjectMemberRelation!]`](#projectmemberrelation) | Filter members by the given member relations. | | <a id="projectprojectmembersrelations"></a>`relations` | [`[ProjectMemberRelation!]`](#projectmemberrelation) | Filter members by the given member relations. |
| <a id="projectprojectmemberssearch"></a>`search` | [`String`](#string) | Search query. | | <a id="projectprojectmemberssearch"></a>`search` | [`String`](#string) | Search query. |
| <a id="projectprojectmemberssort"></a>`sort` | [`MemberSort`](#membersort) | sort query. |
   
##### `Project.release` ##### `Project.release`
   
...@@ -20315,6 +20317,25 @@ Possible identifier types for a measurement. ...@@ -20315,6 +20317,25 @@ Possible identifier types for a measurement.
| <a id="measurementidentifierprojects"></a>`PROJECTS` | Project count. | | <a id="measurementidentifierprojects"></a>`PROJECTS` | Project count. |
| <a id="measurementidentifierusers"></a>`USERS` | User count. | | <a id="measurementidentifierusers"></a>`USERS` | User count. |
   
### `MemberSort`
Values for sorting members.
| Value | Description |
| ----- | ----------- |
| <a id="membersortaccess_level_asc"></a>`ACCESS_LEVEL_ASC` | Access level ascending order. |
| <a id="membersortaccess_level_desc"></a>`ACCESS_LEVEL_DESC` | Access level descending order. |
| <a id="membersortcreated_asc"></a>`CREATED_ASC` | Created at ascending order. |
| <a id="membersortcreated_desc"></a>`CREATED_DESC` | Created at descending order. |
| <a id="membersortupdated_asc"></a>`UPDATED_ASC` | Updated at ascending order. |
| <a id="membersortupdated_desc"></a>`UPDATED_DESC` | Updated at descending order. |
| <a id="membersortuser_full_name_asc"></a>`USER_FULL_NAME_ASC` | User's full name ascending order. |
| <a id="membersortuser_full_name_desc"></a>`USER_FULL_NAME_DESC` | User's full name descending order. |
| <a id="membersortcreated_asc"></a>`created_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `CREATED_ASC`. |
| <a id="membersortcreated_desc"></a>`created_desc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `CREATED_DESC`. |
| <a id="membersortupdated_asc"></a>`updated_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_ASC`. |
| <a id="membersortupdated_desc"></a>`updated_desc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_DESC`. |
### `MergeRequestNewState` ### `MergeRequestNewState`
   
New state to apply to a merge request. New state to apply to a merge request.
...@@ -2,9 +2,11 @@ ...@@ -2,9 +2,11 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Resolvers::GroupMembersResolver do RSpec.describe 'Resolvers::GroupMembersResolver' do
include GraphqlHelpers include GraphqlHelpers
let(:described_class) { Resolvers::GroupMembersResolver }
specify do specify do
expect(described_class).to have_nullable_graphql_type(Types::GroupMemberType.connection_type) expect(described_class).to have_nullable_graphql_type(Types::GroupMemberType.connection_type)
end end
......
...@@ -2,9 +2,11 @@ ...@@ -2,9 +2,11 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Resolvers::ProjectMembersResolver do RSpec.describe 'Resolvers::ProjectMembersResolver' do
include GraphqlHelpers include GraphqlHelpers
let(:described_class) { Resolvers::ProjectMembersResolver }
it_behaves_like 'querying members with a group' do it_behaves_like 'querying members with a group' do
let_it_be(:project) { create(:project, group: group_1) } let_it_be(:project) { create(:project, group: group_1) }
let_it_be(:resource_member) { create(:project_member, user: user_1, project: project) } let_it_be(:resource_member) { create(:project_member, user: user_1, project: project) }
......
...@@ -52,6 +52,15 @@ ...@@ -52,6 +52,15 @@
expect(subject).to contain_exactly(resource_member, group_1_member, root_group_member) expect(subject).to contain_exactly(resource_member, group_1_member, root_group_member)
end end
context 'with sort options' do
let(:args) { { sort: 'name_asc' } }
it 'searches users by user name' do
# the order is important here
expect(subject.items).to eq([root_group_member, resource_member, group_1_member])
end
end
context 'with search' do context 'with search' do
context 'when the search term matches a user' do context 'when the search term matches a user' do
let(:args) { { search: 'test' } } let(:args) { { search: 'test' } }
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册