Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for CORS headers #172

Merged
merged 2 commits into from Aug 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions app/controllers/qa/application_controller.rb
@@ -1,4 +1,21 @@
module Qa
class ApplicationController < ActionController::Base
# See https://fetch.spec.whatwg.org/#http-access-control-allow-headers
def options
unless Qa.config.cors_headers?
head :not_implemented
return
end
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, OPTIONS'
head :no_content
end

private

# See https://fetch.spec.whatwg.org/#http-access-control-allow-origin
def cors_allow_origin_header
response.headers['Access-Control-Allow-Origin'] = '*' if Qa.config.cors_headers?
end
end
end
4 changes: 3 additions & 1 deletion app/controllers/qa/linked_data_terms_controller.rb
@@ -1,7 +1,7 @@
# This controller is used for all requests to linked data authorities. It will verify params and figure
# out which linked data authority to query based on the 'vocab' param.

class Qa::LinkedDataTermsController < ApplicationController
class Qa::LinkedDataTermsController < Qa::ApplicationController
before_action :check_authority, :init_authority
before_action :check_search_subauthority, :check_query_param, only: :search
before_action :check_show_subauthority, :check_id_param, only: :show
Expand Down Expand Up @@ -31,6 +31,7 @@ def search # rubocop:disable Metrics/MethodLength
head :internal_server_error
return
end
cors_allow_origin_header
render json: terms
end

Expand All @@ -57,6 +58,7 @@ def show # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
head :internal_server_error
return
end
cors_allow_origin_header
render json: term
end

Expand Down
5 changes: 4 additions & 1 deletion app/controllers/qa/terms_controller.rb
Expand Up @@ -3,12 +3,13 @@
# All the authority classes inherit from a super class so they implement the
# same methods.

class Qa::TermsController < ApplicationController
class Qa::TermsController < Qa::ApplicationController
before_action :check_vocab_param, :init_authority
before_action :check_query_param, only: :search

# If the subauthority supports it, return a list of all terms in the authority
def index
cors_allow_origin_header
render json: begin
@authority.all
rescue NotImplementedError
Expand All @@ -19,12 +20,14 @@ def index
# Return a list of terms based on a query
def search
terms = @authority.method(:search).arity == 2 ? @authority.search(url_search, self) : @authority.search(url_search)
cors_allow_origin_header
render json: terms
end

# If the subauthority supports it, return all the information for a given term
def show
term = @authority.find(params[:id])
cors_allow_origin_header
render json: term
end

Expand Down
8 changes: 8 additions & 0 deletions config/routes.rb
Expand Up @@ -6,4 +6,12 @@
get "/search/:vocab(/:subauthority)", controller: :terms, action: :search
get "/show/:vocab/:id", controller: :terms, action: :show
get "/show/:vocab/:subauthority/:id", controller: :terms, action: :show

match "/search/linked_data/:vocab(/:subauthority)", to: 'application#options', via: [:options]
match "/show/linked_data/:vocab/:id", to: 'application#options', via: [:options]
match "/show/linked_data/:vocab/:subauthority/:id", to: 'application#options', via: [:options]
match "/terms/:vocab(/:subauthority)", to: 'application#options', via: [:options]
match "/search/:vocab(/:subauthority)", to: 'application#options', via: [:options]
match "/show/:vocab/:id", to: 'application#options', via: [:options]
match "/show/:vocab/:subauthority/:id", to: 'application#options', via: [:options]
end
4 changes: 4 additions & 0 deletions lib/generators/qa/install/install_generator.rb
Expand Up @@ -7,6 +7,10 @@ def inject_routes
end
end

def create_initializer_config_file
copy_file 'config/initializers/qa.rb'
end

def copy_oclcts_configs
copy_file "config/oclcts-authorities.yml", "config/oclcts-authorities.yml"
end
Expand Down
7 changes: 7 additions & 0 deletions lib/generators/qa/install/templates/config/initializers/qa.rb
@@ -0,0 +1,7 @@
Qa.config do |config|
# When enabled, CORS headers will be added to the responses for search and show. `OPTIONS` method will also be supported.
# Uncomment one of the lines below to enable or disable CORS headers. This configuration defaults to disabled when not set.
# More information on CORS headers at: https://fetch.spec.whatwg.org/#cors-protocol
# config.enable_cors_headers
# config.disable_cors_headers
end
16 changes: 16 additions & 0 deletions lib/qa.rb
Expand Up @@ -6,8 +6,24 @@ module Qa
extend ActiveSupport::Autoload

autoload :Authorities
autoload :Configuration
autoload :Services

# @api public
#
# Exposes the Questioning Authority configuration
#
# @yield [Qa::Configuration] if a block is passed
# @return [Qa::Configuration]
# @see Qa::Configuration for configuration options
def self.config(&block)
@config ||= Qa::Configuration.new

yield @config if block

@config
end

# Raised when the configuration directory for local authorities doesn't exist
class ConfigDirectoryNotFound < StandardError; end

Expand Down
16 changes: 16 additions & 0 deletions lib/qa/configuration.rb
@@ -0,0 +1,16 @@
module Qa
class Configuration
def cors_headers?
return @cors_headers_enabled unless @cors_headers_enabled.nil?
@cors_headers_enabled = false
end

def enable_cors_headers
@cors_headers_enabled = true
end

