Skip to content

Commit e9851a2

Browse files
committed
Add /api/search/autocompletes path
- This path is a wrapper around a call to the search_api_v2 autocomplete endpoint, but requires CORS support (and most of the test suite is CORS related) because it's currently required to be accessible from all true .gov.uk hosts (including non-prod) as well as heroku preview apps. Audit trail: https://github.com/alphagov/finder-frontend/blob/ddb7cd2c04a4f3d564cf11fb86393e75678add00/app/controllers/api/autocompletes_controller.rb https://github.com/alphagov/finder-frontend/blob/ddb7cd2c04a4f3d564cf11fb86393e75678add00/config/routes.rb#L13-L15 https://github.com/alphagov/finder-frontend/blob/ddb7cd2c04a4f3d564cf11fb86393e75678add00/config/initializers/cors.rb https://github.com/alphagov/finder-frontend/blob/ddb7cd2c04a4f3d564cf11fb86393e75678add00/spec/requests/api/autocomplete_spec.rb
1 parent bd91647 commit e9851a2

4 files changed

Lines changed: 92 additions & 0 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module Api
2+
class AutocompletesController < ApplicationController
3+
def index
4+
render json: autocomplete_response
5+
end
6+
7+
private
8+
9+
def autocomplete_response
10+
Services
11+
.search_api_v2
12+
.autocomplete(params.require(:q))
13+
.to_hash
14+
end
15+
end
16+
end

config/initializers/cors.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Be sure to restart your server when you modify this file.
2+
3+
Rails.application.config.middleware.insert_before 0, Rack::Cors do
4+
# Allow the autocomplete API to be accessed from any GOV.UK domain, including non-production ones,
5+
# as well as Heroku preview apps. Note that the header is rendered on some arbitrary GOV.UK
6+
# subdomains (such as assets.publishing.service.gov.uk for CSV preview pages) so www alone is not
7+
# enough, and we may need autocomplete on local dev environments and Heroku preview apps as well.
8+
allow do
9+
origins %r{(\.gov\.uk|\.herokuapp.com)\z}
10+
11+
resource "/api/search/autocomplete*",
12+
headers: :any,
13+
methods: %i[get]
14+
end
15+
end

config/routes.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@
3434
scope "/places" do
3535
get "/:service_slug" => "api/places#show"
3636
end
37+
38+
scope "/search" do
39+
get "/autocomplete" => "api/autocompletes#index"
40+
end
3741
end
3842

3943
get "/find-local-council" => "find_local_council#index"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
RSpec.describe "Autocomplete API", type: :request do
2+
let(:search_api_v2) { instance_double(GdsApi::SearchApiV2, autocomplete: autocomplete_response) }
3+
4+
let(:suggestions) { %w[blue grey red] }
5+
let(:autocomplete_response) { instance_double(GdsApi::Response, to_hash: { suggestions: }) }
6+
let(:params) { { q: "loving him was" } }
7+
8+
before do
9+
allow(Services).to receive(:search_api_v2).and_return(search_api_v2)
10+
end
11+
12+
it "returns suggestions from Search API v2" do
13+
get "/api/search/autocomplete", params: params
14+
15+
expect(search_api_v2).to have_received(:autocomplete).with("loving him was")
16+
expect(response).to be_successful
17+
expect(JSON.parse(response.body)).to eq("suggestions" => suggestions)
18+
end
19+
20+
it "fails if the query parameter is missing" do
21+
get "/api/search/autocomplete"
22+
23+
expect(response).to have_http_status(:bad_request)
24+
end
25+
26+
describe "CORS headers" do
27+
%w[
28+
https://www.gov.uk
29+
http://example.dev.gov.uk
30+
https://example.publishing.service.gov.uk
31+
https://preview-app-abcd123.herokuapp.com
32+
].each do |allowed_host|
33+
it "returns CORS headers for #{allowed_host}" do
34+
get "/api/search/autocomplete", params:, headers: { Origin: allowed_host }
35+
36+
expect(response.headers.to_h).to include({
37+
"access-control-allow-origin" => allowed_host,
38+
"access-control-allow-methods" => "GET",
39+
})
40+
end
41+
end
42+
43+
it "returns CORS headers when there is a format extension on the path" do
44+
get "/api/search/autocomplete.json", params:, headers: { Origin: "https://www.gov.uk" }
45+
46+
expect(response.headers)
47+
.to include("access-control-allow-origin", "access-control-allow-methods")
48+
end
49+
50+
it "doesn't return CORS headers for an unsupported hosts" do
51+
get "/api/search/autocomplete", params:, headers: { Origin: "https://www.gov.uk.non-govuk.com" }
52+
53+
expect(response.headers)
54+
.not_to include("access-control-allow-origin", "access-control-allow-methods")
55+
end
56+
end
57+
end

0 commit comments

Comments
 (0)