Commit 07208ef9 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by James Fargher

Use finder with parameters

- project / registry
- group / registry
- unit tests
Co-authored-by: default avatarNick Kipling <nkipling@gitlab.com>
Co-authored-by: default avatarSteve Abrams <sabrams@gitlab.com>
parent 87d63f10
...@@ -9,7 +9,9 @@ module Groups ...@@ -9,7 +9,9 @@ module Groups
respond_to do |format| respond_to do |format|
format.html format.html
format.json do format.json do
@images = ContainerRepositoriesFinder.new(user: current_user, subject: group).execute.with_api_entity_associations @images = ContainerRepositoriesFinder.new(user: current_user, subject: group, params: params.slice(:name))
.execute
.with_api_entity_associations
track_event(:list_repositories) track_event(:list_repositories)
......
...@@ -10,7 +10,8 @@ module Projects ...@@ -10,7 +10,8 @@ module Projects
respond_to do |format| respond_to do |format|
format.html format.html
format.json do format.json do
@images = project.container_repositories @images = ContainerRepositoriesFinder.new(user: current_user, subject: project, params: params.slice(:name))
.execute
track_event(:list_repositories) track_event(:list_repositories)
......
...@@ -3,17 +3,18 @@ ...@@ -3,17 +3,18 @@
class ContainerRepositoriesFinder class ContainerRepositoriesFinder
VALID_SUBJECTS = [Group, Project].freeze VALID_SUBJECTS = [Group, Project].freeze
def initialize(user:, subject:) def initialize(user:, subject:, params: {})
@user = user @user = user
@subject = subject @subject = subject
@params = params
end end
def execute def execute
raise ArgumentError, "invalid subject_type" unless valid_subject_type? raise ArgumentError, "invalid subject_type" unless valid_subject_type?
return unless authorized? return unless authorized?
return project_repositories if @subject.is_a?(Project) repositories = @subject.is_a?(Project) ? project_repositories : group_repositories
return group_repositories if @subject.is_a?(Group) filter_by_image_name(repositories)
end end
private private
...@@ -32,6 +33,12 @@ class ContainerRepositoriesFinder ...@@ -32,6 +33,12 @@ class ContainerRepositoriesFinder
ContainerRepository.for_group_and_its_subgroups(@subject) ContainerRepository.for_group_and_its_subgroups(@subject)
end end
def filter_by_image_name(repositories)
return repositories unless @params[:name]
repositories.search_by_name(@params[:name])
end
def authorized? def authorized?
Ability.allowed?(@user, :read_container_image, @subject) Ability.allowed?(@user, :read_container_image, @subject)
end end
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
class ContainerRepository < ApplicationRecord class ContainerRepository < ApplicationRecord
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
include Gitlab::SQL::Pattern
belongs_to :project belongs_to :project
...@@ -17,6 +18,7 @@ class ContainerRepository < ApplicationRecord ...@@ -17,6 +18,7 @@ class ContainerRepository < ApplicationRecord
scope :for_group_and_its_subgroups, ->(group) do scope :for_group_and_its_subgroups, ->(group) do
where(project_id: Project.for_group_and_its_subgroups(group).with_container_registry.select(:id)) where(project_id: Project.for_group_and_its_subgroups(group).with_container_registry.select(:id))
end end
scope :search_by_name, ->(query) { fuzzy_search(query, [:name], use_minimum_char_limit: false) }
def self.exists_by_path?(path) def self.exists_by_path?(path)
where( where(
......
---
title: Add search by name to registry image repositories
merge_request: 29763
author:
type: added
# frozen_string_literal: true
class AddIndexContainerRepositoryOnNameTrigramToContainerRepository < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_container_repository_on_name_trigram'
disable_ddl_transaction!
def up
add_concurrent_index :container_repositories, :name, name: INDEX_NAME, using: :gin, opclass: { name: :gin_trgm_ops }
end
def down
remove_concurrent_index_by_name(:container_repositories, INDEX_NAME)
end
end
...@@ -9223,6 +9223,8 @@ CREATE INDEX index_container_repositories_on_project_id ON public.container_repo ...@@ -9223,6 +9223,8 @@ CREATE INDEX index_container_repositories_on_project_id ON public.container_repo
CREATE UNIQUE INDEX index_container_repositories_on_project_id_and_name ON public.container_repositories USING btree (project_id, name); CREATE UNIQUE INDEX index_container_repositories_on_project_id_and_name ON public.container_repositories USING btree (project_id, name);
CREATE INDEX index_container_repository_on_name_trigram ON public.container_repositories USING gin (name public.gin_trgm_ops);
CREATE UNIQUE INDEX index_daily_report_results_unique_columns ON public.ci_daily_report_results USING btree (project_id, ref_path, param_type, date, title); CREATE UNIQUE INDEX index_daily_report_results_unique_columns ON public.ci_daily_report_results USING btree (project_id, ref_path, param_type, date, title);
CREATE INDEX index_dependency_proxy_blobs_on_group_id_and_file_name ON public.dependency_proxy_blobs USING btree (group_id, file_name); CREATE INDEX index_dependency_proxy_blobs_on_group_id_and_file_name ON public.dependency_proxy_blobs USING btree (group_id, file_name);
...@@ -13475,6 +13477,7 @@ COPY "schema_migrations" (version) FROM STDIN; ...@@ -13475,6 +13477,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200420172752 20200420172752
20200420172927 20200420172927
20200420201933 20200420201933
20200421092907
20200421233150 20200421233150
20200423075720 20200423075720
20200423080334 20200423080334
......
...@@ -6,12 +6,13 @@ describe Groups::Registry::RepositoriesController do ...@@ -6,12 +6,13 @@ describe Groups::Registry::RepositoriesController do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:guest) { create(:user) } let_it_be(:guest) { create(:user) }
let_it_be(:group, reload: true) { create(:group) } let_it_be(:group, reload: true) { create(:group) }
let(:additional_parameters) { {} }
subject do subject do
get :index, params: { get :index, params: additional_parameters.merge({
group_id: group, group_id: group,
format: format format: format
} })
end end
before do before do
...@@ -36,6 +37,25 @@ describe Groups::Registry::RepositoriesController do ...@@ -36,6 +37,25 @@ describe Groups::Registry::RepositoriesController do
end end
end end
shared_examples 'with name parameter' do
let_it_be(:project) { create(:project, group: test_group) }
let_it_be(:repo) { create(:container_repository, project: project, name: 'my_searched_image') }
let_it_be(:another_repo) { create(:container_repository, project: project, name: 'bar') }
let(:additional_parameters) { { name: 'my_searched_image' } }
it 'returns the searched repo' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.length).to eq 1
expect(json_response.first).to include(
'id' => repo.id,
'name' => repo.name
)
end
end
shared_examples 'renders correctly' do shared_examples 'renders correctly' do
context 'when user has access to registry' do context 'when user has access to registry' do
let_it_be(:test_group) { group } let_it_be(:test_group) { group }
...@@ -64,6 +84,8 @@ describe Groups::Registry::RepositoriesController do ...@@ -64,6 +84,8 @@ describe Groups::Registry::RepositoriesController do
it_behaves_like 'renders a list of repositories' it_behaves_like 'renders a list of repositories'
it_behaves_like 'with name parameter'
it_behaves_like 'a gitlab tracking event', described_class.name, 'list_repositories' it_behaves_like 'a gitlab tracking event', described_class.name, 'list_repositories'
context 'with project in subgroup' do context 'with project in subgroup' do
...@@ -71,6 +93,8 @@ describe Groups::Registry::RepositoriesController do ...@@ -71,6 +93,8 @@ describe Groups::Registry::RepositoriesController do
it_behaves_like 'renders a list of repositories' it_behaves_like 'renders a list of repositories'
it_behaves_like 'with name parameter'
context 'with project in subgroup and group' do context 'with project in subgroup and group' do
let_it_be(:repo_in_test_group) { create_project_with_repo(test_group) } let_it_be(:repo_in_test_group) { create_project_with_repo(test_group) }
let_it_be(:repo_in_group) { create_project_with_repo(group) } let_it_be(:repo_in_group) { create_project_with_repo(group) }
...@@ -81,6 +105,8 @@ describe Groups::Registry::RepositoriesController do ...@@ -81,6 +105,8 @@ describe Groups::Registry::RepositoriesController do
expect(json_response).to be_kind_of(Array) expect(json_response).to be_kind_of(Array)
expect(json_response.length).to eq 2 expect(json_response.length).to eq 2
end end
it_behaves_like 'with name parameter'
end end
end end
end end
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
require 'spec_helper' require 'spec_helper'
describe Projects::Registry::RepositoriesController do describe Projects::Registry::RepositoriesController do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project, :private) } let_it_be(:project) { create(:project, :private) }
before do before do
sign_in(user) sign_in(user)
...@@ -16,6 +16,22 @@ describe Projects::Registry::RepositoriesController do ...@@ -16,6 +16,22 @@ describe Projects::Registry::RepositoriesController do
project.add_developer(user) project.add_developer(user)
end end
shared_examples 'with name parameter' do
let_it_be(:repo) { create(:container_repository, project: project, name: 'my_searched_image') }
let_it_be(:another_repo) { create(:container_repository, project: project, name: 'bar') }
it 'returns the searched repo' do
go_to_index(format: :json, params: { name: 'my_searched_image' })
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.length).to eq 1
expect(json_response.first).to include(
'id' => repo.id,
'name' => repo.name
)
end
end
shared_examples 'renders a list of repositories' do shared_examples 'renders a list of repositories' do
context 'when root container repository exists' do context 'when root container repository exists' do
before do before do
...@@ -60,6 +76,8 @@ describe Projects::Registry::RepositoriesController do ...@@ -60,6 +76,8 @@ describe Projects::Registry::RepositoriesController do
expect(response).to match_response_schema('registry/repositories') expect(response).to match_response_schema('registry/repositories')
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
end end
it_behaves_like 'with name parameter'
end end
context 'when there are no tags for this repository' do context 'when there are no tags for this repository' do
...@@ -138,11 +156,11 @@ describe Projects::Registry::RepositoriesController do ...@@ -138,11 +156,11 @@ describe Projects::Registry::RepositoriesController do
end end
end end
def go_to_index(format: :html) def go_to_index(format: :html, params: {} )
get :index, params: { get :index, params: params.merge({
namespace_id: project.namespace, namespace_id: project.namespace,
project_id: project project_id: project
}, }),
format: format format: format
end end
......
...@@ -6,18 +6,35 @@ describe ContainerRepositoriesFinder do ...@@ -6,18 +6,35 @@ describe ContainerRepositoriesFinder do
let_it_be(:reporter) { create(:user) } let_it_be(:reporter) { create(:user) }
let_it_be(:guest) { create(:user) } let_it_be(:guest) { create(:user) }
let(:group) { create(:group) } let_it_be(:group) { create(:group) }
let(:project) { create(:project, group: group) } let_it_be(:project) { create(:project, group: group) }
let!(:project_repository) { create(:container_repository, project: project) } let_it_be(:project_repository) { create(:container_repository, name: 'my_image', project: project) }
let(:params) { {} }
before do before do
group.add_reporter(reporter) group.add_reporter(reporter)
project.add_reporter(reporter) project.add_reporter(reporter)
end end
shared_examples 'with name search' do
let_it_be(:not_searched_repository) do
create(:container_repository, name: 'foo_bar_baz', project: project)
end
%w[my_image my_imag _image _imag].each do |name|
context "with name set to #{name}" do
let(:params) { { name: name } }
it { is_expected.to contain_exactly(project_repository)}
it { is_expected.not_to include(not_searched_repository)}
end
end
end
describe '#execute' do describe '#execute' do
context 'with authorized user' do context 'with authorized user' do
subject { described_class.new(user: reporter, subject: subject_object).execute } subject { described_class.new(user: reporter, subject: subject_object, params: params).execute }
context 'when subject_type is group' do context 'when subject_type is group' do
let(:subject_object) { group } let(:subject_object) { group }
...@@ -28,12 +45,16 @@ describe ContainerRepositoriesFinder do ...@@ -28,12 +45,16 @@ describe ContainerRepositoriesFinder do
end end
it { is_expected.to match_array([project_repository, other_repository]) } it { is_expected.to match_array([project_repository, other_repository]) }
it_behaves_like 'with name search'
end end
context 'when subject_type is project' do context 'when subject_type is project' do
let(:subject_object) { project } let(:subject_object) { project }
it { is_expected.to match_array([project_repository]) } it { is_expected.to match_array([project_repository]) }
it_behaves_like 'with name search'
end end
context 'with invalid subject_type' do context 'with invalid subject_type' do
......
...@@ -309,4 +309,14 @@ describe ContainerRepository do ...@@ -309,4 +309,14 @@ describe ContainerRepository do
it { is_expected.to eq([]) } it { is_expected.to eq([]) }
end end
end end
describe '.search_by_name' do
let!(:another_repository) do
create(:container_repository, name: 'my_foo_bar', project: project)
end
subject { described_class.search_by_name('my_image') }
it { is_expected.to contain_exactly(repository) }
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