Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ PRIMO_SCOPE=cdi
PRIMO_TAB=all
PRIMO_VID=01MIT_INST:MIT
SYNDETICS_PRIMO_URL=https://syndetics.com/index.php?client=primo
TACOS_HOST=FAKE_TACOS_HOST
TACOS_URL=http://FAKE_TACOS_HOST/graphql
TACOS_SOURCE=FAKE_TACOS_SOURCE
TIMDEX_GRAPHQL=https://FAKE_TIMDEX_HOST/graphql
TIMDEX_HOST=FAKE_TIMDEX_HOST
TIMDEX_INDEX=FAKE_TIMDEX_INDEX
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ mode (e.g., `GDT=false` will still enable GDT features). Note that this is curre
may have unexpected consequences if applied to other TIMDEX UI apps.
- `GLOBAL_ALERT`: The main functionality for this comes from our theme gem, but when set the value will be rendered as
safe html above the main header of the site.
- `ORIGINS`: sets origins for CORS (currently used only for TACOS API calls).
- `PLATFORM_NAME`: The value set is added to the header after the MIT Libraries logo. The logic and CSS for this comes from our theme gem.
- `PRIMO_TIMEOUT`: The number of seconds before a Primo request times out (default 6).
- `REQUESTS_PER_PERIOD` - number of requests that can be made for general throttles per `REQUEST_PERIOD`
Expand All @@ -136,13 +137,21 @@ may have unexpected consequences if applied to other TIMDEX UI apps.
- `REDIRECT_REQUEST_PERIOD`- time in minutes used along with `REDIRECT_REQUEST_PERIOD`
- `SENTRY_DSN`: Client key for Sentry exception logging.
- `SENTRY_ENV`: Sentry environment for the application. Defaults to 'unknown' if unset.
- `TACOS_SOURCE`: If set, this value is sent to TACOS (as the `sourceSystem` value) to distinguish which application
instance is sending what search traffic. Defaults to "unset" if not defined.
- `TACOS_URL`: The GraphQL endpoint for the [TACOS API](https://github.com/mitlibraries/tacos/). When set, the
application will log search terms to TACOS (and eventually return suggested resources that TACOS detects).
- `TIMDEX_INDEX`: Name of the index, or alias, to provide to the GraphQL endpoint. Defaults to `nil` which will let TIMDEX determine the best index to use. Wildcard values can be set, for example `rdi*` would search any indexes that begin with `rdi` in the underlying OpenSearch instance behind TIMDEX.
- `TIMDEX_SOURCES`: Comma-separated list of sources to display in the advanced-search source selection element. This
overrides the default which is set in ApplicationHelper.

#### Test Environment-only Variables

- `SPEC_REPORTER`: Optional variable. If set, enables spec reporter style output from tests rather than minimal output.
- `TACOS_HOST`: Test Env only. Used to ensure the VCR cassettes can properly scrub specific host data to make sure we
get the same cassettes regardless of which host was used to generate the cassettes. This should be set to the hostname
that matches `TACOS_URL`. Ex: If `TACOS_URL` is `http://localhost:3001/graphql` then `TACOS_HOST` should be
`localhost:3001`.
- `TIMDEX_HOST`: Test Env only. Used to ensure the VCR cassettes can properly scrub specific host data to make sure we get the same cassettes regardless of which host was used to generate the cassettes. This should be set to the host name that matches `TIMDEX_GRAPHQL`. Ex: If `TIMDEX_GRAPHQL` is `https://www.example.com/graphql` then `TIMDEX_HOST` should be `www.example.com`.

### Generating VCR Cassettes
Expand Down
9 changes: 9 additions & 0 deletions app/controllers/tacos_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class TacosController < ApplicationController
layout false

def analyze
return unless ApplicationHelper.tacos_enabled?

Tacos.analyze(params[:q])
end
end
5 changes: 5 additions & 0 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
module ApplicationHelper
def tacos_enabled?
ENV.fetch('TACOS_URL', '').present?
end
module_function :tacos_enabled?

def timdex_sources
ENV.fetch('TIMDEX_SOURCES', timdex_source_defaults).split(',')
end
Expand Down
49 changes: 49 additions & 0 deletions app/models/tacos.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
class Tacos
# The tacos_client argument here is unused in production - it is provided for
# our test suite so that we can mock various error conditions to ensure that
# error handling happens as we intend.
def self.analyze(term, tacos_client = nil)
tacos_http = setup(tacos_client)
query = '{ "query": "{ logSearchEvent(searchTerm: \"' + clean_term(term) + '\", sourceSystem: \"' + tacos_source + '\" ) { phrase source detectors { suggestedResources { title url } } } }" }'
begin
raw_response = tacos_http.timeout(http_timeout).post(tacos_url, body: query)
JSON.parse(raw_response.to_s)
rescue HTTP::Error
{"error" => "A connection error has occurred"}
rescue JSON::ParserError
{"error" => "A parsing error has occurred"}
end
end

private

def self.clean_term(term)
term.gsub('"', '\'')
end

def self.http_timeout
ENV.fetch('TIMDEX_TIMEOUT', 6).to_f
end

def self.origins
ENV.fetch('ORIGINS', nil)
end

# We define the HTTP connection this way so that it can be overridden during
# testing, to make sure that the .analyze method can handle specific error
# conditions.
def self.setup(tacos_client)
tacos_client || HTTP.persistent(tacos_url)
.headers(accept: 'application/json',
'Content-Type': 'application/json',
origin: origins)
end

def self.tacos_source
ENV.fetch('TACOS_SOURCE', 'timdexui_unset')
end

def self.tacos_url
ENV.fetch('TACOS_URL', nil)
end
end
8 changes: 8 additions & 0 deletions app/views/search/_trigger_tacos.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<% return unless (tacos_enabled? and @enhanced_query[:q].present?) %>

<% data_url = "/analyze?q=#{URI.encode_www_form_component(@enhanced_query[:q])}" %>

<div class="tacos-container"
data-controller="content-loader"
data-content-loader-url-value=<%= data_url %>>
</div>
2 changes: 2 additions & 0 deletions app/views/search/results.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,6 @@
<% end %>
</div>

<%= render(partial: 'trigger_tacos') if tacos_enabled? %>

<%= javascript_include_tag "filters" %>
1 change: 1 addition & 0 deletions app/views/tacos/analyze.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- Result of TACOS analysis would go here -->
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
get 'issn', to: 'fact#issn'
get 'pmid', to: 'fact#pmid'

get 'analyze', to: 'tacos#analyze'

get 'record/(:id)',
to: 'record#view',
as: 'record',
Expand Down
32 changes: 28 additions & 4 deletions test/controllers/search_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ def setup
end
assert_response :success

assert_select('div[data-content-loader-url-value]', 0)
assert_select('div[data-content-loader-url-value].fact-container', 0)
end
end

Expand Down Expand Up @@ -305,7 +305,7 @@ def setup

assert_response :success

assert_select('div[data-content-loader-url-value]', 0)
assert_select('div[data-content-loader-url-value].fact-container', 0)
end
end

Expand Down Expand Up @@ -333,7 +333,7 @@ def setup
end
assert_response :success

assert_select('div[data-content-loader-url-value]', 0)
assert_select('div[data-content-loader-url-value].fact-container', 0)
end
end

Expand Down Expand Up @@ -361,7 +361,31 @@ def setup
end
assert_response :success

assert_select('div[data-content-loader-url-value]', 0)
assert_select('div[data-content-loader-url-value].fact-container', 0)
end
end

test 'TACOS intervention is inserted when TACOS enabled' do
VCR.use_cassette('tacos',
allow_playback_repeats: true) do
get '/results?q=tacos'

assert_response :success

tacos_div = assert_select('div[data-content-loader-url-value].tacos-container')
assert_equal '/analyze?q=tacos', tacos_div.attribute('data-content-loader-url-value').value
end
end

test 'TACOS intervention not inserted when TACOS not enabled' do
VCR.use_cassette('tacos',
allow_playback_repeats: true) do
ClimateControl.modify(TACOS_URL: '') do
get '/results?q=tacos'
end
assert_response :success

assert_select('div[data-content-loader-url-value].tacos-container', 0)
end
end

Expand Down
12 changes: 12 additions & 0 deletions test/controllers/tacos_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require 'test_helper'

class TacosControllerTest < ActionDispatch::IntegrationTest
test 'analyze route exists but returns an HTML comment for now' do
VCR.use_cassette('tacos direct') do
get '/analyze?q=direct'

assert_response :success
assert_equal "<!-- Result of TACOS analysis would go here -->\n", response.body
end
end
end
10 changes: 10 additions & 0 deletions test/helpers/application_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
class ApplicationHelperTest < ActionView::TestCase
include ApplicationHelper

test 'tacos_enabled? requires TACOS_URL env variable to have non-zero length' do
ClimateControl.modify TACOS_URL: 'foo' do
assert_equal true, tacos_enabled?
end

ClimateControl.modify TACOS_URL: '' do
assert_equal false, tacos_enabled?
end
end

test 'index_page_title renders as expected' do
assert_equal 'Search | MIT Libraries', index_page_title
end
Expand Down
62 changes: 62 additions & 0 deletions test/models/tacos_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'test_helper'

class TacosConnectionError
def timeout(_)
self
end

def post(_url, body:)
raise HTTP::ConnectionError, "forced connection failure"
end
end

class TacosParsingError
def timeout(_)
self
end

def post(_url, body:)
'This is not valid json'
end
end

class TacosTest < ActiveSupport::TestCase
test 'TACOS model has a call method that reflects a search term back' do
VCR.use_cassette('tacos popcorn') do
searchterm = 'popcorn'

result = Tacos.analyze(searchterm)

assert_instance_of Hash, result
assert_equal searchterm, result['data']['logSearchEvent']['phrase']
end
end

test 'TACOS model will use ENV to populate the sourceSystem value' do
VCR.use_cassette('tacos fake system') do
ClimateControl.modify(TACOS_SOURCE: 'faked') do
result = Tacos.analyze('popcorn')

assert_equal 'faked', result['data']['logSearchEvent']['source']
end
end
end

test 'TACOS model catches connection errors' do
tacos_client = TacosConnectionError.new

result = Tacos.analyze('popcorn', tacos_client)

assert_instance_of Hash, result
assert_equal 'A connection error has occurred', result['error']
end

test 'TACOS model catches parsing errors' do
tacos_client = TacosParsingError.new

result = Tacos.analyze('popcorn', tacos_client)

assert_instance_of Hash, result
assert_equal 'A parsing error has occurred', result['error']
end
end
4 changes: 3 additions & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@
end

VCR.configure do |config|
config.ignore_localhost = true
config.ignore_localhost = false
config.cassette_library_dir = 'test/vcr_cassettes'
config.hook_into :webmock
config.allow_http_connections_when_no_cassette = false
config.filter_sensitive_data('FAKE_TIMDEX_HOST') { ENV.fetch('TIMDEX_HOST').to_s }
config.filter_sensitive_data('http://FAKE_TIMDEX_HOST/graphql/') { ENV.fetch('TIMDEX_GRAPHQL').to_s }
config.filter_sensitive_data('FAKE_TIMDEX_INDEX') { ENV.fetch('TIMDEX_INDEX').to_s }
config.filter_sensitive_data('FAKE_PRIMO_API_KEY') { ENV.fetch('PRIMO_API_KEY').to_s }
config.filter_sensitive_data('FAKE_TACOS_HOST') { ENV.fetch('TACOS_HOST').to_s }
config.filter_sensitive_data('http://FAKE_TACOS_HOST/graphql/') { ENV.fetch('TACOS_URL').to_s }
end

module ActiveSupport
Expand Down
88 changes: 88 additions & 0 deletions test/vcr_cassettes/tacos.yml

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions test/vcr_cassettes/tacos_direct.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading