FEATURE: Add tag_group option in `/filter` (#27427)

* FEATURE: Add tag_group option in `/filter`

* DEV: Update tag_group_filter in `/filter` to use SQL

* DEV: Add guardian to `/filter` by tag_group

* DEV: remove unused code

* DEV: Update tag_group filter implementation

* DEV: Add tests to tag_group filter
This commit is contained in:
Gabriel Grubba 2024-06-13 14:03:49 -03:00 committed by GitHub
parent d7ece61252
commit 93c2ae585c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 107 additions and 0 deletions

View File

@ -74,6 +74,8 @@ class TopicsFilter
filter_by_number_of_posters(max: filter_values)
when "status"
filter_values.each { |status| @scope = filter_status(status: status) }
when "tag_group"
filter_tag_groups(values: key_prefixes.zip(filter_values))
when "tag"
filter_tags(values: key_prefixes.zip(filter_values))
when "views-min"
@ -368,6 +370,27 @@ class TopicsFilter
all_tag_ids
end
def filter_tag_groups(values:)
values.each do |key_prefix, tag_groups|
tag_group_ids = TagGroup.visible(@guardian).where(name: tag_groups).pluck(:id)
exclude_clause = "NOT" if key_prefix == "-"
filter =
"tags.id #{exclude_clause} IN (SELECT tag_id FROM tag_group_memberships WHERE tag_group_id IN (?))"
query =
if exclude_clause.present?
@scope
.joins("LEFT JOIN topic_tags ON topic_tags.topic_id = topics.id")
.joins("LEFT JOIN tags ON tags.id = topic_tags.tag_id")
.where("tags.id IS NULL OR #{filter}", tag_group_ids)
else
@scope.joins(:tags).where(filter, tag_group_ids)
end
@scope = query.distinct(:id)
end
end
def filter_tags(values:)
return if !SiteSetting.tagging_enabled?

View File

@ -929,6 +929,72 @@ RSpec.describe TopicsFilter do
end
end
describe "when filtering by tag_groups" do
fab!(:tag) { Fabricate(:tag, name: "tag1") }
fab!(:tag2) { Fabricate(:tag, name: "tag2") }
fab!(:tag3) { Fabricate(:tag, name: "tag3") }
fab!(:topic_without_tag) { Fabricate(:topic) }
fab!(:topic_with_tag) { Fabricate(:topic, tags: [tag]) }
fab!(:topic_with_tag_and_tag2) { Fabricate(:topic, tags: [tag, tag2]) }
fab!(:topic_with_tag2) { Fabricate(:topic, tags: [tag2]) }
fab!(:tag_group) { Fabricate(:tag_group, tag_names: [tag.name, tag2.name]) }
fab!(:topic_with_tag3) { Fabricate(:topic, tags: [tag3]) }
fab!(:staff_only_tag) { Fabricate(:tag, name: "group-only-tag") }
fab!(:group)
let!(:staff_tag_group) do
Fabricate(
:tag_group,
permissions: {
group.name => TagGroupPermission.permission_types[:full],
},
name: "staff-only-tag-group",
tag_names: [staff_only_tag.name],
)
end
fab!(:topic_with_staff_only_tag) { Fabricate(:topic, tags: [staff_only_tag]) }
it "should only return topics that are tagged with any of the specified tag_group when query string is tag_group:tag_group_name" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("tag_group:#{tag_group.name}")
.pluck(:id),
).to contain_exactly(topic_with_tag.id, topic_with_tag_and_tag2.id, topic_with_tag2.id)
end
it "should only return topics that are not excluded by the specified tag_group when query string is -tag_group:tag_group_name" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("-tag_group:#{tag_group.name}")
.pluck(:id),
).to contain_exactly(topic_with_tag3.id, topic_without_tag.id, topic_with_staff_only_tag.id)
end
it "should return the right topics when query string is `tag_group:staff_tag_group` and user has access to specified tag" do
group.add(admin)
expect(
TopicsFilter
.new(guardian: Guardian.new(admin))
.filter_from_query_string("tag_group:#{staff_tag_group.name}")
.pluck(:id),
).to contain_exactly(topic_with_staff_only_tag.id)
end
it "should not return any topics when query string is `tag_group:staff_tag_group` because specified tag is hidden to user" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("tag_group:#{staff_tag_group.name}")
.pluck(:id),
).to eq([])
end
end
describe "when filtering by topic author" do
fab!(:user2) { Fabricate(:user, username: "username2") }
fab!(:topic_by_user) { Fabricate(:topic, user: user) }

View File

@ -1283,6 +1283,24 @@ RSpec.describe ListController do
end
end
it "should filter with tag_group option" do
topic_with_tag = Fabricate(:topic, tags: [tag])
topic2_with_tag = Fabricate(:topic, tags: [tag])
tag_group = Fabricate(:tag_group, tags: [tag])
sign_in(user)
get "/filter.json", params: { q: "tag_group:#{tag_group.name}" }
parsed = response.parsed_body
expect(response.status).to eq(200)
expect(parsed["topic_list"]["topics"].length).to eq(2)
expect(parsed["topic_list"]["topics"].map { |topic| topic["id"] }).to contain_exactly(
topic_with_tag.id,
topic2_with_tag.id,
)
end
describe "when filtering with the `created-by:<username>` filter" do
fab!(:topic2) { Fabricate(:topic, user: admin) }