diff --git a/lib/collectionspace/mapper/searcher.rb b/lib/collectionspace/mapper/searcher.rb
index 15edfcda..2127b2c9 100644
--- a/lib/collectionspace/mapper/searcher.rb
+++ b/lib/collectionspace/mapper/searcher.rb
@@ -21,16 +21,13 @@ def call(value:, type:, subtype: nil)
attr_reader :client, :active, :search_fields
- def case_swap(string)
- string.match?(/[A-Z]/) ? string.downcase : string.capitalize
- end
-
- def get_response(value, type, subtype)
+ def get_response(value, type, subtype, operator = "=")
response = client.find(
type: type,
subtype: subtype,
value: value,
- field: search_field(type)
+ field: search_field(type),
+ operator: operator
)
rescue => e
puts e.message
@@ -50,6 +47,10 @@ def lookup_search_field(type)
def parse_response(response)
parsed = response.parsed["abstract_common_list"]
+ return parsed if parsed["list_item"].is_a?(Array)
+
+ parsed["list_item"] = [parsed["list_item"]]
+ parsed
rescue => e
puts e.message
nil
@@ -83,7 +84,21 @@ def search_response(value, type, subtype)
as_is = get_response(value, type, subtype)
return as_is if response_usable?(as_is)
- get_response(case_swap(value), type, subtype)
+ case_insensitive_response(value, type, subtype)
+ end
+
+ def case_insensitive_response(value, type, subtype)
+ response = get_response(value, type, subtype, "ILIKE")
+ return nil unless response_usable?(response)
+
+ displayname = response.dig("list_item", 0, "displayName") ||
+ response.dig("list_item", 0, "termDisplayName")
+ warning = {
+ category: "case_insensitive_match",
+ message: "Searched: #{value}. Using: #{displayname}"
+ }
+ response["warnings"] = [warning]
+ response
end
end
end
diff --git a/lib/collectionspace/mapper/term_searchable.rb b/lib/collectionspace/mapper/term_searchable.rb
index d97b5c30..0def4229 100644
--- a/lib/collectionspace/mapper/term_searchable.rb
+++ b/lib/collectionspace/mapper/term_searchable.rb
@@ -194,13 +194,20 @@ def rec_from_response(category, val, apiresponse)
end
def return_record(category, val, apiresponse, term_ct)
+ rec = apiresponse["list_item"][0]
+
case term_ct
when 0
- rec = nil
+ nil
when 1
- rec = apiresponse["list_item"]
+ if apiresponse.key?("warnings")
+ apiresponse["warnings"].each do |warning|
+ response.add_warning(warning.merge({field: column}))
+ end
+ end
+ rec
+ # rec = apiresponse["list_item"]
else
- rec = apiresponse["list_item"][0]
using_uri = "#{client.config.base_uri}#{rec["uri"]}"
response.add_warning({
category: :"multiple_records_found_for_#{category}",
@@ -210,9 +217,8 @@ def return_record(category, val, apiresponse, term_ct)
value: val,
message: "#{term_ct} records found. Using #{using_uri}"
})
+ rec
end
-
- rec
end
def add_bad_lookup_error(category, val)
diff --git a/spec/collectionspace/mapper/searcher_spec.rb b/spec/collectionspace/mapper/searcher_spec.rb
index d97f34d2..2f21a348 100644
--- a/spec/collectionspace/mapper/searcher_spec.rb
+++ b/spec/collectionspace/mapper/searcher_spec.rb
@@ -15,44 +15,48 @@
describe "#.call", vcr: "core_domain_check" do
let(:result) { searcher.call(**args) }
- let(:args) { {value: "All", type: "vocabularies", subtype: "publishto"} }
+ let(:args) { {value: value, type: "vocabularies", subtype: "publishto"} }
context "when search_if_not_cached = true", vcr: "searcher_search" do
let(:config) { {} }
+ let(:value) { "All" }
it "returns expected hash" do
- expected = {
- "fieldsReturned" =>
- "csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|"\
- "proposed|referenced|deprecated|termStatus|description|source|"\
- "order|displayName|shortIdentifier",
- "itemsInPage" => "1",
- "list_item" => {
- "csid" => "d614ebc5-96fd-4680-9727",
- "displayName" => "All",
- "proposed" => "true",
- "refName" =>
- "urn:cspace:core.collectionspace.org:vocabularies:name"\
- "(publishto):item:name(all)'All'",
- "rev" => "0",
- "sas" => "false",
- "shortIdentifier" => "all",
- "updatedAt" => "2020-02-08T03:30:26.054Z",
- "uri" =>
- "/vocabularies/e2ea6ca3-4c60-427d-96e5/items/"\
- "d614ebc5-96fd-4680-9727",
- "workflowState" => "project"
- },
- "pageNum" => "0",
- "pageSize" => "25",
- "totalItems" => "1"
- }
- expect(result).to eq(expected)
+ expect(result["totalItems"]).to eq("1")
+ expect(result.dig("list_item", 0, "displayName")).to eq("All")
+ end
+
+ context "when term not matching case sensitively" do
+ context "when vocabulary", vcr: "searcher_ci_vocab" do
+ let(:value) { "alL" }
+
+ it "returns expected hash" do
+ expect(result.key?("warnings")).to be true
+ warning = result["warnings"].first
+ expect(warning[:category]).to eq("case_insensitive_match")
+ expect(warning[:message]).to eq("Searched: #{value}. Using: All")
+ end
+ end
+
+ context "when authority", vcr: "searcher_ci_authority" do
+ let(:value) { "Art" }
+ let(:args) do
+ {value: value, type: "conceptauthorities", subtype: "concept"}
+ end
+
+ it "returns expected hash" do
+ expect(result.key?("warnings")).to be true
+ warning = result["warnings"].first
+ expect(warning[:category]).to eq("case_insensitive_match")
+ expect(warning[:message]).to eq("Searched: #{value}. Using: art")
+ end
+ end
end
end
context "when search_if_not_cached = false" do
let(:config) { {search_if_not_cached: false} }
+ let(:value) { "All" }
it "returns nil" do
expect(result).to be_nil
diff --git a/spec/collectionspace/mapper/term_searchable_spec.rb b/spec/collectionspace/mapper/term_searchable_spec.rb
index a336799f..917be6c5 100644
--- a/spec/collectionspace/mapper/term_searchable_spec.rb
+++ b/spec/collectionspace/mapper/term_searchable_spec.rb
@@ -7,13 +7,16 @@ class TermClass
include CollectionSpace::Mapper::TermSearchable
- attr_reader :type, :subtype, :handler
+ attr_reader :type, :subtype, :handler, :response
def initialize(type, subtype, handler)
@type = type
@subtype = subtype
@handler = handler
+ @response = CollectionSpace::Mapper::Response.new({}, handler)
@errors = []
end
+
+ def column = "foo"
end
RSpec.describe CollectionSpace::Mapper::TermSearchable,
@@ -131,6 +134,11 @@ def initialize(type, subtype, handler)
expected = "urn:cspace:core.collectionspace.org:vocabularies:name"\
"(publishto):item:name(all)'All'"
expect(result).to eq(expected)
+ expect(term.response.warnings).to include({
+ category: "case_insensitive_match",
+ message: "Searched: all. Using: All",
+ field: "foo"
+ })
end
end
end
diff --git a/spec/support/cassettes/searcher_ci_authority.yml b/spec/support/cassettes/searcher_ci_authority.yml
new file mode 100644
index 00000000..c74d01ac
--- /dev/null
+++ b/spec/support/cassettes/searcher_ci_authority.yml
@@ -0,0 +1,167 @@
+---
+http_interactions:
+- request:
+ method: get
+ uri: https://core.dev.collectionspace.org/cspace-services/personauthorities?pgNum=0&pgSz=1&wf_deleted=false
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Authorization:
+ - Basic YWRtaW5AY29yZS5jb2xsZWN0aW9uc3BhY2Uub3JnOkFkbWluaXN0cmF0b3I=
+ response:
+ status:
+ code: 200
+ message: ''
+ headers:
+ Date:
+ - Sat, 19 Oct 2024 00:12:24 GMT
+ Content-Type:
+ - application/xml
+ Content-Length:
+ - '805'
+ Connection:
+ - keep-alive
+ Vary:
+ - Access-Control-Request-Headers
+ - Access-Control-Request-Method
+ - Origin
+ Set-Cookie:
+ - JSESSIONID=FEBAA20BE92073180EE63DDEC3490877; Path=/cspace-services; Secure;
+ HttpOnly
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Cache-Control:
+ - no-cache, no-store, max-age=0, must-revalidate
+ Pragma:
+ - no-cache
+ Expires:
+ - '0'
+ Strict-Transport-Security:
+ - max-age=31536000 ; includeSubDomains
+ X-Frame-Options:
+ - DENY
+ body:
+ encoding: UTF-8
+ string: 0112csid|uri|refName|updatedAt|workflowState|rev|shortIdentifier|sas|displayName|vocabType4bea28cb-83be-4ca1-971b/personauthorities/4bea28cb-83be-4ca1-971burn:cspace:core.collectionspace.org:personauthorities:name(person)'Local
+ Persons'2023-08-26T05:44:37.565Zproject13personLocal
+ PersonsPersonAuthority
+ recorded_at: Sat, 19 Oct 2024 00:12:24 GMT
+- request:
+ method: get
+ uri: https://core.dev.collectionspace.org/cspace-services/conceptauthorities/urn:cspace:name(concept)/items?as=concepts_common:conceptTermGroupList/0/termDisplayName%20=%20%27Art%27&pgSz=25&sortBy=collectionspace_core:updatedAt%20DESC&wf_deleted=false
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Authorization:
+ - Basic YWRtaW5AY29yZS5jb2xsZWN0aW9uc3BhY2Uub3JnOkFkbWluaXN0cmF0b3I=
+ response:
+ status:
+ code: 200
+ message: ''
+ headers:
+ Date:
+ - Sat, 19 Oct 2024 00:12:25 GMT
+ Content-Type:
+ - application/xml
+ Content-Length:
+ - '402'
+ Connection:
+ - keep-alive
+ Vary:
+ - Access-Control-Request-Headers
+ - Access-Control-Request-Method
+ - Origin
+ Set-Cookie:
+ - JSESSIONID=A5EB36E7ED1BEEB2C121BD38D3CFD55A; Path=/cspace-services; Secure;
+ HttpOnly
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Cache-Control:
+ - no-cache, no-store, max-age=0, must-revalidate
+ Pragma:
+ - no-cache
+ Expires:
+ - '0'
+ Strict-Transport-Security:
+ - max-age=31536000 ; includeSubDomains
+ X-Frame-Options:
+ - DENY
+ body:
+ encoding: UTF-8
+ string: 02500csid|uri|refName|updatedAt|workflowState|rev|shortIdentifier|sas|proposed|deprecated|termStatus|termDisplayName
+ recorded_at: Sat, 19 Oct 2024 00:12:25 GMT
+- request:
+ method: get
+ uri: https://core.dev.collectionspace.org/cspace-services/conceptauthorities/urn:cspace:name(concept)/items?as=concepts_common:conceptTermGroupList/0/termDisplayName%20ILIKE%20%27Art%27&pgSz=25&sortBy=collectionspace_core:updatedAt%20DESC&wf_deleted=false
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Authorization:
+ - Basic YWRtaW5AY29yZS5jb2xsZWN0aW9uc3BhY2Uub3JnOkFkbWluaXN0cmF0b3I=
+ response:
+ status:
+ code: 200
+ message: ''
+ headers:
+ Date:
+ - Sat, 19 Oct 2024 00:12:25 GMT
+ Content-Type:
+ - application/xml
+ Content-Length:
+ - '892'
+ Connection:
+ - keep-alive
+ Vary:
+ - Access-Control-Request-Headers
+ - Access-Control-Request-Method
+ - Origin
+ Set-Cookie:
+ - JSESSIONID=CF61C02A1720ED8CFAE1B82E1F0C7CFE; Path=/cspace-services; Secure;
+ HttpOnly
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Cache-Control:
+ - no-cache, no-store, max-age=0, must-revalidate
+ Pragma:
+ - no-cache
+ Expires:
+ - '0'
+ Strict-Transport-Security:
+ - max-age=31536000 ; includeSubDomains
+ X-Frame-Options:
+ - DENY
+ body:
+ encoding: UTF-8
+ string: 02511csid|uri|refName|updatedAt|workflowState|rev|shortIdentifier|sas|proposed|deprecated|termStatus|termDisplayName69b1a466-0c91-4baf-868c/conceptauthorities/92e3f0a0-2fa2-4ecb-a3f7/items/69b1a466-0c91-4baf-868curn:cspace:core.collectionspace.org:conceptauthorities:name(concept):item:name(art1729294383466)'art'2024-10-18T23:33:03.465Zproject0art1729294383466falsetrueart
+ recorded_at: Sat, 19 Oct 2024 00:12:25 GMT
+recorded_with: VCR 6.1.0
diff --git a/spec/support/cassettes/searcher_ci_vocab.yml b/spec/support/cassettes/searcher_ci_vocab.yml
new file mode 100644
index 00000000..b2e47791
--- /dev/null
+++ b/spec/support/cassettes/searcher_ci_vocab.yml
@@ -0,0 +1,167 @@
+---
+http_interactions:
+- request:
+ method: get
+ uri: https://core.dev.collectionspace.org/cspace-services/personauthorities?pgNum=0&pgSz=1&wf_deleted=false
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Authorization:
+ - Basic YWRtaW5AY29yZS5jb2xsZWN0aW9uc3BhY2Uub3JnOkFkbWluaXN0cmF0b3I=
+ response:
+ status:
+ code: 200
+ message: ''
+ headers:
+ Date:
+ - Sat, 19 Oct 2024 00:09:32 GMT
+ Content-Type:
+ - application/xml
+ Content-Length:
+ - '805'
+ Connection:
+ - keep-alive
+ Vary:
+ - Access-Control-Request-Headers
+ - Access-Control-Request-Method
+ - Origin
+ Set-Cookie:
+ - JSESSIONID=E723F5E64299E095C333FC7B57C0EB1E; Path=/cspace-services; Secure;
+ HttpOnly
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Cache-Control:
+ - no-cache, no-store, max-age=0, must-revalidate
+ Pragma:
+ - no-cache
+ Expires:
+ - '0'
+ Strict-Transport-Security:
+ - max-age=31536000 ; includeSubDomains
+ X-Frame-Options:
+ - DENY
+ body:
+ encoding: UTF-8
+ string: 0112csid|uri|refName|updatedAt|workflowState|rev|shortIdentifier|sas|displayName|vocabType4bea28cb-83be-4ca1-971b/personauthorities/4bea28cb-83be-4ca1-971burn:cspace:core.collectionspace.org:personauthorities:name(person)'Local
+ Persons'2023-08-26T05:44:37.565Zproject13personLocal
+ PersonsPersonAuthority
+ recorded_at: Sat, 19 Oct 2024 00:09:32 GMT
+- request:
+ method: get
+ uri: https://core.dev.collectionspace.org/cspace-services/vocabularies/urn:cspace:name(publishto)/items?as=vocabularyitems_common:displayName%20=%20%27alL%27&pgSz=25&sortBy=collectionspace_core:updatedAt%20DESC&wf_deleted=false
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Authorization:
+ - Basic YWRtaW5AY29yZS5jb2xsZWN0aW9uc3BhY2Uub3JnOkFkbWluaXN0cmF0b3I=
+ response:
+ status:
+ code: 200
+ message: ''
+ headers:
+ Date:
+ - Sat, 19 Oct 2024 00:09:33 GMT
+ Content-Type:
+ - application/xml
+ Content-Length:
+ - '445'
+ Connection:
+ - keep-alive
+ Vary:
+ - Access-Control-Request-Headers
+ - Access-Control-Request-Method
+ - Origin
+ Set-Cookie:
+ - JSESSIONID=5703A73AE43342059484EF6DE4077EF0; Path=/cspace-services; Secure;
+ HttpOnly
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Cache-Control:
+ - no-cache, no-store, max-age=0, must-revalidate
+ Pragma:
+ - no-cache
+ Expires:
+ - '0'
+ Strict-Transport-Security:
+ - max-age=31536000 ; includeSubDomains
+ X-Frame-Options:
+ - DENY
+ body:
+ encoding: UTF-8
+ string: 02500csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|referenced|deprecated|termStatus|description|source|order|displayName|shortIdentifier
+ recorded_at: Sat, 19 Oct 2024 00:09:33 GMT
+- request:
+ method: get
+ uri: https://core.dev.collectionspace.org/cspace-services/vocabularies/urn:cspace:name(publishto)/items?as=vocabularyitems_common:displayName%20ILIKE%20%27alL%27&pgSz=25&sortBy=collectionspace_core:updatedAt%20DESC&wf_deleted=false
+ body:
+ encoding: US-ASCII
+ string: ''
+ headers:
+ Accept-Encoding:
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
+ Accept:
+ - "*/*"
+ User-Agent:
+ - Ruby
+ Authorization:
+ - Basic YWRtaW5AY29yZS5jb2xsZWN0aW9uc3BhY2Uub3JnOkFkbWluaXN0cmF0b3I=
+ response:
+ status:
+ code: 200
+ message: ''
+ headers:
+ Date:
+ - Sat, 19 Oct 2024 00:09:33 GMT
+ Content-Type:
+ - application/xml
+ Content-Length:
+ - '891'
+ Connection:
+ - keep-alive
+ Vary:
+ - Access-Control-Request-Headers
+ - Access-Control-Request-Method
+ - Origin
+ Set-Cookie:
+ - JSESSIONID=12AC3BF6A209CF8FC3622F4212CF4258; Path=/cspace-services; Secure;
+ HttpOnly
+ X-Content-Type-Options:
+ - nosniff
+ X-Xss-Protection:
+ - 1; mode=block
+ Cache-Control:
+ - no-cache, no-store, max-age=0, must-revalidate
+ Pragma:
+ - no-cache
+ Expires:
+ - '0'
+ Strict-Transport-Security:
+ - max-age=31536000 ; includeSubDomains
+ X-Frame-Options:
+ - DENY
+ body:
+ encoding: UTF-8
+ string: 02511csid|uri|refName|updatedAt|workflowState|rev|sourcePage|sas|proposed|referenced|deprecated|termStatus|description|source|order|displayName|shortIdentifier1be6baa8-3691-4fad-bf01/vocabularies/8fd03497-36d2-4d91-a46b/items/1be6baa8-3691-4fad-bf01urn:cspace:core.collectionspace.org:vocabularies:name(publishto):item:name(all)'All'2023-08-26T05:43:29.142Zproject0falsetrueAllall
+ recorded_at: Sat, 19 Oct 2024 00:09:33 GMT
+recorded_with: VCR 6.1.0