DEV: Support `created-by:<username>` filter on `/filter` route (#21067)

This commit adds support for the `created-by:<username>` query filter
which will return topics created by the specified user. Multiple
usernames can be specified by comma seperating the usernames like so:
`created-by:username1,username2`. This will filter for topics created by
either of the specified users. Multiple `created-by:<username>` can also
be composed together. `created-by:username1 created-by:username2` is
equivalent to `created-by:username1,username2`.
This commit is contained in:
Alan Guo Xiang Tan 2023-04-12 09:25:06 +08:00 committed by GitHub
parent 584a17c948
commit a1524b84e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 138 additions and 2 deletions

View File

@ -16,9 +16,11 @@ class TopicsFilter
return @scope if query_string.blank?
query_string.scan(
/(?<key_prefix>[-=])?(?<key>\w+):(?<value>[^\s]+)/,
/(?<key_prefix>[-=])?(?<key>[\w-]+):(?<value>[^\s]+)/,
) do |key_prefix, key, value|
case key
when "created-by"
filter_created_by_user(usernames: value.split(","))
when "in"
@scope = filter_state(state: value)
when "status"
@ -90,6 +92,17 @@ class TopicsFilter
private
def filter_created_by_user(usernames:)
register_scope(
key: :created_by_user,
params: {
usernames: usernames.map(&:downcase),
},
) do |usernames_lower|
@scope.joins(:user).where("users.username_lower IN (?)", usernames_lower)
end
end
def filter_state(state:)
case state
when "pinned"

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.describe TopicsFilter do
fab!(:user) { Fabricate(:user) }
fab!(:user) { Fabricate(:user, username: "username") }
fab!(:admin) { Fabricate(:admin) }
fab!(:group) { Fabricate(:group) }
@ -737,5 +737,78 @@ RSpec.describe TopicsFilter do
).to contain_exactly(topic_without_tag.id, topic_with_group_only_tag.id)
end
end
describe "when filtering by topic author" do
fab!(:user2) { Fabricate(:user, username: "username2") }
fab!(:topic_by_user) { Fabricate(:topic, user: user) }
fab!(:topic2_by_user) { Fabricate(:topic, user: user) }
fab!(:topic_by_user2) { Fabricate(:topic, user: user2) }
describe "when query string is `created-by:username`" do
it "should return the topics created by the specified user" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:#{user.username}")
.pluck(:id),
).to contain_exactly(topic_by_user.id, topic2_by_user.id)
end
end
describe "when query string is `created-by:username2`" do
it "should return the topics created by the specified user" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:#{user2.username}")
.pluck(:id),
).to contain_exactly(topic_by_user2.id)
end
end
describe "when query string is `created-by:username created-by:username2`" do
it "should return the topics created by either of the specified users" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:#{user.username} created-by:#{user2.username}")
.pluck(:id),
).to contain_exactly(topic_by_user.id, topic2_by_user.id, topic_by_user2.id)
end
end
describe "when query string is `created-by:username,invalid`" do
it "should only return the topics created by the user with the valid username" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:#{user.username},invalid")
.pluck(:id),
).to contain_exactly(topic_by_user.id, topic2_by_user.id)
end
end
describe "when query string is `created-by:username,username2`" do
it "should return the topics created by either of the specified users" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:#{user.username},#{user2.username}")
.pluck(:id),
).to contain_exactly(topic_by_user.id, topic2_by_user.id, topic_by_user2.id)
end
end
describe "when query string is `created-by:invalid`" do
it "should not return any topics" do
expect(
TopicsFilter
.new(guardian: Guardian.new)
.filter_from_query_string("created-by:invalid")
.pluck(:id),
).to eq([])
end
end
end
end
end

View File

@ -1132,9 +1132,25 @@ RSpec.describe ListController do
describe "#filter" do
fab!(:category) { Fabricate(:category) }
fab!(:tag) { Fabricate(:tag, name: "tag1") }
fab!(:group) { Fabricate(:group) }
fab!(:private_category) { Fabricate(:private_category, group: Fabricate(:group)) }
fab!(:private_message_topic) { Fabricate(:private_message_topic) }
fab!(:topic_in_private_category) { Fabricate(:topic, category: private_category) }
before { SiteSetting.experimental_topics_filter = true }
it "should not return topics that the user is not allowed to view" do
sign_in(user)
get "/filter.json"
expect(response.status).to eq(200)
expect(
response.parsed_body["topic_list"]["topics"].map { |topic| topic["id"] },
).to contain_exactly(topic.id)
end
it "should respond with 403 response code for an anonymous user" do
get "/filter.json"
@ -1212,6 +1228,40 @@ RSpec.describe ListController do
end
end
describe "when filtering with the `created-by:<username>` filter" do
fab!(:topic2) { Fabricate(:topic, user: admin) }
before do
topic.update!(user: user)
user.update!(username: "username")
admin.update!(username: "username2")
end
it "returns only topics created by the user when `q` query param is `created-by:username`" do
sign_in(user)
get "/filter.json", params: { q: "created-by:username" }
expect(response.status).to eq(200)
expect(
response.parsed_body["topic_list"]["topics"].map { |topic| topic["id"] },
).to contain_exactly(topic.id)
end
it "returns only topics created by either user when `q` query param is `created-by:username,username2`" do
sign_in(user)
get "/filter.json", params: { q: "created-by:username,username2" }
expect(response.status).to eq(200)
expect(
response.parsed_body["topic_list"]["topics"].map { |topic| topic["id"] },
).to contain_exactly(topic.id, topic2.id)
end
end
describe "when filtering with the `in:<topic_notification_level>` filter" do
fab!(:user_muted_topic) do
Fabricate(:topic).tap do |topic|