diff --git a/app/controllers/api/v1/peers/search_controller.rb b/app/controllers/api/v1/peers/search_controller.rb index 0c503d9bc54..1780554c5d8 100644 --- a/app/controllers/api/v1/peers/search_controller.rb +++ b/app/controllers/api/v1/peers/search_controller.rb @@ -27,7 +27,7 @@ class Api::V1::Peers::SearchController < Api::BaseController @domains = InstancesIndex.query(function_score: { query: { prefix: { - domain: TagManager.instance.normalize_domain(params[:q].strip), + domain: normalized_domain, }, }, @@ -37,11 +37,18 @@ class Api::V1::Peers::SearchController < Api::BaseController }, }).limit(10).pluck(:domain) else - domain = params[:q].strip - domain = TagManager.instance.normalize_domain(domain) - @domains = Instance.searchable.where(Instance.arel_table[:domain].matches("#{Instance.sanitize_sql_like(domain)}%", false, true)).limit(10).pluck(:domain) + domain = normalized_domain + @domains = Instance.searchable.domain_starts_with(domain).limit(10).pluck(:domain) end rescue Addressable::URI::InvalidURIError @domains = [] end + + def normalized_domain + TagManager.instance.normalize_domain(query_value) + end + + def query_value + params[:q].strip + end end diff --git a/app/models/instance.rb b/app/models/instance.rb index 17ee0cbb1e0..8f8d87c62a6 100644 --- a/app/models/instance.rb +++ b/app/models/instance.rb @@ -23,6 +23,7 @@ class Instance < ApplicationRecord scope :searchable, -> { where.not(domain: DomainBlock.select(:domain)) } scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } + scope :domain_starts_with, ->(value) { where(arel_table[:domain].matches("#{sanitize_sql_like(value)}%", false, true)) } scope :by_domain_and_subdomains, ->(domain) { where("reverse('.' || domain) LIKE reverse(?)", "%.#{domain}") } def self.refresh diff --git a/spec/requests/api/v1/peers/search_spec.rb b/spec/requests/api/v1/peers/search_spec.rb new file mode 100644 index 00000000000..dcdea387a5b --- /dev/null +++ b/spec/requests/api/v1/peers/search_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe 'API Peers Search' do + describe 'GET /api/v1/peers/search' do + context 'when peers api is disabled' do + before do + Setting.peers_api_enabled = false + end + + it 'returns http not found response' do + get '/api/v1/peers/search' + + expect(response) + .to have_http_status(404) + end + end + + context 'with no search param' do + it 'returns http success and empty response' do + get '/api/v1/peers/search' + + expect(response) + .to have_http_status(200) + expect(body_as_json) + .to be_blank + end + end + + context 'with invalid search param' do + it 'returns http success and empty response' do + get '/api/v1/peers/search', params: { q: 'ftp://Invalid-Host!!.valüe' } + + expect(response) + .to have_http_status(200) + expect(body_as_json) + .to be_blank + end + end + + context 'with search param' do + let!(:account) { Fabricate(:account, domain: 'host.example') } + + before { Instance.refresh } + + it 'returns http success and json with known domains' do + get '/api/v1/peers/search', params: { q: 'host.example' } + + expect(response) + .to have_http_status(200) + expect(body_as_json.size) + .to eq(1) + expect(body_as_json.first) + .to eq(account.domain) + end + end + end +end