def disable_cors_headers
@cors_headers_enabled = false
end
end
end
49 changes: 49 additions & 0 deletions spec/controllers/linked_data_terms_controller_spec.rb
Expand Up @@ -123,6 +123,7 @@
expect(response.code).to eq('503')
end
end

context 'in OCLC_FAST authority' do
context '0 search results' do
before do
Expand Down Expand Up @@ -169,6 +170,30 @@
expect(response).to be_successful
end
end

context 'when cors headers are enabled' do
before do
Qa.config.enable_cors_headers
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=oclc.personalName%20all%20%22cornell%22&sortKeys=usage')
.to_return(status: 200, body: webmock_fixture('lod_oclc_personalName_query_3_results.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
end
it 'Access-Control-Allow-Origin is *' do
get :search, params: { q: 'cornell', vocab: 'OCLC_FAST', subauthority: 'personal_name', maximumRecords: '3' }
expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
end
end

context 'when cors headers are disabled' do
before do
Qa.config.disable_cors_headers
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=oclc.personalName%20all%20%22cornell%22&sortKeys=usage')
.to_return(status: 200, body: webmock_fixture('lod_oclc_personalName_query_3_results.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
end
it 'Access-Control-Allow-Origin is not present' do
get :search, params: { q: 'cornell', vocab: 'OCLC_FAST', subauthority: 'personal_name', maximumRecords: '3' }
expect(response.headers.key?('Access-Control-Allow-Origin')).to be false
end
end
end

context 'in AGROVOC authority' do
Expand Down Expand Up @@ -267,6 +292,30 @@
expect(response).to be_successful
end
end

context 'when cors headers are enabled' do
before do
Qa.config.enable_cors_headers
stub_request(:get, 'http://id.worldcat.org/fast/530369')
.to_return(status: 200, body: webmock_fixture('lod_oclc_term_found.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
end
it 'Access-Control-Allow-Origin is *' do
get :show, params: { id: '530369', vocab: 'OCLC_FAST' }
expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
end
end

context 'when cors headers are disabled' do
before do
Qa.config.disable_cors_headers
stub_request(:get, 'http://id.worldcat.org/fast/530369')
.to_return(status: 200, body: webmock_fixture('lod_oclc_term_found.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
end
it 'Access-Control-Allow-Origin is not present' do
get :show, params: { id: '530369', vocab: 'OCLC_FAST' }
expect(response.headers.key?('Access-Control-Allow-Origin')).to be false
end
end
end

context 'in AGROVOC authority' do
Expand Down
66 changes: 66 additions & 0 deletions spec/controllers/terms_controller_spec.rb
Expand Up @@ -92,6 +92,32 @@ def search(_arg1, _arg2)
get :search, params: { q: "Berry", vocab: "loc", subauthority: "names" }
expect(response).to be_successful
end

context 'when cors headers are enabled' do
before do
Qa.config.enable_cors_headers
stub_request(:get, "http://id.loc.gov/search/?format=json&q=Berry&q=cs:http://id.loc.gov/authorities/names")
.with(headers: { 'Accept' => 'application/json' })
.to_return(body: webmock_fixture("loc-names-response.txt"), status: 200)
end
it 'Access-Control-Allow-Origin is *' do
get :search, params: { q: "Tibetan", vocab: "tgnlang" }
expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
end
end

context 'when cors headers are disabled' do
before do
Qa.config.disable_cors_headers
stub_request(:get, "http://id.loc.gov/search/?format=json&q=Berry&q=cs:http://id.loc.gov/authorities/names")
.with(headers: { 'Accept' => 'application/json' })
.to_return(body: webmock_fixture("loc-names-response.txt"), status: 200)
end
it 'Access-Control-Allow-Origin is not present' do
get :search, params: { q: "Tibetan", vocab: "tgnlang" }
expect(response.headers.key?('Access-Control-Allow-Origin')).to be false
end
end
end

context "assign_fast" do
Expand All @@ -117,6 +143,26 @@ def search(_arg1, _arg2)
get :index, params: { vocab: "mesh" }
expect(response).to be_successful
end

context 'when cors headers are enabled' do
before do
Qa.config.enable_cors_headers
end
it 'Access-Control-Allow-Origin is *' do
get :index, params: { vocab: "local", subauthority: "states" }
expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
end
end

context 'when cors headers are disabled' do
before do
Qa.config.disable_cors_headers
end
it 'Access-Control-Allow-Origin is not present' do
get :index, params: { vocab: "local", subauthority: "states" }
expect(response.headers.key?('Access-Control-Allow-Origin')).to be false
end
end
end

context "when the authority does not support #all" do
Expand Down Expand Up @@ -157,6 +203,26 @@ def search(_arg1, _arg2)
get :show, params: { vocab: "loc", subauthority: "subjects", id: "sh85077565" }
expect(response).to be_successful
end

context 'when cors headers are enabled' do
before do
Qa.config.enable_cors_headers
end
it 'Access-Control-Allow-Origin is *' do
get :show, params: { vocab: "mesh", id: "D000001" }
expect(response.headers['Access-Control-Allow-Origin']).to eq '*'
end
end

context 'when cors headers are disabled' do
before do
Qa.config.disable_cors_headers
end
it 'Access-Control-Allow-Origin is not present' do
get :show, params: { vocab: "mesh", id: "D000001" }
expect(response.headers.key?('Access-Control-Allow-Origin')).to be false
end
end
end
end
end