qa 3.1.0 → 4.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +16 -548
- data/app/controllers/qa/linked_data_terms_controller.rb +64 -42
- data/app/controllers/qa/terms_controller.rb +14 -6
- data/app/models/qa/iri_template/url_config.rb +47 -0
- data/app/models/qa/iri_template/variable_map.rb +62 -0
- data/app/models/qa/linked_data/config/context_map.rb +77 -0
- data/app/models/qa/linked_data/config/context_property_map.rb +144 -0
- data/app/models/qa/linked_data/config/helper.rb +34 -0
- data/app/services/qa/iri_template_service.rb +31 -0
- data/{lib/qa/authorities → app/services/qa}/linked_data/authority_service.rb +3 -4
- data/app/services/qa/linked_data/authority_url_service.rb +48 -0
- data/app/services/qa/linked_data/deep_sort_service.rb +238 -0
- data/app/services/qa/linked_data/graph_service.rb +106 -0
- data/app/services/qa/linked_data/language_service.rb +30 -0
- data/app/services/qa/linked_data/language_sort_service.rb +81 -0
- data/app/services/qa/linked_data/mapper/context_mapper_service.rb +59 -0
- data/app/services/qa/linked_data/mapper/graph_mapper_service.rb +40 -0
- data/app/services/qa/linked_data/mapper/search_results_mapper_service.rb +70 -0
- data/config/authorities/linked_data/loc.json +5 -2
- data/config/authorities/linked_data/oclc_fast.json +3 -2
- data/config/initializers/linked_data_authorities.rb +1 -1
- data/config/locales/qa.en.yml +9 -0
- data/lib/generators/qa/install/templates/config/initializers/qa.rb +4 -0
- data/lib/qa.rb +8 -0
- data/lib/qa/authorities/assign_fast/generic_authority.rb +1 -1
- data/lib/qa/authorities/base.rb +0 -11
- data/lib/qa/authorities/crossref/generic_authority.rb +1 -1
- data/lib/qa/authorities/geonames.rb +1 -1
- data/lib/qa/authorities/getty/aat.rb +7 -2
- data/lib/qa/authorities/getty/tgn.rb +7 -2
- data/lib/qa/authorities/getty/ulan.rb +7 -2
- data/lib/qa/authorities/linked_data.rb +0 -1
- data/lib/qa/authorities/linked_data/config.rb +29 -28
- data/lib/qa/authorities/linked_data/config/search_config.rb +21 -79
- data/lib/qa/authorities/linked_data/config/term_config.rb +7 -77
- data/lib/qa/authorities/linked_data/find_term.rb +25 -17
- data/lib/qa/authorities/linked_data/generic_authority.rb +6 -5
- data/lib/qa/authorities/linked_data/rdf_helper.rb +6 -73
- data/lib/qa/authorities/linked_data/search_query.rb +54 -101
- data/lib/qa/authorities/loc/generic_authority.rb +4 -4
- data/lib/qa/authorities/web_service_base.rb +1 -8
- data/lib/qa/configuration.rb +7 -0
- data/lib/qa/version.rb +1 -1
- data/lib/tasks/mesh.rake +19 -18
- data/spec/controllers/linked_data_terms_controller_spec.rb +51 -1
- data/spec/controllers/terms_controller_spec.rb +15 -15
- data/spec/fixtures/authorities/linked_data/lod_encoding_config.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_full_config.json +56 -2
- data/spec/fixtures/authorities/linked_data/lod_full_config_1_0.json +164 -0
- data/spec/fixtures/authorities/linked_data/lod_lang_defaults.json +5 -4
- data/spec/fixtures/authorities/linked_data/lod_lang_multi_defaults.json +3 -2
- data/spec/fixtures/authorities/linked_data/lod_lang_no_defaults.json +3 -2
- data/spec/fixtures/authorities/linked_data/lod_lang_param.json +3 -2
- data/spec/fixtures/authorities/linked_data/lod_min_config.json +3 -2
- data/spec/fixtures/authorities/linked_data/lod_search_only_config.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_sort.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_term_id_param_config.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_term_only_config.json +2 -1
- data/spec/fixtures/authorities/linked_data/lod_term_uri_param_config.json +2 -1
- data/spec/fixtures/getty-error-response.txt +10 -0
- data/spec/fixtures/lod_2_ranked_2_unranked.nt +17 -0
- data/spec/fixtures/lod_3_ranked_varying_preds.nt +16 -0
- data/spec/fixtures/lod_lang_search_filtering.nt +11 -0
- data/spec/fixtures/lod_search_with_blanknode_subjects.nt +18 -0
- data/spec/fixtures/lod_term_with_blanknode_objects.nt +8 -0
- data/spec/lib/authorities/assign_fast_spec.rb +1 -0
- data/spec/lib/authorities/getty/aat_spec.rb +14 -2
- data/spec/lib/authorities/getty/tgn_spec.rb +14 -2
- data/spec/lib/authorities/getty/ulan_spec.rb +14 -2
- data/spec/lib/authorities/linked_data/authority_service_spec.rb +2 -1
- data/spec/lib/authorities/linked_data/config_spec.rb +284 -5
- data/spec/lib/authorities/linked_data/find_term_spec.rb +3 -1
- data/spec/lib/authorities/linked_data/generic_authority_spec.rb +92 -42
- data/spec/lib/authorities/linked_data/search_config_spec.rb +67 -160
- data/spec/lib/authorities/linked_data/search_query_spec.rb +3 -127
- data/spec/lib/authorities/linked_data/term_config_spec.rb +6 -134
- data/spec/lib/authorities/loc_spec.rb +9 -9
- data/spec/lib/configuration_spec.rb +20 -7
- data/spec/lib/tasks/mesh.rake_spec.rb +2 -2
- data/spec/models/iri_template/url_config_spec.rb +102 -0
- data/spec/models/iri_template/variable_map_spec.rb +105 -0
- data/spec/models/linked_data/config/context_map_spec.rb +148 -0
- data/spec/models/linked_data/config/context_property_map_spec.rb +286 -0
- data/spec/services/iri_template_service_spec.rb +69 -0
- data/spec/services/linked_data/authority_url_service_spec.rb +107 -0
- data/spec/services/linked_data/deep_sort_service_spec.rb +260 -0
- data/spec/services/linked_data/graph_service_spec.rb +232 -0
- data/spec/services/linked_data/language_service_spec.rb +66 -0
- data/spec/services/linked_data/language_sort_service_spec.rb +58 -0
- data/spec/services/linked_data/mapper/context_mapper_service_spec.rb +137 -0
- data/spec/services/linked_data/mapper/graph_mapper_service_spec.rb +110 -0
- data/spec/services/linked_data/mapper/search_results_mapper_service_spec.rb +109 -0
- data/spec/spec_helper.rb +10 -2
- metadata +81 -11
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Qa::IriTemplateService do
|
4
|
+
let(:url_template) do
|
5
|
+
{
|
6
|
+
:'@context' => 'http://www.w3.org/ns/hydra/context.jsonld',
|
7
|
+
:'@type' => 'IriTemplate',
|
8
|
+
template: 'http://localhost/test_default/search?{?subauth}&{?query}&{?max_records}&{?language}',
|
9
|
+
variableRepresentation: 'BasicRepresentation',
|
10
|
+
mapping: [
|
11
|
+
{
|
12
|
+
:'@type' => 'IriTemplateMapping',
|
13
|
+
variable: 'query',
|
14
|
+
property: 'hydra:freetextQuery',
|
15
|
+
required: true
|
16
|
+
},
|
17
|
+
{
|
18
|
+
:'@type' => 'IriTemplateMapping',
|
19
|
+
variable: 'subauth',
|
20
|
+
property: 'hydra:freetextQuery',
|
21
|
+
required: false,
|
22
|
+
default: 'personal_names'
|
23
|
+
},
|
24
|
+
{
|
25
|
+
:'@type' => 'IriTemplateMapping',
|
26
|
+
variable: 'max_records',
|
27
|
+
property: 'hydra:freetextQuery',
|
28
|
+
required: false,
|
29
|
+
default: 20
|
30
|
+
},
|
31
|
+
{
|
32
|
+
:'@type' => 'IriTemplateMapping',
|
33
|
+
variable: 'language',
|
34
|
+
property: 'hydra:freetextQuery',
|
35
|
+
required: false,
|
36
|
+
default: 'en'
|
37
|
+
}
|
38
|
+
]
|
39
|
+
}
|
40
|
+
end
|
41
|
+
let(:url_config) { Qa::IriTemplate::UrlConfig.new(url_template) }
|
42
|
+
|
43
|
+
describe '.build_url' do
|
44
|
+
context 'when all substitutions specified' do
|
45
|
+
let(:substitutions) do
|
46
|
+
HashWithIndifferentAccess.new(
|
47
|
+
query: 'mark twain',
|
48
|
+
subauth: 'corporate_names',
|
49
|
+
max_records: 10,
|
50
|
+
language: 'fr'
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'returns template with substitutions' do
|
55
|
+
expected_url = 'http://localhost/test_default/search?subauth=corporate_names&query=mark twain&max_records=10&language=fr'
|
56
|
+
expect(described_class.build_url(url_config: url_config, substitutions: substitutions)).to eq expected_url
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when minimal substitutions specified' do
|
61
|
+
let(:substitutions) { HashWithIndifferentAccess.new(query: 'mark twain') }
|
62
|
+
|
63
|
+
it 'returns template with substitutions' do
|
64
|
+
expected_url = 'http://localhost/test_default/search?subauth=personal_names&query=mark twain&max_records=20&language=en'
|
65
|
+
expect(described_class.build_url(url_config: url_config, substitutions: substitutions)).to eq expected_url
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Qa::LinkedData::AuthorityUrlService do
|
4
|
+
let(:authority) { :OCLC_FAST }
|
5
|
+
let(:search_config) { Qa::Authorities::LinkedData::Config.new(authority).search }
|
6
|
+
let(:term_config) { Qa::Authorities::LinkedData::Config.new(authority).term }
|
7
|
+
let(:action_config) { search_config }
|
8
|
+
|
9
|
+
let(:subauthority) { nil }
|
10
|
+
let(:action) { :search }
|
11
|
+
let(:action_request) { "mark+twain" }
|
12
|
+
let(:substitutions) do
|
13
|
+
{}
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.build_url' do
|
17
|
+
context 'when authority is not registered' do
|
18
|
+
let(:authority) { :BAD_AUTHORITY }
|
19
|
+
|
20
|
+
it 'raises error' do
|
21
|
+
expected_error = Qa::InvalidLinkedDataAuthority
|
22
|
+
expected_error_message = "Unable to initialize linked data authority 'BAD_AUTHORITY'"
|
23
|
+
expect { described_class.build_url(action_config: action_config, subauthority: subauthority, action: action, action_request: action_request, substitutions: substitutions) }
|
24
|
+
.to raise_error(expected_error, expected_error_message)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# TODO: elr - currently uses the default subauthority if the one passed in isn't supported
|
29
|
+
context 'when subauthority is not supported' do
|
30
|
+
let(:subauthority) { :BAD_SUBAUTHORITY }
|
31
|
+
|
32
|
+
it 'raises error' do
|
33
|
+
skip "Pending better handling of unsupported subauthorities"
|
34
|
+
expected_error = Qa::InvalidLinkedDataAuthority
|
35
|
+
expected_error_message = "Unable to initialize linked data sub-authority BAD_SUBAUTHORITY"
|
36
|
+
expect { described_class.build_url(action_config: action_config, subauthority: subauthority, action: action, action_request: action_request, substitutions: substitutions) }
|
37
|
+
.to raise_error(expected_error, expected_error_message)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when invalid action is specified' do
|
42
|
+
let(:action) { :BAD_ACTION }
|
43
|
+
|
44
|
+
it 'raises error' do
|
45
|
+
expected_error = Qa::UnsupportedAction
|
46
|
+
expected_error_message = "BAD_ACTION Not Supported - Action must be one of the supported actions (e.g. :term, :search)"
|
47
|
+
expect { described_class.build_url(action_config: action_config, subauthority: subauthority, action: action, action_request: action_request, substitutions: substitutions) }
|
48
|
+
.to raise_error(expected_error, expected_error_message)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when action_request is missing' do
|
53
|
+
let(:action_request) { nil }
|
54
|
+
|
55
|
+
it 'raises error' do
|
56
|
+
expected_error = Qa::IriTemplate::MissingParameter
|
57
|
+
expected_error_message = "query is required, but missing"
|
58
|
+
expect { described_class.build_url(action_config: action_config, subauthority: subauthority, action: action, action_request: action_request, substitutions: substitutions) }
|
59
|
+
.to raise_error(expected_error, expected_error_message)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
subject do
|
64
|
+
described_class.build_url(action_config: action_config, subauthority: subauthority, action: action, action_request: action_request, substitutions: substitutions)
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when no errors' do
|
68
|
+
context 'and performing search action' do
|
69
|
+
context 'and all substitutions specified' do
|
70
|
+
let(:substitutions) do
|
71
|
+
HashWithIndifferentAccess.new(
|
72
|
+
maximumRecords: 10,
|
73
|
+
language: 'fr'
|
74
|
+
)
|
75
|
+
end
|
76
|
+
let(:subauthority) { 'personal_name' }
|
77
|
+
let(:action_request) { 'mark twain' }
|
78
|
+
|
79
|
+
it 'returns template with substitutions' do
|
80
|
+
expected_url = 'http://experimental.worldcat.org/fast/search?query=oclc.personalName+all+%22mark twain%22&sortKeys=usage&maximumRecords=10'
|
81
|
+
expect(subject).to eq expected_url
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when no substitutions specified' do
|
86
|
+
let(:action_request) { 'mark twain' }
|
87
|
+
|
88
|
+
it 'returns template with substitutions' do
|
89
|
+
expected_url = 'http://experimental.worldcat.org/fast/search?query=cql.any+all+%22mark twain%22&sortKeys=usage&maximumRecords=20'
|
90
|
+
expect(subject).to eq expected_url
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'and performing term action' do
|
96
|
+
let(:action) { :term }
|
97
|
+
let(:action_config) { term_config }
|
98
|
+
let(:action_request) { 'n79021164' }
|
99
|
+
|
100
|
+
it 'returns template with substitutions' do
|
101
|
+
expected_url = 'http://id.worldcat.org/fast/n79021164'
|
102
|
+
expect(subject).to eq expected_url
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Qa::LinkedData::DeepSortService do
|
4
|
+
describe "#deep_sort" do
|
5
|
+
subject { described_class.new(the_array, :sort, preferred_language).sort }
|
6
|
+
|
7
|
+
let(:preferred_language) { nil }
|
8
|
+
|
9
|
+
context 'as numeric sort' do
|
10
|
+
context 'when sort values are integers' do
|
11
|
+
let(:term_1) { RDF::Literal.new(1) }
|
12
|
+
let(:term_2) { RDF::Literal.new(15) }
|
13
|
+
let(:term_3) { RDF::Literal.new(20) }
|
14
|
+
let(:the_array) do
|
15
|
+
[{ sort: [term_2] },
|
16
|
+
{ sort: [term_3] },
|
17
|
+
{ sort: [term_1] }]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "does numeric sort" do
|
21
|
+
expected_results = [{ sort: [term_1] },
|
22
|
+
{ sort: [term_2] },
|
23
|
+
{ sort: [term_3] }]
|
24
|
+
expect(subject).to eq expected_results
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'when sort values are string representations of integers' do
|
29
|
+
let(:term_1) { RDF::Literal.new('1') }
|
30
|
+
let(:term_2) { RDF::Literal.new('15') }
|
31
|
+
let(:term_3) { RDF::Literal.new('20') }
|
32
|
+
let(:the_array) do
|
33
|
+
[{ sort: [term_2] },
|
34
|
+
{ sort: [term_3] },
|
35
|
+
{ sort: [term_1] }]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "does numeric sort" do
|
39
|
+
expected_results = [{ sort: [term_1] },
|
40
|
+
{ sort: [term_2] },
|
41
|
+
{ sort: [term_3] }]
|
42
|
+
expect(subject).to eq expected_results
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when sort values are a mix of integer representations' do
|
47
|
+
let(:term_1) { RDF::Literal.new(1) }
|
48
|
+
let(:term_2) { RDF::Literal.new('15') }
|
49
|
+
let(:term_3) { RDF::Literal.new(20) }
|
50
|
+
let(:term_4) { RDF::Literal.new('201') }
|
51
|
+
let(:the_array) do
|
52
|
+
[{ sort: [term_2] },
|
53
|
+
{ sort: [term_3] },
|
54
|
+
{ sort: [term_1] },
|
55
|
+
{ sort: [term_4] }]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "does numeric sort" do
|
59
|
+
expected_results = [{ sort: [term_1] },
|
60
|
+
{ sort: [term_2] },
|
61
|
+
{ sort: [term_3] },
|
62
|
+
{ sort: [term_4] }]
|
63
|
+
expect(subject).to eq expected_results
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'as single value sort' do
|
69
|
+
context 'when sort values do not have the language markers' do
|
70
|
+
let(:term_1) { RDF::Literal.new("apple") }
|
71
|
+
let(:term_2) { RDF::Literal.new("Banana") }
|
72
|
+
let(:term_3) { RDF::Literal.new("carrot") }
|
73
|
+
let(:the_array) do
|
74
|
+
[{ sort: [term_2] },
|
75
|
+
{ sort: [term_3] },
|
76
|
+
{ sort: [term_1] }]
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'does alpha sort ignoring case' do
|
80
|
+
expected_results = [{ sort: [term_1] },
|
81
|
+
{ sort: [term_2] },
|
82
|
+
{ sort: [term_3] }]
|
83
|
+
expect(subject).to eq expected_results
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'when sort values all have the same language marker' do
|
88
|
+
let(:term_1) { RDF::Literal.new("apple", language: :en) }
|
89
|
+
let(:term_2) { RDF::Literal.new("Banana", language: :en) }
|
90
|
+
let(:term_3) { RDF::Literal.new("carrot", language: :en) }
|
91
|
+
let(:the_array) do
|
92
|
+
[{ sort: [term_2] },
|
93
|
+
{ sort: [term_3] },
|
94
|
+
{ sort: [term_1] }]
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'does alpha sort ignoring case' do
|
98
|
+
expected_results = [{ sort: [term_1] },
|
99
|
+
{ sort: [term_2] },
|
100
|
+
{ sort: [term_3] }]
|
101
|
+
expect(subject).to eq expected_results
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'when sort values all have different language markers and no preferred language' do
|
106
|
+
let(:term_1) { RDF::Literal.new("Kuh", language: :de) }
|
107
|
+
let(:term_2) { RDF::Literal.new("Rind", language: :de) }
|
108
|
+
let(:term_3) { RDF::Literal.new("bovine", language: :en) }
|
109
|
+
let(:term_4) { RDF::Literal.new("cow", language: :en) }
|
110
|
+
let(:term_5) { RDF::Literal.new("vache", language: :fr) }
|
111
|
+
let(:term_6) { RDF::Literal.new("mucca") }
|
112
|
+
let(:term_7) { RDF::Literal.new("vaca") }
|
113
|
+
let(:the_array) do
|
114
|
+
[{ sort: [term_5] },
|
115
|
+
{ sort: [term_7] },
|
116
|
+
{ sort: [term_2] },
|
117
|
+
{ sort: [term_6] },
|
118
|
+
{ sort: [term_1] },
|
119
|
+
{ sort: [term_4] },
|
120
|
+
{ sort: [term_3] }]
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'does alpha sort of languages and alpha sort of terms within each language' do
|
124
|
+
expected_results = [{ sort: [term_1] },
|
125
|
+
{ sort: [term_2] },
|
126
|
+
{ sort: [term_3] },
|
127
|
+
{ sort: [term_4] },
|
128
|
+
{ sort: [term_5] },
|
129
|
+
{ sort: [term_6] },
|
130
|
+
{ sort: [term_7] }]
|
131
|
+
expect(subject).to eq expected_results
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'when sort values all have different language markers and has preferred language' do
|
136
|
+
let(:preferred_language) { :en }
|
137
|
+
|
138
|
+
let(:term_1) { RDF::Literal.new("bovine", language: :en) }
|
139
|
+
let(:term_2) { RDF::Literal.new("cow", language: :en) }
|
140
|
+
let(:term_3) { RDF::Literal.new("Kuh", language: :de) }
|
141
|
+
let(:term_4) { RDF::Literal.new("Rind", language: :de) }
|
142
|
+
let(:term_5) { RDF::Literal.new("vache", language: :fr) }
|
143
|
+
let(:term_6) { RDF::Literal.new("mucca") }
|
144
|
+
let(:term_7) { RDF::Literal.new("vaca") }
|
145
|
+
let(:the_array) do
|
146
|
+
[{ sort: [term_5] },
|
147
|
+
{ sort: [term_7] },
|
148
|
+
{ sort: [term_2] },
|
149
|
+
{ sort: [term_6] },
|
150
|
+
{ sort: [term_1] },
|
151
|
+
{ sort: [term_4] },
|
152
|
+
{ sort: [term_3] }]
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'does alpha sort of languages shifting perferred language to the front and alpha sort of terms within each language' do
|
156
|
+
expected_results = [{ sort: [term_1] },
|
157
|
+
{ sort: [term_2] },
|
158
|
+
{ sort: [term_3] },
|
159
|
+
{ sort: [term_4] },
|
160
|
+
{ sort: [term_5] },
|
161
|
+
{ sort: [term_6] },
|
162
|
+
{ sort: [term_7] }]
|
163
|
+
expect(subject).to eq expected_results
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'as multiple value sort' do
|
169
|
+
context 'when all the terms in both lists have no language markers' do
|
170
|
+
let(:term_1) { RDF::Literal.new("apple") }
|
171
|
+
let(:term_2) { RDF::Literal.new("Banana") }
|
172
|
+
let(:term_3) { RDF::Literal.new("carrot") }
|
173
|
+
let(:term_4) { RDF::Literal.new("Woof Woof") }
|
174
|
+
let(:the_array) do
|
175
|
+
[{ sort: [term_2, term_1] },
|
176
|
+
{ sort: [term_3, term_2] },
|
177
|
+
{ sort: [term_1, term_3, term_4, term_2] }]
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'does alpha sort ignoring case' do
|
181
|
+
expected_results = [{ sort: [term_1, term_2] },
|
182
|
+
{ sort: [term_1, term_2, term_3, term_4] },
|
183
|
+
{ sort: [term_2, term_3] }]
|
184
|
+
expect(subject).to eq expected_results
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'when all the terms in both lists have the same language markers' do
|
189
|
+
let(:term_1) { RDF::Literal.new("bovine", language: :en) }
|
190
|
+
let(:term_2) { RDF::Literal.new("Cow", language: :en) }
|
191
|
+
let(:term_3) { RDF::Literal.new("hefer", language: :en) }
|
192
|
+
let(:term_4) { RDF::Literal.new("Moo Moo", language: :en) }
|
193
|
+
let(:the_array) do
|
194
|
+
[{ sort: [term_2, term_1] },
|
195
|
+
{ sort: [term_3, term_2] },
|
196
|
+
{ sort: [term_1, term_3, term_4, term_2] }]
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'does alpha sort ignoring case' do
|
200
|
+
expected_results = [{ sort: [term_1, term_2] },
|
201
|
+
{ sort: [term_1, term_2, term_3, term_4] },
|
202
|
+
{ sort: [term_2, term_3] }]
|
203
|
+
expect(subject).to eq expected_results
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'when terms exist in both lists that match the preferred language' do
|
208
|
+
let(:preferred_language) { :en }
|
209
|
+
|
210
|
+
let(:term_1) { RDF::Literal.new("bovine", language: :en) }
|
211
|
+
let(:term_2) { RDF::Literal.new("cow", language: :en) }
|
212
|
+
let(:term_3) { RDF::Literal.new("heffer", language: :en) }
|
213
|
+
let(:term_4) { RDF::Literal.new("Kuh", language: :de) }
|
214
|
+
let(:term_5) { RDF::Literal.new("Rind", language: :de) }
|
215
|
+
let(:term_6) { RDF::Literal.new("vache", language: :fr) }
|
216
|
+
let(:term_7) { RDF::Literal.new("mucca") }
|
217
|
+
let(:term_8) { RDF::Literal.new("vaca") }
|
218
|
+
let(:the_array) do
|
219
|
+
[{ sort: [term_4, term_5, term_2, term_1] },
|
220
|
+
{ sort: [term_3, term_5, term_4, term_7, term_2] },
|
221
|
+
{ sort: [term_1, term_6, term_4, term_2, term_5, term_8, term_7, term_3] }]
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'does alpha sort ignoring case on the preferred language only' do
|
225
|
+
expected_results = [{ sort: [term_1, term_2, term_4, term_5] },
|
226
|
+
{ sort: [term_1, term_2, term_3, term_4, term_5, term_6, term_7, term_8] },
|
227
|
+
{ sort: [term_2, term_3, term_4, term_5, term_7] }]
|
228
|
+
expect(subject).to eq expected_results
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'when there is complete chaos under heaven and all things are rotten' do
|
233
|
+
let(:preferred_language) { :en }
|
234
|
+
|
235
|
+
let(:term_1) { RDF::Literal.new("Kuh", language: :de) }
|
236
|
+
let(:term_2) { RDF::Literal.new("Rind", language: :de) }
|
237
|
+
let(:term_3) { RDF::Literal.new("hembra", language: :es) }
|
238
|
+
let(:term_4) { RDF::Literal.new("res vacuna", language: :es) }
|
239
|
+
let(:term_5) { RDF::Literal.new("vaca", language: :es) }
|
240
|
+
let(:term_6) { RDF::Literal.new("vache", language: :fr) }
|
241
|
+
let(:term_7) { RDF::Literal.new("ko") }
|
242
|
+
let(:term_8) { RDF::Literal.new("mucca") }
|
243
|
+
let(:the_array) do
|
244
|
+
[{ sort: [term_4, term_5, term_2, term_1] }, # res vacuna, vaca, Rind, Kuh
|
245
|
+
{ sort: [term_3, term_5, term_4, term_7, term_2] }, # hembra, vaca, res vacuna, ko, Rind
|
246
|
+
{ sort: [term_3, term_6, term_5] }, # hembra, vache, vaca
|
247
|
+
{ sort: [term_1, term_6, term_4, term_2, term_5, term_8, term_7, term_3] }] # Kuh, vache, res vacuna, Rind, vaca, mucca, ko, hembra
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'does alpha sort ignoring case on the preferred language only' do
|
251
|
+
expected_results = [{ sort: [term_1, term_2, term_3, term_4, term_5, term_6, term_7, term_8] }, # Kuh, Rind, hembra, res vacuna, vaca, vache, ko, mucca
|
252
|
+
{ sort: [term_2, term_3, term_4, term_5, term_7] }, # Kuh, Rind, res vacuna, vaca
|
253
|
+
{ sort: [term_3, term_5, term_6] }, # hembra, vaca, vache
|
254
|
+
{ sort: [term_1, term_2, term_4, term_5] }] # Rind, hembra, res vacuna, vaca, mucca
|
255
|
+
expect(subject).to eq expected_results
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Qa::LinkedData::GraphService do
|
4
|
+
describe '.load_graph' do
|
5
|
+
subject { described_class.load_graph(url: url) }
|
6
|
+
let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
|
7
|
+
|
8
|
+
context 'when graph can be loaded' do
|
9
|
+
before do
|
10
|
+
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
|
11
|
+
.to_return(status: 200, body: webmock_fixture('lod_oclc_all_query_3_results.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'builds a graph with many statements' do
|
15
|
+
expect(subject).to be_kind_of RDF::Graph
|
16
|
+
expect(subject.statements.size).to be > 10
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when term is not found' do
|
21
|
+
let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
|
22
|
+
|
23
|
+
before do
|
24
|
+
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
|
25
|
+
.to_return(status: 404)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises error' do
|
29
|
+
expect { described_class.load_graph(url: url) }.to raise_error(Qa::TermNotFound, "#{url} Not Found - Term may not exist at LOD Authority. (HTTPNotFound - 404)")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when service error' do
|
34
|
+
subject { described_class.load_graph(url: url) }
|
35
|
+
|
36
|
+
let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
|
37
|
+
let(:uri) { URI(url) }
|
38
|
+
|
39
|
+
before do
|
40
|
+
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
|
41
|
+
.to_return(status: 500)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'raises error' do
|
45
|
+
expect { subject }.to raise_error(Qa::ServiceError, "#{uri.hostname} on port #{uri.port} is not responding. Try again later. (HTTPServerError - 500)")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when service unavailable' do
|
50
|
+
subject { described_class.load_graph(url: url) }
|
51
|
+
|
52
|
+
let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
|
53
|
+
let(:uri) { URI(url) }
|
54
|
+
|
55
|
+
before do
|
56
|
+
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
|
57
|
+
.to_return(status: 503)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'raises error' do
|
61
|
+
expect { subject }.to raise_error(Qa::ServiceUnavailable, "#{uri.hostname} on port #{uri.port} is not responding. Try again later. (HTTPServiceUnavailable - 503)")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when error isn't specifically handled" do
|
66
|
+
subject { described_class.load_graph(url: url) }
|
67
|
+
|
68
|
+
let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
|
69
|
+
let(:uri) { URI(url) }
|
70
|
+
|
71
|
+
before do
|
72
|
+
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
|
73
|
+
.to_return(status: 504)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'raises error' do
|
77
|
+
expect { subject }.to raise_error(Qa::ServiceError, "Unknown error for #{uri.hostname} on port #{uri.port}. Try again later. (Cause - <#{url}>: (504))")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '.filter' do
|
83
|
+
context 'with language filter' do
|
84
|
+
subject { described_class.filter(graph: graph, language: language) }
|
85
|
+
|
86
|
+
let(:url) { 'http://authority.with.language/search?query=foo' }
|
87
|
+
let(:graph) { described_class.load_graph(url: url) }
|
88
|
+
|
89
|
+
let(:en_dried_milk) { RDF::Literal.new("dried milk", language: :en) }
|
90
|
+
let(:fr_dried_milk) { RDF::Literal.new("lait en poudre", language: :fr) }
|
91
|
+
let(:de_dried_milk) { RDF::Literal.new("getrocknete Milch", language: :de) }
|
92
|
+
|
93
|
+
let(:en_buttermilk) { RDF::Literal.new("buttermilk", language: :en) }
|
94
|
+
let(:fr_buttermilk) { RDF::Literal.new("Babeurre", language: :fr) }
|
95
|
+
let(:de_buttermilk) { RDF::Literal.new("Buttermilch", language: :de) }
|
96
|
+
|
97
|
+
let(:en_condensed_milk) { RDF::Literal.new("condensed milk", language: :en) }
|
98
|
+
let(:fr_condensed_milk) { RDF::Literal.new("lait condensé", language: :fr) }
|
99
|
+
let(:de_condensed_milk) { RDF::Literal.new("Kondensmilch", language: :de) }
|
100
|
+
|
101
|
+
before do
|
102
|
+
stub_request(:get, 'http://authority.with.language/search?query=foo')
|
103
|
+
.to_return(status: 200, body: webmock_fixture('lod_lang_search_enfrde.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'when one language passed in' do
|
107
|
+
let(:language) { [:fr] }
|
108
|
+
|
109
|
+
it 'returns the graph with only the French subset of triples' do
|
110
|
+
expect(subject.has_object?(en_dried_milk)).to be false
|
111
|
+
expect(subject.has_object?(en_buttermilk)).to be false
|
112
|
+
expect(subject.has_object?(en_condensed_milk)).to be false
|
113
|
+
|
114
|
+
expect(subject.has_object?(fr_dried_milk)).to be true
|
115
|
+
expect(subject.has_object?(fr_buttermilk)).to be true
|
116
|
+
expect(subject.has_object?(fr_condensed_milk)).to be true
|
117
|
+
|
118
|
+
expect(subject.has_object?(de_dried_milk)).to be false
|
119
|
+
expect(subject.has_object?(de_buttermilk)).to be false
|
120
|
+
expect(subject.has_object?(de_condensed_milk)).to be false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "when mix of language markers on graph statements" do
|
125
|
+
let(:nomarker_de_buttermilk) { RDF::Literal.new("Buttermilch") }
|
126
|
+
let(:language) { [:fr] }
|
127
|
+
|
128
|
+
before do
|
129
|
+
stub_request(:get, 'http://authority.with.language/search?query=foo')
|
130
|
+
.to_return(status: 200, body: webmock_fixture('lod_lang_search_filtering.nt'), headers: { 'Content-Type' => 'application/n-triples' })
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'filters down to the expected size' do
|
134
|
+
expect(graph.size).to eq 11
|
135
|
+
expect(subject.size).to eq 8
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'the graph includes triples where object has the targeted language marker (e.g. :fr)' do
|
139
|
+
expect(subject.has_object?(fr_dried_milk)).to be true
|
140
|
+
expect(subject.has_object?(fr_buttermilk)).to be true
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'the graph includes triples where object does not have a language marker' do
|
144
|
+
expect(subject.has_object?(nomarker_de_buttermilk)).to be true
|
145
|
+
end
|
146
|
+
|
147
|
+
it "the graph includes triples regardless of language when there are no object's for the predicate that have the targeted language marker" do
|
148
|
+
expect(subject.has_object?(en_condensed_milk)).to be true
|
149
|
+
expect(subject.has_object?(de_condensed_milk)).to be true
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'filters out the rest' do
|
153
|
+
expect(subject.has_object?(en_dried_milk)).to be false
|
154
|
+
expect(subject.has_object?(en_buttermilk)).to be false
|
155
|
+
expect(subject.has_object?(de_dried_milk)).to be false
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'when multiple languages passed in' do
|
160
|
+
let(:language) { [:en, :fr] }
|
161
|
+
|
162
|
+
it 'returns the graph with English and French subset of triples' do
|
163
|
+
expect(subject.has_object?(en_dried_milk)).to be true
|
164
|
+
expect(subject.has_object?(en_buttermilk)).to be true
|
165
|
+
expect(subject.has_object?(en_condensed_milk)).to be true
|
166
|
+
|
167
|
+
expect(subject.has_object?(fr_dried_milk)).to be true
|
168
|
+
expect(subject.has_object?(fr_buttermilk)).to be true
|
169
|
+
expect(subject.has_object?(fr_condensed_milk)).to be true
|
170
|
+
|
171
|
+
expect(subject.has_object?(de_dried_milk)).to be false
|
172
|
+
expect(subject.has_object?(de_buttermilk)).to be false
|
173
|
+
expect(subject.has_object?(de_condensed_milk)).to be false
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'with filter out subject blanknodes' do
|
179
|
+
subject { described_class.filter(graph: graph, remove_blanknode_subjects: true) }
|
180
|
+
|
181
|
+
let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
|
182
|
+
let(:graph) { described_class.load_graph(url: url) }
|
183
|
+
|
184
|
+
before do
|
185
|
+
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
|
186
|
+
.to_return(status: 200, body: webmock_fixture('lod_search_with_blanknode_subjects.nt'), headers: { 'Content-Type' => 'application/n-triples' })
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'removes statements where the subject is a blanknode' do
|
190
|
+
expect(graph.size).to be 18
|
191
|
+
expect(subject.size).to be 12
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe '.object_values' do
|
197
|
+
subject { described_class.object_values(graph: graph, subject: subject_uri, predicate: predicate_uri) }
|
198
|
+
|
199
|
+
let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
|
200
|
+
let(:graph) { described_class.load_graph(url: url) }
|
201
|
+
let(:subject_uri) { RDF::URI('http://id.worldcat.org/fast/530369') }
|
202
|
+
let(:predicate_uri) { RDF::URI('http://schema.org/sameAs') }
|
203
|
+
|
204
|
+
before do
|
205
|
+
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
|
206
|
+
.to_return(status: 200, body: webmock_fixture('lod_oclc_all_query_3_results.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'returns all values for the subject-predicate pair' do
|
210
|
+
expect(subject.size).to be 2
|
211
|
+
expect(subject.map(&:to_s)).to match_array ['http://id.loc.gov/authorities/names/n79021621', 'https://viaf.org/viaf/126293486']
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe '.deep_copy' do
|
216
|
+
subject { described_class.object_values(graph: graph, subject: subject_uri, predicate: predicate_uri) }
|
217
|
+
|
218
|
+
let(:url) { 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage' }
|
219
|
+
let(:graph) { described_class.load_graph(url: url) }
|
220
|
+
let(:copied_graph) { described_class.deep_copy(graph: graph) }
|
221
|
+
|
222
|
+
before do
|
223
|
+
stub_request(:get, 'http://experimental.worldcat.org/fast/search?maximumRecords=3&query=cql.any%20all%20%22cornell%22&sortKeys=usage')
|
224
|
+
.to_return(status: 200, body: webmock_fixture('lod_oclc_all_query_3_results.rdf.xml'), headers: { 'Content-Type' => 'application/rdf+xml' })
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'returns a copy of the graph' do
|
228
|
+
expect(copied_graph).to be_kind_of RDF::Graph
|
229
|
+
expect(copied_graph.statements.count).to eq graph.statements.count
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|