diff --git a/app/graphql/resolvers/projects/user_contributed_projects_resolver.rb b/app/graphql/resolvers/projects/user_contributed_projects_resolver.rb index 73f8b72ed04707837ceb3fb8b571a2164971d009..712c01411636de4bb4eeeddf59061b6ae3d7ca93 100644 --- a/app/graphql/resolvers/projects/user_contributed_projects_resolver.rb +++ b/app/graphql/resolvers/projects/user_contributed_projects_resolver.rb @@ -14,17 +14,26 @@ class UserContributedProjectsResolver < BaseResolver required: false, description: 'Return only projects where current user has at least the specified access level.' + argument :include_personal, GraphQL::Types::Boolean, + description: 'Include personal projects.', + required: false, + default_value: false + alias_method :user, :object def resolve(**args) - ContributedProjectsFinder.new( + contributed_projects = ContributedProjectsFinder.new( user: user, current_user: current_user, params: { order_by: args[:sort], min_access_level: args[:min_access_level] } - ).execute.joined(user) + ).execute + + return contributed_projects if args[:include_personal] + + contributed_projects.joined(user) end end end diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 46c27f00ef93a36d336f749e970cb07c629a32e5..e8ba4a043cdbc64f69f38f67b5704d7c57f86a25 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -17369,6 +17369,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="addonusercontributedprojectsincludepersonal"></a>`includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | <a id="addonusercontributedprojectsminaccesslevel"></a>`minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | <a id="addonusercontributedprojectssort"></a>`sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -18257,6 +18258,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="autocompletedusercontributedprojectsincludepersonal"></a>`includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | <a id="autocompletedusercontributedprojectsminaccesslevel"></a>`minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | <a id="autocompletedusercontributedprojectssort"></a>`sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -20649,6 +20651,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="currentusercontributedprojectsincludepersonal"></a>`includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | <a id="currentusercontributedprojectsminaccesslevel"></a>`minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | <a id="currentusercontributedprojectssort"></a>`sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -26221,6 +26224,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="mergerequestassigneecontributedprojectsincludepersonal"></a>`includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | <a id="mergerequestassigneecontributedprojectsminaccesslevel"></a>`minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | <a id="mergerequestassigneecontributedprojectssort"></a>`sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -26611,6 +26615,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="mergerequestauthorcontributedprojectsincludepersonal"></a>`includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | <a id="mergerequestauthorcontributedprojectsminaccesslevel"></a>`minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | <a id="mergerequestauthorcontributedprojectssort"></a>`sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -27047,6 +27052,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="mergerequestparticipantcontributedprojectsincludepersonal"></a>`includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | <a id="mergerequestparticipantcontributedprojectsminaccesslevel"></a>`minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | <a id="mergerequestparticipantcontributedprojectssort"></a>`sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -27456,6 +27462,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="mergerequestreviewercontributedprojectsincludepersonal"></a>`includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | <a id="mergerequestreviewercontributedprojectsminaccesslevel"></a>`minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | <a id="mergerequestreviewercontributedprojectssort"></a>`sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -33950,6 +33957,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="usercorecontributedprojectsincludepersonal"></a>`includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | <a id="usercorecontributedprojectsminaccesslevel"></a>`minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | <a id="usercorecontributedprojectssort"></a>`sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | @@ -41101,6 +41109,7 @@ four standard [pagination arguments](#pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | +| <a id="usercontributedprojectsincludepersonal"></a>`includePersonal` | [`Boolean`](#boolean) | Include personal projects. | | <a id="usercontributedprojectsminaccesslevel"></a>`minAccessLevel` | [`AccessLevelEnum`](#accesslevelenum) | Return only projects where current user has at least the specified access level. | | <a id="usercontributedprojectssort"></a>`sort` | [`ProjectSort`](#projectsort) | Sort contributed projects. | diff --git a/spec/requests/api/graphql/user/contributed_projects_query_spec.rb b/spec/requests/api/graphql/user/contributed_projects_query_spec.rb index 1fe088e1403355491ee51a6be33a020c7982bd93..64b7a24dea2b8999ab4863bf8516ad32e21c4952 100644 --- a/spec/requests/api/graphql/user/contributed_projects_query_spec.rb +++ b/spec/requests/api/graphql/user/contributed_projects_query_spec.rb @@ -9,22 +9,25 @@ let(:user_params) { { username: user.username } } let(:user_fields) { 'contributedProjects { nodes { id } }' } - let_it_be(:user) { create(:user) } + let_it_be(:user) { create(:user, :with_namespace) } let_it_be(:current_user) { create(:user) } let_it_be(:public_project) { create(:project, :public) } let_it_be(:private_project) { create(:project, :private) } let_it_be(:internal_project) { create(:project, :internal) } + let_it_be(:personal_project) { create(:project, namespace: user.namespace) } let(:path) { %i[user contributed_projects nodes] } before_all do private_project.add_developer(user) private_project.add_developer(current_user) + personal_project.add_developer(current_user) travel_to(4.hours.from_now) { create(:push_event, project: private_project, author: user) } travel_to(3.hours.from_now) { create(:push_event, project: internal_project, author: user) } travel_to(2.hours.from_now) { create(:push_event, project: public_project, author: user) } + travel_to(2.hours.from_now) { create(:push_event, project: personal_project, author: user) } end it_behaves_like 'a working graphql query' do @@ -410,6 +413,37 @@ end end + context 'when include_personal argument is false' do + it 'does not include personal projects' do + post_graphql(query, current_user: current_user) + + expect(graphql_data_at(*path)) + .to contain_exactly( + a_graphql_entity_for(private_project), + a_graphql_entity_for(internal_project), + a_graphql_entity_for(public_project) + ) + end + end + + context 'when include_personal argument is true' do + let(:query_with_include_personal) do + graphql_query_for(:user, user_params, 'contributedProjects(includePersonal: true) { nodes { id } }') + end + + it 'includes personal projects' do + post_graphql(query_with_include_personal, current_user: current_user) + + expect(graphql_data_at(*path)) + .to contain_exactly( + a_graphql_entity_for(private_project), + a_graphql_entity_for(internal_project), + a_graphql_entity_for(public_project), + a_graphql_entity_for(personal_project) + ) + end + end + describe 'sorting and pagination' do let(:data_path) { [:user, :contributed_projects] }