Commit 1be3d5e6 authored by Pavel Kuznetsov's avatar Pavel Kuznetsov

Expose a list of projects starred by the user to GraphQL API

Previously it was possible to get projects starred by user with
REST API, but it was impossible with GraphQL API.
Now starredProjects field was added to User GraphQL type.
parent 81ff89a6
# frozen_string_literal: true
module Resolvers
class UserStarredProjectsResolver < BaseResolver
type Types::ProjectType, null: true
argument :search, GraphQL::STRING_TYPE,
required: false,
description: 'Search query'
alias_method :user, :object
def resolve(**args)
StarredProjectsFinder.new(user, params: args, current_user: current_user).execute
end
end
end
...@@ -37,6 +37,9 @@ module Types ...@@ -37,6 +37,9 @@ module Types
field :project_memberships, Types::ProjectMemberType.connection_type, null: true, field :project_memberships, Types::ProjectMemberType.connection_type, null: true,
description: 'Project memberships of the user', description: 'Project memberships of the user',
method: :project_members method: :project_members
field :starred_projects, Types::ProjectType.connection_type, null: true,
description: 'Projects starred by the user',
resolver: Resolvers::UserStarredProjectsResolver
# Merge request field: MRs can be either authored or assigned: # Merge request field: MRs can be either authored or assigned:
field :authored_merge_requests, Types::MergeRequestType.connection_type, null: true, field :authored_merge_requests, Types::MergeRequestType.connection_type, null: true,
......
---
title: Expose a list of projects starred by the user to GraphQL API
author: Pavel Kuznetsov
merge_request: 41076
type: added
...@@ -17557,6 +17557,36 @@ type User { ...@@ -17557,6 +17557,36 @@ type User {
visibility: VisibilityScopesEnum visibility: VisibilityScopesEnum
): SnippetConnection ): SnippetConnection
"""
Projects starred by the user
"""
starredProjects(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
"""
Search query
"""
search: String
): ProjectConnection
""" """
State of the user State of the user
""" """
......
...@@ -51381,6 +51381,69 @@ ...@@ -51381,6 +51381,69 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "starredProjects",
"description": "Projects starred by the user",
"args": [
{
"name": "search",
"description": "Search query",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "ProjectConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "state", "name": "state",
"description": "State of the user", "description": "State of the user",
...@@ -25,6 +25,7 @@ RSpec.describe GitlabSchema.types['User'] do ...@@ -25,6 +25,7 @@ RSpec.describe GitlabSchema.types['User'] do
assignedMergeRequests assignedMergeRequests
groupMemberships groupMemberships
projectMemberships projectMemberships
starredProjects
] ]
expect(described_class).to have_graphql_fields(*expected_fields) expect(described_class).to have_graphql_fields(*expected_fields)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Getting starredProjects of the user' do
include GraphqlHelpers
let(:query) do
graphql_query_for(:user, user_params, user_fields)
end
let(:user_params) { { username: user.username } }
let_it_be(:project_a) { create(:project, :public) }
let_it_be(:project_b) { create(:project, :private) }
let_it_be(:project_c) { create(:project, :private) }
let_it_be(:user, reload: true) { create(:user) }
let(:user_fields) { 'starredProjects { nodes { id } }' }
let(:starred_projects) { graphql_data_at(:user, :starred_projects, :nodes) }
before do
project_b.add_reporter(user)
project_c.add_reporter(user)
user.toggle_star(project_a)
user.toggle_star(project_b)
user.toggle_star(project_c)
post_graphql(query)
end
it_behaves_like 'a working graphql query'
it 'found only public project' do
expect(starred_projects).to contain_exactly(
a_hash_including('id' => global_id_of(project_a))
)
end
context 'the current user is the user' do
let(:current_user) { user }
before do
post_graphql(query, current_user: current_user)
end
it 'found all projects' do
expect(starred_projects).to contain_exactly(
a_hash_including('id' => global_id_of(project_a)),
a_hash_including('id' => global_id_of(project_b)),
a_hash_including('id' => global_id_of(project_c))
)
end
end
context 'the current user is a member of a private project the user starred' do
let_it_be(:other_user) { create(:user) }
before do
project_b.add_reporter(other_user)
post_graphql(query, current_user: other_user)
end
it 'finds public and member projects' do
expect(starred_projects).to contain_exactly(
a_hash_including('id' => global_id_of(project_a)),
a_hash_including('id' => global_id_of(project_b))
)
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