From fd8f83dc7d2a295d47fb2df9f3cad594ed8fd92b Mon Sep 17 00:00:00 2001 From: Molly Struve Date: Wed, 18 Nov 2020 17:43:59 -0500 Subject: [PATCH] Feature:Backend Username Search Endpoint for Mention Completion (#11479) * Feature:Backend Username Search Endpoint for Mention Completion * ensure user is authenticated to use usernames endpoint * update spec with authaa --- app/controllers/search_controller.rb | 10 +++++++++- app/services/search/user.rb | 19 ++++++++++++++++++ config/routes.rb | 1 + spec/requests/search_spec.rb | 14 +++++++++++++ spec/services/search/user_spec.rb | 30 ++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 5e9637713..65550cad9 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,5 +1,5 @@ class SearchController < ApplicationController - before_action :authenticate_user!, only: %i[tags chat_channels reactions] + before_action :authenticate_user!, only: %i[tags chat_channels reactions usernames] before_action :format_integer_params before_action :sanitize_params, only: %i[listings reactions feed_content] @@ -82,6 +82,14 @@ class SearchController < ApplicationController render json: { result: user_search } end + def usernames + usernames = Search::User.search_usernames(params[:username]) + + render json: { result: usernames } + rescue Search::Errors::Transport::BadRequest + render json: { result: [] } + end + def feed_content feed_docs = if params[:class_name].blank? # If we are in the main feed and not filtering by type return diff --git a/app/services/search/user.rb b/app/services/search/user.rb index c19c54990..6d48b16ca 100644 --- a/app/services/search/user.rb +++ b/app/services/search/user.rb @@ -7,8 +7,27 @@ module Search DEFAULT_PER_PAGE = 20 class << self + def search_usernames(username) + results = search(body: username_query(username)) + results.dig("hits", "hits").map do |doc| + doc.dig("_source", "username") + end + end + private + def username_query(username) + { + query: { + query_string: { + query: "username:#{username}*", + analyze_wildcard: true, + allow_leading_wildcard: false + } + } + } + end + def prepare_doc(hit) source = hit["_source"] { diff --git a/config/routes.rb b/config/routes.rb index df9c3389a..ea1caa665 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -310,6 +310,7 @@ Rails.application.routes.draw do get "/search/chat_channels" => "search#chat_channels" get "/search/listings" => "search#listings" get "/search/users" => "search#users" + get "/search/usernames" => "search#usernames" get "/search/feed_content" => "search#feed_content" get "/search/reactions" => "search#reactions" get "/chat_channel_memberships/find_by_chat_channel_id" => "chat_channel_memberships#find_by_chat_channel_id" diff --git a/spec/requests/search_spec.rb b/spec/requests/search_spec.rb index c4e0558d3..0df9c6a06 100644 --- a/spec/requests/search_spec.rb +++ b/spec/requests/search_spec.rb @@ -66,6 +66,20 @@ RSpec.describe "Search", type: :request, proper_status: true do end end + describe "GET /search/usernames" do + let(:authorized_user) { create(:user) } + let(:names) { ["username"] } + + it "returns json" do + sign_in authorized_user + allow(Search::User).to receive(:search_usernames).and_return( + names, + ) + get "/search/usernames" + expect(response.parsed_body).to eq("result" => names) + end + end + describe "GET /search/feed_content" do let(:mock_documents) { [{ "title" => "article1" }] } diff --git a/spec/services/search/user_spec.rb b/spec/services/search/user_spec.rb index 9075dad63..5561f78e9 100644 --- a/spec/services/search/user_spec.rb +++ b/spec/services/search/user_spec.rb @@ -57,4 +57,34 @@ RSpec.describe Search::User, type: :service do end end end + + describe "::search_usernames", elasticsearch: "User" do + let(:user1) { create(:user, username: "star_wars_is_the_best") } + let(:user2) { create(:user, username: "star_trek_is_the_best") } + + before do + index_documents([user1, user2]) + end + + it "searches with username" do + usernames = described_class.search_usernames(user1.username) + expect(usernames.count).to eq(1) + expect(usernames).to match([user1.username]) + end + + it "analyzes wildcards" do + user3 = create(:user, username: "does_not_start_with_a_star") + index_documents([user3]) + + usernames = described_class.search_usernames("star*") + expect(usernames).to match( + [user1.username, user2.username], + ) + expect(usernames).not_to include(user3.username) + end + + it "does not allow leading wildcards" do + expect { described_class.search_usernames("*star") }.to raise_error(Search::Errors::Transport::BadRequest) + end + end end