supplejack_client 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/Gemfile +16 -0
- data/Guardfile +24 -0
- data/LICENSE.txt +674 -0
- data/README.md +153 -0
- data/Rakefile +9 -0
- data/lib/generators/locales/en.yml +11 -0
- data/lib/generators/supplejack/install_generator.rb +28 -0
- data/lib/generators/templates/README +19 -0
- data/lib/generators/templates/supplejack_client.rb +120 -0
- data/lib/supplejack/config.rb +116 -0
- data/lib/supplejack/controllers/helpers.rb +172 -0
- data/lib/supplejack/engine.rb +20 -0
- data/lib/supplejack/exceptions.rb +17 -0
- data/lib/supplejack/facet.rb +33 -0
- data/lib/supplejack/item.rb +94 -0
- data/lib/supplejack/item_relation.rb +73 -0
- data/lib/supplejack/log_subscriber.rb +58 -0
- data/lib/supplejack/paginated_collection.rb +61 -0
- data/lib/supplejack/record.rb +147 -0
- data/lib/supplejack/request.rb +95 -0
- data/lib/supplejack/search.rb +346 -0
- data/lib/supplejack/url_formats/item_hash.rb +208 -0
- data/lib/supplejack/user.rb +132 -0
- data/lib/supplejack/user_set.rb +349 -0
- data/lib/supplejack/user_set_relation.rb +143 -0
- data/lib/supplejack/util.rb +120 -0
- data/lib/supplejack/version.rb +10 -0
- data/lib/supplejack_client.rb +29 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/supplejack/controllers/helpers_spec.rb +277 -0
- data/spec/supplejack/facet_spec.rb +44 -0
- data/spec/supplejack/item_relation_spec.rb +111 -0
- data/spec/supplejack/item_spec.rb +115 -0
- data/spec/supplejack/log_subscriber_spec.rb +40 -0
- data/spec/supplejack/paginated_collection_spec.rb +43 -0
- data/spec/supplejack/record_spec.rb +255 -0
- data/spec/supplejack/request_spec.rb +195 -0
- data/spec/supplejack/search_spec.rb +727 -0
- data/spec/supplejack/url_formats/item_hash_spec.rb +341 -0
- data/spec/supplejack/user_set_relation_spec.rb +149 -0
- data/spec/supplejack/user_set_spec.rb +465 -0
- data/spec/supplejack/user_spec.rb +159 -0
- data/supplejack_client.gemspec +30 -0
- metadata +159 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
# The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
|
2
|
+
# and is licensed under the GNU General Public License, version 3.
|
3
|
+
# See https://github.com/DigitalNZ/supplejack_client for details.
|
4
|
+
#
|
5
|
+
# Supplejack was created by DigitalNZ at the National Library of NZ
|
6
|
+
# and the Department of Internal Affairs. http://digitalnz.org/supplejack
|
7
|
+
|
8
|
+
require 'spec_helper'
|
9
|
+
|
10
|
+
class Test
|
11
|
+
include Supplejack::Request
|
12
|
+
end
|
13
|
+
|
14
|
+
module Supplejack
|
15
|
+
describe Request do
|
16
|
+
before(:each) do
|
17
|
+
@test = Test.new
|
18
|
+
Supplejack.stub(:api_key) { '123' }
|
19
|
+
Supplejack.stub(:api_url) { 'http://api.org' }
|
20
|
+
Supplejack.stub(:timeout) { 20 }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#get' do
|
24
|
+
before(:each) do
|
25
|
+
RestClient::Request.stub(:execute).and_return(%{ {"search": {}} })
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'serializes the parameters in the url' do
|
29
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:url => "http://api.org/records.json?#{{:and => {:name => 'John'}}.to_query}&api_key=123"))
|
30
|
+
@test.get('/records', {:and => {:name => 'John'}})
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'parses the JSON returned' do
|
34
|
+
RestClient::Request.stub(:execute).and_return(%{ {"search": {}} })
|
35
|
+
@test.get('/records').should eq({'search' => {}})
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'logs a error correctly when the response from the API failed' do
|
39
|
+
RestClient::Request.stub(:execute).and_return(nil)
|
40
|
+
@subscriber = Supplejack::LogSubscriber.new
|
41
|
+
Supplejack::LogSubscriber.stub(:new) { @subscriber }
|
42
|
+
@subscriber.should_receive(:log_request)
|
43
|
+
@test.get('/records')
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'request format' do
|
47
|
+
it 'executes a request in a json format' do
|
48
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:url => 'http://api.org/records.json?api_key=123'))
|
49
|
+
@test.get('/records')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'overrides the response format with xml' do
|
53
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:url => 'http://api.org/records.xml?api_key=123'))
|
54
|
+
@test.get('/records', {}, {:format => :xml})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'api key' do
|
59
|
+
it 'overrides the api key' do
|
60
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:url => 'http://api.org/records.json?api_key=456'))
|
61
|
+
@test.get('/records', {:api_key => '456'})
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'timeout' do
|
66
|
+
it 'calculates the timeout' do
|
67
|
+
@test.should_receive(:timeout).with(hash_including({:timeout => 60}))
|
68
|
+
@test.get('/', {}, {:timeout => 60})
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#post' do
|
74
|
+
before(:each) do
|
75
|
+
RestClient::Request.stub(:execute)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'executes a post request' do
|
79
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:method => :post))
|
80
|
+
@test.post('/records/1/ucm', {})
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'passes the payload along' do
|
84
|
+
payload = {'ucm_record' => {:name => 'geocords', :value => '1234'}}
|
85
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:payload => payload.to_json))
|
86
|
+
@test.post('/records/1/ucm', {}, payload)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'adds the extra parameters to the post request' do
|
90
|
+
@test.should_receive(:full_url).with('/records/1/ucm', nil, {api_key: '12344'})
|
91
|
+
@test.post('/records/1/ucm', {api_key: '12344'}, {})
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'adds json headers and converts the payload into json' do
|
95
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:headers => {:content_type => :json, :accept => :json}, :payload => {records: [{record_id: 1, position: 1}, {record_id:2, position:2}]}.to_json))
|
96
|
+
@test.post('/records/1/ucm', {}, {records: [{record_id: 1, position: 1}, {record_id:2, position:2}]})
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'parses the JSON response' do
|
100
|
+
RestClient::Request.stub(:execute) { {user: {name: 'John'}}.to_json }
|
101
|
+
@test.post('/users', {}, {}).should eq({'user' => {'name' => 'John'}})
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '#delete' do
|
106
|
+
before(:each) do
|
107
|
+
RestClient::Request.stub(:execute)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'executes a delete request' do
|
111
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:method => :delete))
|
112
|
+
@test.delete('/records/1/ucm/1')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'adds the extra parameters to the delete request' do
|
116
|
+
@test.should_receive(:full_url).with('/records/1/ucm/1', nil, {api_key: '12344'})
|
117
|
+
@test.delete('/records/1/ucm/1', {api_key: '12344'})
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#put' do
|
122
|
+
before(:each) do
|
123
|
+
RestClient::Request.stub(:execute)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'executes a put request' do
|
127
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:method => :put))
|
128
|
+
@test.put('/records/1/ucm/1')
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'passes the payload along' do
|
132
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:payload => {:name => 1}.to_json))
|
133
|
+
@test.put('/records/1/ucm/1', {}, {:name => 1})
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'adds the extra parameters to the put request' do
|
137
|
+
@test.should_receive(:full_url).with('/records/1/ucm/1', nil, {api_key: '12344'})
|
138
|
+
@test.put('/records/1/ucm/1', {api_key: '12344'}, {})
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'adds json headers and converts the payload into json' do
|
142
|
+
RestClient::Request.should_receive(:execute).with(hash_including(:headers => {:content_type => :json, :accept => :json}, :payload => {records: [1,2,3]}.to_json))
|
143
|
+
@test.put('/records/1/ucm/1', {}, {records: [1,2,3]})
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'parses the JSON response' do
|
147
|
+
RestClient::Request.stub(:execute) { {user: {name: 'John'}}.to_json }
|
148
|
+
@test.put('/users/1', {}, {}).should eq({'user' => {'name' => 'John'}})
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '#timeout' do
|
153
|
+
it 'defaults to the timeout in the configuration' do
|
154
|
+
@test.send(:timeout).should eq 20
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'defaults to 30 when not set in the configuration' do
|
158
|
+
Supplejack.stub(:timeout) { nil }
|
159
|
+
@test.send(:timeout).should eq 30
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'overrides the timeout' do
|
163
|
+
@test.send(:timeout, {:timeout => 60}).should eq 60
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#full_url' do
|
168
|
+
it 'returns the full url with default api_url, format and api_key' do
|
169
|
+
@test.send(:full_url, '/records').should eq('http://api.org/records.json?api_key=123')
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'overrides the format' do
|
173
|
+
@test.send(:full_url, '/records', 'xml').should eq('http://api.org/records.xml?api_key=123')
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'overrides the api key' do
|
177
|
+
@test.send(:full_url, '/records', nil, {:api_key => '456'}).should eq('http://api.org/records.json?api_key=456')
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'url encodes the parameters' do
|
181
|
+
@test.send(:full_url, '/records', nil, {:api_key => '456', :i => {:category => 'Images'}}).should eq('http://api.org/records.json?api_key=456&i%5Bcategory%5D=Images')
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'adds debug=true when enable_debugging is set to true' do
|
185
|
+
Supplejack.stub(:enable_debugging) { true }
|
186
|
+
@test.send(:full_url, '/records', nil, {}).should eq 'http://api.org/records.json?api_key=123&debug=true'
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'handles nil params' do
|
190
|
+
@test.send(:full_url, '/records', nil, nil).should eq 'http://api.org/records.json?api_key=123'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,727 @@
|
|
1
|
+
# The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
|
2
|
+
# and is licensed under the GNU General Public License, version 3.
|
3
|
+
# See https://github.com/DigitalNZ/supplejack_client for details.
|
4
|
+
#
|
5
|
+
# Supplejack was created by DigitalNZ at the National Library of NZ
|
6
|
+
# and the Department of Internal Affairs. http://digitalnz.org/supplejack
|
7
|
+
|
8
|
+
require 'spec_helper'
|
9
|
+
|
10
|
+
class TestRecord
|
11
|
+
def initialize(attributes={})
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestItem
|
16
|
+
def initialize(attributes={})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Supplejack
|
21
|
+
describe Search do
|
22
|
+
|
23
|
+
describe '#initalize' do
|
24
|
+
it 'doesn\'t break when initalized with nil' do
|
25
|
+
Search.new(nil)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#params' do
|
30
|
+
it 'defaults to facets set in initializer' do
|
31
|
+
Supplejack.stub(:facets) { ['location', 'description'] }
|
32
|
+
Search.new.api_params[:facets].should eq('location,description')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'sets the facets through the params' do
|
36
|
+
Search.new(:facets => 'name,description').api_params[:facets].should eq('name,description')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'defaults to facets_per_page set in initializer' do
|
40
|
+
Supplejack.stub(:facets_per_page) { 33 }
|
41
|
+
Search.new.api_params[:facets_per_page].should eq 33
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'sets the facets_per_page through the params' do
|
45
|
+
Search.new(:facets_per_page => 13).api_params[:facets_per_page].should eq 13
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'deletes rails specific params' do
|
49
|
+
params = Search.new(:controller => 'records', :action => 'index').api_params
|
50
|
+
params.should_not have_key(:controller)
|
51
|
+
params.should_not have_key(:action)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#text' do
|
56
|
+
it 'returns the search text' do
|
57
|
+
Search.new(:text => 'dog').text.should eq('dog')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# describe '#geo_bbox' do
|
62
|
+
# end
|
63
|
+
|
64
|
+
# describe "#record_type" do
|
65
|
+
# end
|
66
|
+
|
67
|
+
describe '#page' do
|
68
|
+
it 'defaults to 1' do
|
69
|
+
Search.new.page.should eq 1
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'converts the page to a number' do
|
73
|
+
Search.new(:page => '2').page.should eq 2
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#per_page' do
|
78
|
+
it 'defaults to the per_page in the initializer' do
|
79
|
+
Supplejack.stub(:per_page) { 16 }
|
80
|
+
Search.new.per_page.should eq 16
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'sers the per_page through the params' do
|
84
|
+
Search.new(:per_page => '3').per_page.should eq 3
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#sort' do
|
89
|
+
it 'returns the field to sort on' do
|
90
|
+
Search.new(:sort => 'content_partner').sort.should eq 'content_partner'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'returns nil when not set' do
|
94
|
+
Search.new.sort.should be_nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '#direction' do
|
99
|
+
it 'returns the direction to sort the results' do
|
100
|
+
Search.new(:direction => 'asc').direction.should eq 'asc'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'returns nil when not set' do
|
104
|
+
Search.new.direction.should be_nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# describe "#custom_search" do
|
109
|
+
# end
|
110
|
+
|
111
|
+
describe '#search_attributes' do
|
112
|
+
it 'sets the filter defined in Supplejack.search_attributes with its current value' do
|
113
|
+
Supplejack.stub(:search_attributes) { [:location] }
|
114
|
+
Search.new(:i => {:location => 'Wellington'}).location.should eq 'Wellington'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#filters' do
|
119
|
+
before(:each) do
|
120
|
+
@filters = {:location => 'Wellington', :country => 'New Zealand'}
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'returns filters in a array format by default' do
|
124
|
+
Search.new(:i => @filters).filters.should include([:location, 'Wellington'], [:country, 'New Zealand'])
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'returns a hash of the current search filters' do
|
128
|
+
Search.new(:i => @filters).filters(:format => :hash).should include(@filters)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'expands filters that contain arrays' do
|
132
|
+
@filters = {:location => ['Wellington', 'Auckland']}
|
133
|
+
Search.new(:i => @filters).filters.should include([:location, 'Wellington'], [:location, 'Auckland'])
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'removes the location filter' do
|
137
|
+
@filters = {:location => ['Wellington', 'Auckland']}
|
138
|
+
Search.new(:i => @filters).filters(:except => [:location]).should_not include([:location, 'Wellington'], [:location, 'Auckland'])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "#facets" do
|
143
|
+
before(:each) do
|
144
|
+
@search = Search.new
|
145
|
+
Supplejack.facets = [:location]
|
146
|
+
@facets_hash = {"location" => {"Wellington" => 100}, "mayor" => {"Brake, Brian" => 20}}
|
147
|
+
@search.instance_variable_set(:@response, {"search" => {"facets" => @facets_hash}})
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'executes the request only once' do
|
151
|
+
@search.should_receive(:execute_request).once
|
152
|
+
@search.facets
|
153
|
+
@search.facets
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'handles a failed request from the API' do
|
157
|
+
@search.instance_variable_set(:@response, {'search' => {}})
|
158
|
+
@search.facets.should be_empty
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'initializes a facet object for each facet' do
|
162
|
+
Supplejack::Facet.should_receive(:new).with('location', {'Wellington' => 100})
|
163
|
+
Supplejack::Facet.should_receive(:new).with('mayor', {'Brake, Brian' => 20})
|
164
|
+
@search.facets
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'orders the facets based on the order set in the initializer' do
|
168
|
+
Supplejack.stub(:facets) { [:mayor, :location] }
|
169
|
+
@search.facets.first.name.should eq 'mayor'
|
170
|
+
@search.facets.last.name.should eq 'location'
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'orders the facets the other way around' do
|
174
|
+
Supplejack.stub(:facets) { [:location, :mayor] }
|
175
|
+
@search.facets.first.name.should eq 'location'
|
176
|
+
@search.facets.last.name.should eq 'mayor'
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'returns the facet values' do
|
180
|
+
@search.facets.first.values.should eq({'Wellington' => 100})
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '#facet' do
|
185
|
+
before(:each) do
|
186
|
+
@search = Search.new
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'returns the specified facet' do
|
190
|
+
facet = Supplejack::Facet.new('collection', [])
|
191
|
+
@search.stub(:facets) { [facet] }
|
192
|
+
|
193
|
+
@search.facet('collection').should eq facet
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'returns nil when facet is not found' do
|
197
|
+
@search.stub(:facets) { [] }
|
198
|
+
@search.facet('collection').should be_nil
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'returns nil if value is nil' do
|
202
|
+
facet = Supplejack::Facet.new('collection', [])
|
203
|
+
@search.stub(:facets) { [facet] }
|
204
|
+
@search.facet(nil).should be_nil
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe '#total' do
|
209
|
+
before(:each) do
|
210
|
+
@search = Search.new
|
211
|
+
@search.instance_variable_set(:@response, {'search' => {'result_count' => 100}})
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'executes the request only once' do
|
215
|
+
@search.should_receive(:execute_request).once
|
216
|
+
@search.total
|
217
|
+
@search.total
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'returns the total from the response' do
|
221
|
+
@search.total.should eq 100
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'returns 0 when the request to the API failed' do
|
225
|
+
@search.instance_variable_set(:@response, {'search' => {}})
|
226
|
+
@search.total.should eq 0
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe '#results' do
|
231
|
+
before(:each) do
|
232
|
+
Supplejack.stub(:record_klass).and_return('TestRecord')
|
233
|
+
@search = Search.new
|
234
|
+
@record1 = {'id' => 1, 'title' => 'Wellington'}
|
235
|
+
@record2 = {'id' => 2, 'title' => 'Auckland'}
|
236
|
+
@search.instance_variable_set(:@response, {'search' => {'results' => [@record1, @record2]}})
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'executes the request only once' do
|
240
|
+
@search.stub(:total) { 10 }
|
241
|
+
@search.should_receive(:execute_request).once
|
242
|
+
@search.results
|
243
|
+
@search.results
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'initializes record objects with the default class' do
|
247
|
+
TestRecord.should_receive(:new).with(@record1)
|
248
|
+
TestRecord.should_receive(:new).with(@record2)
|
249
|
+
@search.results
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'initializes record objects with the class provided in the params' do
|
253
|
+
@search = Search.new(:record_klass => 'test_item')
|
254
|
+
@search.instance_variable_set(:@response, {'search' => {'results' => [@record1, @record2]}})
|
255
|
+
TestItem.should_receive(:new).with(@record1)
|
256
|
+
TestItem.should_receive(:new).with(@record2)
|
257
|
+
@search.results
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'returns a array of objects of the provided class' do
|
261
|
+
@search.results.first.class.should eq TestRecord
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'returns an array wraped in paginated collection object' do
|
265
|
+
@search.results.current_page.should eq 1
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'returns empty paginated collection when API request failed' do
|
269
|
+
@search.instance_variable_set(:@response, {'search' => {}})
|
270
|
+
@search.results.size.should eq 0
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe '#counts' do
|
275
|
+
before(:each) do
|
276
|
+
@search = Search.new
|
277
|
+
@search.stub(:fetch_counts) { {'images' => 100} }
|
278
|
+
end
|
279
|
+
|
280
|
+
context 'caching disabled' do
|
281
|
+
before(:each) do
|
282
|
+
Supplejack.stub(:enable_caching) { false }
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'fetches the counts' do
|
286
|
+
query_params = {'images' => {:category => 'Images'}}
|
287
|
+
@search.should_receive(:fetch_counts).with(query_params)
|
288
|
+
@search.counts(query_params)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe '#fetch_counts' do
|
294
|
+
context 'without filters' do
|
295
|
+
before(:each) do
|
296
|
+
@search = Search.new
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'returns a hash with row names and its values' do
|
300
|
+
@search.stub(:get).and_return({'search' => {'facets' => {'counts' => {'images' => 151818}}}})
|
301
|
+
@search.fetch_counts({}).should eq({'images' => 151818})
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'returns every count even when there are no results matching' do
|
305
|
+
@search.stub(:get).and_return({'search' => {'facets' => {'counts' => {'images' => 151818}}}})
|
306
|
+
@search.fetch_counts({:images => {}, :headings => {}}).should eq({'images' => 151818, 'headings' => 0})
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'returns 0 for every facet when the API response fails' do
|
310
|
+
@search.stub(:get).and_raise(StandardError)
|
311
|
+
@search.fetch_counts({:images => {}}).should eq({'images' => 0})
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe '#counts_params' do
|
317
|
+
context 'without filters' do
|
318
|
+
before(:each) do
|
319
|
+
@search = Search.new
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'requests record_type == all' do
|
323
|
+
query_parameters = {:headings => {:record_type => '1'}}
|
324
|
+
@search.counts_params(query_parameters).should include(:facet_query => query_parameters, :record_type => 'all')
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'adds the restrictions set in the without variable' do
|
328
|
+
query_parameters = {:headings => {:record_type => '1'}}
|
329
|
+
@search.without = {:location => 'Wellington'}
|
330
|
+
@search.counts_params(query_parameters).should include(:without => {:location => 'Wellington'})
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'restricts the result set to only ones that match the and filters' do
|
334
|
+
query_parameters = {:headings => {:record_type => '1'}}
|
335
|
+
@search.and = {:location => 'Wellington'}
|
336
|
+
@search.counts_params(query_parameters).should include(:and => {:location => 'Wellington'})
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'restricts the result set to results that match any of the or filters' do
|
340
|
+
query_parameters = {:headings => {:record_type => '1'}}
|
341
|
+
@search.or = {:location => ['Wellington', 'Auckland']}
|
342
|
+
@search.counts_params(query_parameters).should include(:or => {:location => ['Wellington', 'Auckland']})
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'executes a request with facet_queries' do
|
346
|
+
query_parameters = {:images => {:creator => 'all', :record_type => '0'}, :headings => {:record_type => '1'}}
|
347
|
+
@search.counts_params(query_parameters).should include(:facet_query => query_parameters)
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'passes the text when present' do
|
351
|
+
@search = Search.new(:text => 'dogs')
|
352
|
+
@search.counts_params({}).should include(:text => 'dogs')
|
353
|
+
end
|
354
|
+
|
355
|
+
# it 'passes the geo_bbox when present' do
|
356
|
+
# @search = Search.new(geo_bbox: '1,2,3,4')
|
357
|
+
# @search.counts_params({}).should include(geo_bbox: '1,2,3,4')
|
358
|
+
# end
|
359
|
+
|
360
|
+
it 'merges the :i and :il filters with record_type 0' do
|
361
|
+
query_parameters = {:images => {'creator' => 'all', 'record_type' => '0'}, :headings => {'record_type' => '1', :dc_type => 'Group'}}
|
362
|
+
@search = Search.new(:i => {:category => 'Images'}, :il => {:year => '1998'})
|
363
|
+
|
364
|
+
images_query = {:creator => 'all', :record_type => '0', :category => 'Images', :year => '1998'}
|
365
|
+
headings_query = {:record_type => '1', :dc_type => 'Group'}
|
366
|
+
|
367
|
+
@search.counts_params(query_parameters).should include(:facet_query => {:images => images_query, :headings => headings_query})
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'merges *_text fields' do
|
371
|
+
query_parameters = {:images => {'creator' => 'all', 'record_type' => '0'}}
|
372
|
+
@search = Search.new(:i => {:subject_text => 'dog'})
|
373
|
+
|
374
|
+
images_query = {:creator => 'all', :record_type => '0'}
|
375
|
+
|
376
|
+
@search.counts_params(query_parameters).should include(:text => 'dog', :query_fields => [:subject], :facet_query => {:images => images_query})
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
context 'with active filters' do
|
381
|
+
before(:each) do
|
382
|
+
@search = Search.new(:i => {:location => 'Wellington'})
|
383
|
+
end
|
384
|
+
|
385
|
+
it 'merges the existing filters into every facet query' do
|
386
|
+
query_parameters = {:images => {'creator' => 'all', 'record_type' => 0}}
|
387
|
+
expected_filters = {:images => {:creator => 'all', :location => 'Wellington', :record_type => 0}}
|
388
|
+
@search.counts_params(query_parameters).should include(:facet_query => expected_filters)
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'merges existing filters without overriding' do
|
392
|
+
query_parameters = {:images => {'location' => 'Matapihi', 'record_type' => 0}}
|
393
|
+
expected_filters = {:images => {:location => ['Wellington', 'Matapihi'], :record_type => 0}}
|
394
|
+
@search.counts_params(query_parameters).should include(:facet_query => expected_filters)
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'overrides the record_type' do
|
398
|
+
@search = Search.new(:record_type => '1')
|
399
|
+
query_parameters = {:images => {'record_type' => '0'}}
|
400
|
+
expected_filters = {:images => {:record_type => '0'}}
|
401
|
+
@search.counts_params(query_parameters).should include(:facet_query => expected_filters)
|
402
|
+
end
|
403
|
+
|
404
|
+
it 'merges existing negative filters' do
|
405
|
+
@search = Search.new(:i => {'-category' => 'Groups'})
|
406
|
+
query_parameters = {:photos => {'has_large_thumbnail_url' => 'Y'}}
|
407
|
+
expected_filters = {:photos => {:has_large_thumbnail_url => 'Y', :'-category' => 'Groups'}}
|
408
|
+
@search.counts_params(query_parameters).should include(:facet_query => expected_filters)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
describe '#request_path' do
|
414
|
+
before(:each) do
|
415
|
+
@search = Search.new
|
416
|
+
end
|
417
|
+
|
418
|
+
it 'returns /records by default' do
|
419
|
+
@search.request_path.should eq '/records'
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
describe '#execute_request' do
|
424
|
+
before(:each) do
|
425
|
+
@search = Search.new
|
426
|
+
end
|
427
|
+
|
428
|
+
it 'only executes the request once' do
|
429
|
+
@search.should_receive(:get).once.and_return('{}')
|
430
|
+
@search.execute_request
|
431
|
+
@search.execute_request
|
432
|
+
end
|
433
|
+
|
434
|
+
it 'removes the results that match the without filters' do
|
435
|
+
@search.without = {:location => 'Wellington'}
|
436
|
+
@search.should_receive(:get).with('/records', hash_including(:without => {:location => 'Wellington'}))
|
437
|
+
@search.execute_request
|
438
|
+
end
|
439
|
+
|
440
|
+
it 'restricts the result set to only ones that match the and filters' do
|
441
|
+
@search.and = {:location => 'Wellington'}
|
442
|
+
@search.should_receive(:get).with('/records', hash_including(:and => {:location => 'Wellington'}))
|
443
|
+
@search.execute_request
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'restricts the result set to only ones that match any of the or filters' do
|
447
|
+
@search.or = {:location => ['Wellington']}
|
448
|
+
@search.should_receive(:get).with('/records', hash_including(:or => {:location => ['Wellington']}))
|
449
|
+
@search.execute_request
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'returns a empty search hash when a error is raised' do
|
453
|
+
@search.stub(:get).and_raise(StandardError)
|
454
|
+
@search.execute_request.should eq({'search' => {}})
|
455
|
+
end
|
456
|
+
|
457
|
+
context 'caching enabled' do
|
458
|
+
before :each do
|
459
|
+
@cache = double(:cache).as_null_object
|
460
|
+
Rails.stub(:cache) { @cache }
|
461
|
+
Supplejack.stub(:enable_caching) { true }
|
462
|
+
end
|
463
|
+
|
464
|
+
it 'caches the response when it is cacheable' do
|
465
|
+
search = Supplejack::Search.new
|
466
|
+
search.stub(:cacheable?) { true }
|
467
|
+
cache_key = Digest::MD5.hexdigest("/records?#{search.api_params.to_query}")
|
468
|
+
Rails.cache.should_receive(:fetch).with(cache_key, expires_in: 1.hour)
|
469
|
+
search.execute_request
|
470
|
+
end
|
471
|
+
|
472
|
+
it 'doesnt cache the response it is not cacheable' do
|
473
|
+
search = Supplejack::Search.new(text: "dogs")
|
474
|
+
search.stub(:cacheable?) { false }
|
475
|
+
Rails.cache.should_not_receive(:fetch)
|
476
|
+
search.execute_request
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
describe '#cacheable?' do
|
482
|
+
it 'returns true when it doesn\'t have a text parameter' do
|
483
|
+
Supplejack::Search.new.cacheable?.should be_true
|
484
|
+
end
|
485
|
+
|
486
|
+
it 'returns false when it has a text parameter' do
|
487
|
+
Supplejack::Search.new(text: 'Dogs').cacheable?.should be_false
|
488
|
+
end
|
489
|
+
|
490
|
+
it 'returns false then it\'s not the first page of results' do
|
491
|
+
Supplejack::Search.new(page: '2').cacheable?.should be_false
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
describe '#has_attribute_name?' do
|
496
|
+
before(:each) do
|
497
|
+
@search = Search.new
|
498
|
+
@search.location = ['Wellington', 'Auckland']
|
499
|
+
end
|
500
|
+
|
501
|
+
it 'returns true if value is in filter' do
|
502
|
+
@search.has_location?('Wellington').should be_true
|
503
|
+
end
|
504
|
+
|
505
|
+
it 'returns false is value is not in filter' do
|
506
|
+
@search.has_location?('Videos').should be_false
|
507
|
+
end
|
508
|
+
|
509
|
+
context 'search filter is single valued' do
|
510
|
+
before(:each) do
|
511
|
+
@search = Search.new
|
512
|
+
@search.location = 'Wellington'
|
513
|
+
end
|
514
|
+
|
515
|
+
it 'returns true if value matches filter' do
|
516
|
+
@search.has_location?('Wellington').should be_true
|
517
|
+
end
|
518
|
+
|
519
|
+
it 'returns false if value does not match the filter' do
|
520
|
+
@search.has_location?('Cats').should be_false
|
521
|
+
end
|
522
|
+
|
523
|
+
it 'returns false when location has nil value' do
|
524
|
+
@search.location = nil
|
525
|
+
@search.has_category?('Cats').should be_false
|
526
|
+
end
|
527
|
+
|
528
|
+
it 'shouldn\'t search for a non existent search attribute' do
|
529
|
+
Supplejack.stub(:search_attributes) { [] }
|
530
|
+
@search.should_not_receive(:has_filter_and_value?)
|
531
|
+
@search.has_category?('Cats')
|
532
|
+
end
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
describe '#categories' do
|
537
|
+
before(:each) do
|
538
|
+
@search = Search.new({:i => {:category => 'Books', :year => 2001}, :text => 'Dogs'})
|
539
|
+
@search.stub(:get) { {'search' => {'facets' => {'category' => {'Books' => 123}}, 'result_count' => 123}} }
|
540
|
+
end
|
541
|
+
|
542
|
+
it 'should call the fetch_values method' do
|
543
|
+
@search.should_receive(:facet_values).with('category', {})
|
544
|
+
@search.categories
|
545
|
+
end
|
546
|
+
|
547
|
+
it 'removes category filter from the search request' do
|
548
|
+
@search.should_receive(:get).with('/records', hash_including(:and => {:year => 2001})).and_return({'search' => {'facets' => {'category' => {'Books' => 123}}}})
|
549
|
+
@search.categories
|
550
|
+
end
|
551
|
+
|
552
|
+
it 'returns the category facet hash ' do
|
553
|
+
@search.categories.should include('Books' => 123)
|
554
|
+
end
|
555
|
+
|
556
|
+
it 'asks the API for 0 results' do
|
557
|
+
@search.should_receive(:get).with('/records', hash_including({:per_page => 0}))
|
558
|
+
@search.categories
|
559
|
+
end
|
560
|
+
|
561
|
+
it 'should return add the All count to the hash' do
|
562
|
+
@search.categories['All'].should eq 123
|
563
|
+
end
|
564
|
+
|
565
|
+
it 'orders the category values by :count' do
|
566
|
+
@search.should_receive(:facet_values).with('category', {:sort => :count})
|
567
|
+
@search.categories({:sort => :count})
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
describe '#fetch_facet_values' do
|
572
|
+
before(:each) do
|
573
|
+
@search = Search.new({:i => {:category => 'Books', :year => 2001}, :text => 'Dogs'})
|
574
|
+
@search.stub(:get) { {'search' => {'facets' => {'category' => {'Books' => 123}}, 'result_count' => 123}} }
|
575
|
+
end
|
576
|
+
|
577
|
+
it 'returns the category facet hash' do
|
578
|
+
@search.fetch_facet_values('category').should include('Books' => 123)
|
579
|
+
end
|
580
|
+
|
581
|
+
it 'returns empty values when the request to the API failed' do
|
582
|
+
@search.stub(:get).and_raise(StandardError)
|
583
|
+
@search.fetch_facet_values('category').should eq({'All' => 0})
|
584
|
+
end
|
585
|
+
|
586
|
+
it 'should add the All count to the hash' do
|
587
|
+
@search.fetch_facet_values('category')['All'].should eq 123
|
588
|
+
end
|
589
|
+
|
590
|
+
it 'doesnt return the All count ' do
|
591
|
+
@search.fetch_facet_values('category', {:all => false}).should_not have_key('All')
|
592
|
+
end
|
593
|
+
|
594
|
+
it 'memoizes the facet_values' do
|
595
|
+
@search.should_receive(:get).once
|
596
|
+
@search.fetch_facet_values('category')
|
597
|
+
@search.fetch_facet_values('category')
|
598
|
+
end
|
599
|
+
|
600
|
+
context 'sorting' do
|
601
|
+
before(:each) do
|
602
|
+
@facet = Supplejack::Facet.new('category', {'All' => 123, 'Books' => 123})
|
603
|
+
Supplejack::Facet.stub(:new) { @facet }
|
604
|
+
end
|
605
|
+
|
606
|
+
it 'initializes a Supplejack::Facet' do
|
607
|
+
Supplejack::Facet.should_receive(:new).with('category', {'All' => 123, 'Books' => 123})
|
608
|
+
@search.fetch_facet_values('category')
|
609
|
+
end
|
610
|
+
|
611
|
+
it 'tells the facet how to sort the values' do
|
612
|
+
@facet.should_receive(:values).with(:index)
|
613
|
+
@search.fetch_facet_values('category', {:sort => :index})
|
614
|
+
end
|
615
|
+
|
616
|
+
it 'doesn\'t sort by default' do
|
617
|
+
@facet.should_receive(:values).with(nil)
|
618
|
+
@search.fetch_facet_values('category')
|
619
|
+
end
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
describe 'facet_values_params' do
|
624
|
+
before(:each) do
|
625
|
+
@search = Search.new({:i => {:type => 'Person', :year => 2001}, :text => 'Dogs'})
|
626
|
+
end
|
627
|
+
|
628
|
+
it 'removes type filter from the search request' do
|
629
|
+
@search.facet_values_params('type').should include(:and => {:year => 2001})
|
630
|
+
end
|
631
|
+
|
632
|
+
it 'requests 0 results per_page' do
|
633
|
+
@search.facet_values_params('type').should include(:per_page => 0)
|
634
|
+
end
|
635
|
+
|
636
|
+
it 'adds without filters' do
|
637
|
+
@search = Search.new({:i => {:type => 'Person', :year => 2001, '-group' => 'Group'}, :text => 'Dogs'})
|
638
|
+
@search.facet_values_params('type').should include(:without => {:group => 'Group'})
|
639
|
+
end
|
640
|
+
|
641
|
+
it 'only adds the and_filters to :and' do
|
642
|
+
@search = Search.new({:i => {:type => 'Person', :year => 2001, '-group' => 'Group'}, :text => 'Dogs'})
|
643
|
+
@search.facet_values_params('type').should include(:and => {:year => 2001})
|
644
|
+
end
|
645
|
+
|
646
|
+
it 'gets the facet_values for a record_type 1' do
|
647
|
+
@search = Search.new({:i => {:type => 'Person'}, :h => {:group => 'Group'}, :text => 'Dogs', :record_type => 1})
|
648
|
+
@search.facet_values_params('group').should include(:and => {})
|
649
|
+
end
|
650
|
+
|
651
|
+
it 'restricts results to filters specified in without accessor' do
|
652
|
+
@search = Search.new
|
653
|
+
@search.without = {:website => 'Flickr'}
|
654
|
+
@search.facet_values_params('type').should include(:without => {:website => 'Flickr'})
|
655
|
+
end
|
656
|
+
|
657
|
+
it 'merges in the filters specified in without' do
|
658
|
+
@search = Search.new({:i => {'-type' => 'Person'}})
|
659
|
+
@search.without = {:website => 'Flickr'}
|
660
|
+
@search.facet_values_params('type').should include(:without => {:website => 'Flickr', :type => 'Person'})
|
661
|
+
end
|
662
|
+
|
663
|
+
it 'adds the restrictions set in the and variable' do
|
664
|
+
@search = Search.new
|
665
|
+
@search.and = {:content_partner => 'NLNZ'}
|
666
|
+
@search.facet_values_params('type').should include(:and => {:content_partner => 'NLNZ'})
|
667
|
+
end
|
668
|
+
|
669
|
+
it 'adds the restrictions set in the or variable' do
|
670
|
+
@search = Search.new
|
671
|
+
@search.or = {:content_partner => 'NLNZ'}
|
672
|
+
@search.facet_values_params('type').should include(:or => {:content_partner => 'NLNZ'})
|
673
|
+
end
|
674
|
+
|
675
|
+
it 'memoizes the params' do
|
676
|
+
@search = Search.new
|
677
|
+
@search.should_receive(:url_format).once.and_return(double(:url_format, :and_filters => {}))
|
678
|
+
@search.facet_values_params('type')
|
679
|
+
@search.facet_values_params('type')
|
680
|
+
end
|
681
|
+
|
682
|
+
it 'adds a parameter for facets_per_page if the option is present' do
|
683
|
+
@search.facet_values_params('type', {:facets_per_page => 15}).should include(:facets_per_page => 15)
|
684
|
+
end
|
685
|
+
end
|
686
|
+
|
687
|
+
describe '#facet_values' do
|
688
|
+
before(:each) do
|
689
|
+
@search = Search.new
|
690
|
+
@search.stub(:fetch_facet_values) { {'Books' => 100} }
|
691
|
+
end
|
692
|
+
|
693
|
+
context 'caching disabled' do
|
694
|
+
before(:each) do
|
695
|
+
Supplejack.stub(:enable_caching) { false }
|
696
|
+
end
|
697
|
+
|
698
|
+
it 'fetches the facet values' do
|
699
|
+
@search.should_receive(:fetch_facet_values).with('category', anything)
|
700
|
+
@search.facet_values('category', anything)
|
701
|
+
end
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
describe '#merge_extra_filters' do
|
706
|
+
before(:each) do
|
707
|
+
@search = Search.new
|
708
|
+
end
|
709
|
+
|
710
|
+
it 'merges the and filters' do
|
711
|
+
@search.and = {:type => 'Person'}
|
712
|
+
@search.merge_extra_filters({:and => {:location => 'Wellington'}}).should eq({:and => {:location => 'Wellington', :type => 'Person'}})
|
713
|
+
end
|
714
|
+
|
715
|
+
it 'merges the or filters' do
|
716
|
+
@search.or = {:type => 'Person'}
|
717
|
+
@search.merge_extra_filters({:and => {:location => 'Wellington'}}).should eq({:and => {:location => 'Wellington'}, :or => {:type => 'Person'}})
|
718
|
+
end
|
719
|
+
|
720
|
+
it 'merges the without filters' do
|
721
|
+
@search.without = {:type => 'Person'}
|
722
|
+
@search.merge_extra_filters({:and => {:location => 'Wellington'}}).should eq({:and => {:location => 'Wellington'}, :without => {:type => 'Person'}})
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
end
|
727
|
+
end
|