Update projects finder to filter topics with user's organization

What does this MR do and why?

Update projects finder to filter topics from user's organization.

This is part of the efforts of adding topics sharding key. Related MRs:

Step MR
1 Add sharding key tracking issues for groups_and_projects
2 Add organization id to topics
3 Change app and api to support organization id on topics
4 Uses organization id on Topics GraphQL
5 Update Projects Finder when finding by Topics - this mr
6 Copy project topics to organization - TBD
7 Remove the organization_id column default - TBD

Note: The project's resolver still shows the projects that belong to all organizations, as there's no filtering on the projects. This MR only aims to filter using the correct topics that the user has access.

How to test

1. Setup

Click to expand
  1. Enable Organizations FF
Feature.enable(:ui_for_organizations)
  1. Let's create and assign some topics to projects
organization = Organizations::Organization.default_organization

organization.projects.each do |project|
  project.topic_list = 'Public Topic, ruby, postgres'
  project.save!
end

# Make sure you have three topics: 'Public Topic, ruby, postgres'
organization.reload.topics
  1. Create a new private organization in the console and add some topics:
organization = Organizations::Organization.create!(name: 'Private Organization', path: 'private-org', visibility_level: 0)

Projects::Topic.create!(name: 'private-org-topic', title: 'Private Org Topic', slug: 'private-org-topic', organization: organization)
Projects::Topic.create!(name: 'ruby-private-org', title: 'Ruby (private org)', slug: 'ruby-private-org', organization: organization)
Projects::Topic.create!(name: 'postgres-private-org', title: 'Postgres (private org)', slug: 'postgres-private-org', organization: organization)
  1. Navigate to the organization page
http://gdk.test:3000/-/organizations/private-org
  1. Create a new group for the private organization. Name it my-private-org-group
http://gdk.test:3000/-/organizations/private-org/groups/new
  1. Create a new project. Select the group you created above (my-private-org-group) and name it project-from-private-org.
http://gdk.test:3000/projects/new#blank_project
  • Go to the project settings page and assign the topics from private org to the project:
http://gdk.test:3000/my-private-org-group/project-from-private-org/edit

topics-img-1

  1. Create a user called private-org-user. Under organization section, select the Private Organization for the user.
http://gdk.test:3000/admin/users/new
  1. Navigate to my-private-org-group page and invite the user you created above. Give a maintainer role to the user.
http://gdk.test:3000/groups/my-private-org-group/-/group_members
  1. Check the organization page. You must see 1 group, 1 Project and 2 Users (Admin and the newly created one)
http://gdk.test:3000/-/organizations/private-org

2. Testing Explore topics

As we can't switch organizations in the dashboard, topics from http://gdk.test:3000/explore/projects/topics will use the default organization.

Click to expand
  1. Go to http://gdk.test:3000/explore/projects/topics
  2. Check that no topics from the private-org are shown. The search only shows topics from the default org.

3. Testing the GraphQL

As Admin
Click to expand
Note: Make sure you're logged in as Administrator
  1. Test the query using http://gdk.test:3000/-/graphql-explorer. As admin, you'll be able to filter the project by private organization topic
{
    projects(topics: "private-org-topic") {
        nodes {
            id
            name
            topics
        }
    }
}
{
  "data": {
    "projects": {
      "nodes": [
        {
          "id": "gid://gitlab/Project/19",
          "name": "project-from-private-org",
          "topics": [
            "private-org-topic",
            "ruby-private-org",
            "postgres-private-org"
          ]
        }
      ]
    }
  },
  "correlationId": "01JNH4MZQANWMHNWJYPHZ5DPHW"
}
As member of the private organization (impersonating private org user)
Click to expand
  1. Navigate to http://gdk.test:3000/admin/users/private-org-user and impersonate the user.

  2. Test the query using http://gdk.test:3000/-/graphql-explorer. As the user belongs to the private org only, you'll be able to filter projects using only topics from the private org:

{
  projects(topics: "private-org-topic") {
    nodes {
      id
      name
      topics
    }
  }
}
{
  "data": {
    "projects": {
      "nodes": [
        {
          "id": "gid://gitlab/Project/20",
          "name": "Project From Private Org",
          "topics": [
            "private-org-topic",
            "ruby-private-org",
            "postgres-private-org"
          ]
        }
      ]
    }
  },
  "correlationId": "01JNH8HF4JQSVFCWZWZWXQ1AM2"
}
As a member of the public organization only
Click to expand
  1. Stop impersonating the private-org user and navigate to http://gdk.test:3000/admin/users. Impersonate any member who doesn't belong to private-org and that's not an admin.

  2. Test the query using http://gdk.test:3000/-/graphql-explorer. As this is not a member of the private org, you'll not be able to query projects using private-org topics:

{
  projects(topics: "private-org-topic") {
    nodes {
      id
      name
      topics
    }
  }
}
{
  "data": {
    "projects": {
      "nodes": []
    }
  },
  "correlationId": "01JNH9JQ0FX9NGJMTN1EXZGXFB"
}
Querying without an unauthenticated user
Click to expand
  1. In a new private browser window, go to http://gdk.test:3000/-/graphql-explorer

  2. Try to query private-org topics:

{
  projects(topics: "private-org-topic") {
    nodes {
      id
      name
      topics
    }
  }
}
{
  "data": {
    "projects": {
      "nodes": []
    }
  },
  "correlationId": "01JNH9QBC4CRVRJ3AWFTQFK7V4"
}
  1. Only topics from public org can be used to search projects:
{
  projects(topics: "Public Topic", first: 2) {
    nodes {
      id
      name
      topics
    }
  }
}
{
  "data": {
    "projects": {
      "nodes": [
        {
          "id": "gid://gitlab/Project/18",
          "name": "Wget2",
          "topics": [
            "Public Topic",
            "ruby",
            "postgres"
          ]
        },
        {
          "id": "gid://gitlab/Project/17",
          "name": "Wget2",
          "topics": [
            "Public Topic",
            "ruby",
            "postgres"
          ]
        }
      ]
    }
  },
  "correlationId": "01JNH9V4E8XGQ1MKSCD900EJWW"
}

4. Testing the API endpoint

As member of the private organization only
Click to expand
  1. Navigate to http://gdk.test:3000/admin/users/private-org-user and impersonate the user.
  2. Create a new PAT for this user
curl "http://gdk.test:3000/api/v4/projects?topic=private-org-topic" --header "Authorization: Bearer glpat-..." --header "Content-Type: application/json"
[
  {
    "id": 20,
    "description": "",
    "name": "Project From Private Org",
    "name_with_namespace": "My Private Org Group / Project From Private Org",
    "path": "project-from-private-org",
    ...
    "topics": [
      "postgres-private-org",
      "private-org-topic",
      "ruby-private-org"
    ],
    "ssh_url_to_repo": "ssh://git@gdk.test:2222/my-private-org-group/project-from-private-org.git",
    ...
  }
]
Querying without an unauthenticated user
Click to expand
  1. Private organization topic can't be used to search for projects
curl --request GET "http://gdk.test:3000/api/v4/projects?topic=private-org-topic"
[]%

Related to #463254 (closed)

Merge request reports

Loading