qa 5.7.0 → 5.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3fb01e5ca2c9435b8c22af76ace78a1486d2ad22ee54f0a8e1a40ba24b64e218
4
- data.tar.gz: '0825479f37ac4eb299c944981fff9b670e8efd86eb8b05c3341f7d2a9300361c'
3
+ metadata.gz: b4fcd121b5625fc9bddf0250e2bbf75fb1585a5ad2332b0e9b16c8a927085332
4
+ data.tar.gz: 323903a339a4ef24c3b50db110dfb4e01a93e33dbb3c95e155fbf3577e84b3db
5
5
  SHA512:
6
- metadata.gz: 80792bf55b1f8cf6f7401657d7b7844fbeff0dbc4e4597e16869a63a65a6cf1e271d88d631bd0dd9f4f8dbcdda2caadac47974d8d282f21ee1696308022bb201
7
- data.tar.gz: 7d700e24566d6047cb061731e941affa72d3c260eb7822c421421986127ddfd819f2d6d4cd1c968cd9e527b3276c392a7403564ebce8e5ee4210fb86863072d0
6
+ metadata.gz: c0fc0da7aedbcc2519e0f79d2a6ae403eb26e8a730bb4e1babaf33c6864084b5c624ffe8b2b35cb34dc4057e76f88642a85ebe0b41bcda496d9e6b029a6fad6c
7
+ data.tar.gz: 564e2b850320b6411462236b2dc0ff88d02ce076c33e2abbe4c5d588b9fb8ae5af0382124cd65686bedf132bf42694d965cf68b233348040c2419d947e0398cd
@@ -4,6 +4,9 @@
4
4
  # same methods.
5
5
 
6
6
  class Qa::TermsController < ::ApplicationController
7
+ class_attribute :pagination_service_class
8
+ self.pagination_service_class = Qa::PaginationService
9
+
7
10
  before_action :check_vocab_param, :init_authority
8
11
  before_action :check_query_param, only: :search
9
12
 
@@ -23,7 +26,11 @@ class Qa::TermsController < ::ApplicationController
23
26
  def search
24
27
  terms = @authority.method(:search).arity == 2 ? @authority.search(url_search, self) : @authority.search(url_search)
25
28
  cors_allow_origin_header(response)
26
- render json: terms
29
+ respond_to do |wants|
30
+ wants.json { render json: pagination_service(format: :json, results: terms).build_response }
31
+ wants.jsonapi { render json: pagination_service(format: :jsonapi, results: terms).build_response }
32
+ wants.any { render json: pagination_service(format: :json, results: terms).build_response, content_type: json_content_type }
33
+ end
27
34
  end
28
35
 
29
36
  # If the subauthority supports it, return all the information for a given term
@@ -31,7 +38,13 @@ class Qa::TermsController < ::ApplicationController
31
38
  def show
32
39
  term = @authority.method(:find).arity == 2 ? @authority.find(params[:id], self) : @authority.find(params[:id])
33
40
  cors_allow_origin_header(response)
34
- render json: term, content_type: content_type_for_format
41
+ respond_to do |wants|
42
+ wants.json { render json: term }
43
+ wants.n3 { render json: term }
44
+ wants.jsonld { render json: term }
45
+ wants.ntriples { render json: term }
46
+ wants.any { render json: term, content_type: json_content_type }
47
+ end
35
48
  end
36
49
 
37
50
  # If the subauthority supports it, return all the information for a given term
@@ -39,7 +52,13 @@ class Qa::TermsController < ::ApplicationController
39
52
  def fetch
40
53
  term = @authority.method(:find).arity == 2 ? @authority.find(params[:uri], self) : @authority.find(params[:uri])
41
54
  cors_allow_origin_header(response)
42
- render json: term, content_type: content_type_for_format
55
+ respond_to do |wants|
56
+ wants.json { render json: term }
57
+ wants.n3 { render json: term }
58
+ wants.jsonld { render json: term }
59
+ wants.ntriples { render json: term }
60
+ wants.any { render json: term, content_type: json_content_type }
61
+ end
43
62
  end
44
63
 
45
64
  def check_vocab_param
@@ -90,28 +109,11 @@ class Qa::TermsController < ::ApplicationController
90
109
  params[:q].gsub("*", "%2A")
91
110
  end
92
111
 
93
- def format
94
- return 'json' unless params.key?(:format)
95
- return 'json' if params[:format].blank?
96
- params[:format]
97
- end
98
-
99
- def jsonld?
100
- format.casecmp?('jsonld')
101
- end
102
-
103
- def n3?
104
- format.casecmp?('n3')
105
- end
106
-
107
- def ntriples?
108
- format.casecmp?('ntriples')
112
+ def json_content_type
113
+ Mime::Type.lookup_by_extension(:json).to_str
109
114
  end
110
115
 
111
- def content_type_for_format
112
- return 'application/ld+json' if jsonld?
113
- return 'text/n3' if n3?
114
- return 'application/n-triples' if ntriples?
115
- 'application/json'
116
+ def pagination_service(results:, format:)
117
+ pagination_service_class.new(request: request, results: results, format: format)
116
118
  end
117
119
  end
@@ -0,0 +1,586 @@
1
+ module Qa
2
+ # Provide pagination processing used to respond to requests for paginated results.
3
+ #
4
+ # <b>Defaults for page_offset and page_limit:</b> (see example responses under #build_response)
5
+ #
6
+ # format == :json
7
+ #
8
+ # * if neither page_offset nor page_limit is passed in, then... (backward compatible)
9
+ # * returns results as an Array<Hash>
10
+ # * returns all results
11
+ # * page_offset is "1"
12
+ # * page_limit is total_num_found
13
+ #
14
+ # * if either page_offset or page_limit is passed in, then...
15
+ # * returns results as an Array<Hash>
16
+ # * returns a page of results
17
+ # * default for page_offset is "1"
18
+ # * default for page_limit is DEFAULT_PAGE_LIMIT (i.e., 10)
19
+ #
20
+ # format == :jsonapi
21
+ #
22
+ # * response is always in the jsonapi format
23
+ # * results are always paginated
24
+ # * default for page_offset is "1"
25
+ # * default for page_limit is DEFAULT_PAGE_LIMIT (i.e., 10)
26
+ #
27
+ # <b>How page_offset is calculated for pagination links:</b>
28
+ #
29
+ # expected page boundaries
30
+ #
31
+ # * Expected page boundaries are always calculated starting from page_offset=1
32
+ # and the current page_limit. The page boundaries will include the page_offsets
33
+ # that cover all results. For example, page_limit=10 with 36 results will have
34
+ # page boundaries 1, 11, 21, 31.
35
+ #
36
+ # self url
37
+ #
38
+ # * The self url always has the page_offset for the current page, which defaults
39
+ # to 1 if not passed in.
40
+ #
41
+ # first page url
42
+ #
43
+ # * The first page url always has page_offset=1.
44
+ #
45
+ # last page url
46
+ #
47
+ # * The last page url always has page_offset equal to the last of the expected page
48
+ # boundaries regardless of the passed in page_offset. For the example where
49
+ # page_limit=10 with 36 results, the last page will always have page_offset=31.
50
+ #
51
+ # prev page url
52
+ #
53
+ # * Previous' page_offset is calculated from the passed in page_offset whether or
54
+ # not it is on an expected page boundary.
55
+ #
56
+ # * For prev, page_offset = passed in page_offset - page_limit || nil if calculated as < 1
57
+ # * when current page_offset (e.g. 1) is less than page_limit (e.g. 10), then page_offset
58
+ # for prev will be nil (e.g. 1 - 10 = -9 which is < 1)
59
+ # * when current page_offset is an expected page boundary (e.g. 21), then
60
+ # page_offset for prev will also be a page boundary (e.g. 21 - 10 = 11
61
+ # which is an expected page boundary)
62
+ # * when current page_offset is not on an expected page boundary (e.g. 13), then
63
+ # page_offset for prev will not be on an expected page boundary (e.g. 13 - 10 = 3
64
+ # which is not an expected page boundary)
65
+ #
66
+ # next page url
67
+ #
68
+ # * Next's page_offset is calculated from the passed in page_offset whether or
69
+ # not it is on an expected page boundary.
70
+ #
71
+ # * For next, page_offset = passed in page_offset + page_limit || nil if calculated > total number of results found
72
+ # * when current page_offset (e.g. 31) is greater than total number of results (e.g. 36),
73
+ # then page_offset for next will be nil (e.g. 31 + 10 = 41 which is > 36)
74
+ # * when current page_offset is an expected page boundary (e.g. 21), then
75
+ # page_offset for next will also be a page boundary (e.g. 21 + 10 = 31
76
+ # which is an expected page boundary)
77
+ # * when current page_offset is not on an expected page boundary (e.g. 13), then
78
+ # page_offset for next will not be on an expected page boundary (e.g. 13 + 10 = 23
79
+ # which is not an expected page boundary)
80
+ #
81
+ class PaginationService # rubocop:disable Metrics/ClassLength
82
+ # Default page_limit to use if not passed in with the request.
83
+ DEFAULT_PAGE_LIMIT = 10
84
+
85
+ # Error code for page_limit and page_offset when the value is not an integer.
86
+ ERROR_NOT_INTEGER = 901
87
+ # Error code for page_limit and page_offset when the value is below the acceptable range (e.g. < 1).
88
+ ERROR_OUT_OF_RANGE_TOO_SMALL = 902
89
+ # Error code for page_offset when the value is above the acceptable range (e.g. > total_num_found).
90
+ ERROR_OUT_OF_RANGE_TOO_LARGE = 903
91
+
92
+ # @param request [ActionDispatch::Request] The request from the controller.
93
+ # To support pagination, it's params need to respond to:
94
+ # * #page_offset [Integer] - the offset into the results for the start of the page (counts from 1; default: 1)
95
+ # * #page_limit [Integer] - the max number of records to return in a page
96
+ # * if format==:jsonapi, defaults to: DEFAULT_PAGE_LIMIT
97
+ # * if page_offset is passed in, defaults to: DEFAULT_PAGE_LIMIT
98
+ # * else when format==:json && page_offset.nil?, defaults to all results (backward compatible)
99
+ # @param results [Array<Hash>] results of a search query as processed by the authority module
100
+ # @param format [String] - if present, supported values are [:json | :jsonapi]
101
+ # * when :json, the response is an array of results (default)
102
+ # * when :jsonapi, the response follows the JSON API specification
103
+ #
104
+ # @see https://jsonapi.org/format/#fetching-pagination Pagination section of JSON API specification
105
+ # @see https://jsonapi.org/examples/#pagination JSON API example pagination
106
+ def initialize(request:, results:, format: :json)
107
+ @request = request
108
+ @results = results
109
+ @requested_format = format
110
+ @page_offset_error = false
111
+ @page_limit_error = false
112
+ end
113
+
114
+ # @return json results, optionally limited to requested page and optionally
115
+ # formatted according to the JSON-API standard. The default is to return
116
+ # just the results for backward compatibility. See examples.
117
+ #
118
+ # @example json without pagination (backward compatible) (used only if neither page_offset nor page_limit are passed in)
119
+ # # request: q=term
120
+ # # response: format=json, no pagination, all results
121
+ # [
122
+ # { "id": "1", "label": "term 1" },
123
+ # { "id": "2", "label": "term 2" },
124
+ # ...
125
+ # { "id": "28", "label": "term 28" }
126
+ # ]
127
+ #
128
+ # @example json with pagination (used if either page_offset or page_limit are passed in)
129
+ # # request: q=term, page_offset=3, page_limit=2
130
+ # # response: format=json, paginated, results 3..4
131
+ # [
132
+ # { "id": "3", "label": "term 3" },
133
+ # { "id": "4", "label": "term 4" }
134
+ # ]
135
+ #
136
+ # @example json-api with pagination using default page_offset and page_limit
137
+ # # request: q=term, format=json-api
138
+ # # response: format=json-api, paginated, results 1..10
139
+ # {
140
+ # "data": [
141
+ # { "id": "1", "label": "term 1" },
142
+ # { "id": "2", "label": "term 2" },
143
+ # ...
144
+ # { "id": "10", "label": "term 10" }
145
+ # ],
146
+ # "meta": {
147
+ # "page": {
148
+ # "page_offset": "1",
149
+ # "page_limit": "10",
150
+ # "actual_page_size": "10",
151
+ # "total_num_found": "28",
152
+ # }
153
+ # }
154
+ # "links": {
155
+ # "self_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=1",
156
+ # "first_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=1",
157
+ # "prev_url": nil,
158
+ # "next_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=11",
159
+ # "last_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=21"
160
+ # }
161
+ # }
162
+ #
163
+ # @example json-api with pagination for page_offset=7 and page_limit=2
164
+ # # request: q=term, format=json-api, page_offset=7, page_limit=2
165
+ # # response: format=json, paginated, results 7..8
166
+ # {
167
+ # "data": [
168
+ # { "id": "7", "label": "term 7" },
169
+ # { "id": "8", "label": "term 8" }
170
+ # ],
171
+ # "meta": {
172
+ # "page": {
173
+ # "page_offset": "7",
174
+ # "page_limit": "2",
175
+ # "actual_page_size": "2",
176
+ # "total_num_found": "28",
177
+ # }
178
+ # }
179
+ # "links": {
180
+ # "self_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=7",
181
+ # "first_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=1",
182
+ # "prev_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=5",
183
+ # "next_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=9",
184
+ # "last_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=27"
185
+ # }
186
+ # }
187
+ #
188
+ # @example json-api with page_offset and page_limit errors
189
+ # # request: q=term, format=json-api, page_offset=0, page_limit=-1
190
+ # # response: format=json-api, paginated, no results, errors
191
+ # {
192
+ # "data": [],
193
+ # "meta": {
194
+ # "page": {
195
+ # "page_offset": "0",
196
+ # "page_limit": "-1",
197
+ # "actual_page_size": nil,
198
+ # "total_num_found": "28",
199
+ # }
200
+ # }
201
+ # "links": {
202
+ # "self_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=-1&page_offset=0",
203
+ # "first_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=1",
204
+ # "prev_url": nil,
205
+ # "next_url": nil,
206
+ # "last_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=21"
207
+ # }
208
+ # "errors": [
209
+ # {
210
+ # "status" => "200",
211
+ # "source" => { "page_offset" => "0" },
212
+ # "title" => "Page Offset Out of Range",
213
+ # "detail" => "Offset 0 < 1 (first result). Returning empty results."
214
+ # },
215
+ # {
216
+ # "status" => "200",
217
+ # "source" => { "page_limit" => "-1" },
218
+ # "title" => "Page Limit Out of Range",
219
+ # "detail" => "Page limit -1 < 1 (minimum limit). Returning empty results."
220
+ #
221
+ # }
222
+ # ]
223
+ # }
224
+ #
225
+ # @see DEFAULT_PAGE_LIMIT
226
+ # @see ERROR_NOT_INTEGER
227
+ # @see ERROR_OUT_OF_RANGE_TOO_SMALL
228
+ # @see ERROR_OUT_OF_RANGE_TOO_LARGE
229
+ def build_response
230
+ json_api? ? build_json_api_response : build_json_response
231
+ end
232
+
233
+ private
234
+
235
+ def errors?
236
+ page_offset_error? || page_limit_error?
237
+ end
238
+
239
+ def page_offset_error?
240
+ page_offset
241
+ @page_offset_error
242
+ end
243
+
244
+ def page_limit_error?
245
+ page_limit
246
+ @page_limit_error
247
+ end
248
+
249
+ # @return just the data as a JSON array
250
+ def build_json_response
251
+ errors? ? [] : build_data
252
+ end
253
+
254
+ # @return pages of results following the JSON API standard
255
+ def build_json_api_response
256
+ errors? ? build_json_api_response_with_errors : build_json_api_response_without_errors
257
+ end
258
+
259
+ def build_json_api_response_without_errors
260
+ {
261
+ "data" => build_data,
262
+ "meta" => build_meta,
263
+ "links" => build_links
264
+ }
265
+ end
266
+
267
+ def build_json_api_response_with_errors
268
+ {
269
+ "data" => [],
270
+ "meta" => build_meta,
271
+ "links" => build_links_when_errors,
272
+ "errors" => build_errors
273
+ }
274
+ end
275
+
276
+ def build_data
277
+ @results[start_index..last_index]
278
+ end
279
+
280
+ def build_meta
281
+ meta = {}
282
+ meta['page_offset'] = page_offset_error? ? @requested_page_offset.to_s : page_offset.to_s
283
+ meta['page_limit'] = page_limit_error? ? @requested_page_limit.to_s : page_limit.to_s
284
+ meta['actual_page_size'] = errors? ? "0" : actual_page_size.to_s
285
+ meta['total_num_found'] = total_num_found.to_s
286
+ { "page" => meta }
287
+ end
288
+
289
+ def build_links
290
+ links = {}
291
+ links['self'] = self_link
292
+ links['first'] = first_link
293
+ links['prev'] = prev_link
294
+ links['next'] = next_link
295
+ links['last'] = last_link
296
+ links
297
+ end
298
+
299
+ def build_links_when_errors
300
+ links = {}
301
+ links['self'] = "#{request_base_url}#{request_path}?#{request_query_string}"
302
+ links['first'] = first_link
303
+ links['prev'] = nil
304
+ links['next'] = nil
305
+ links['last'] = last_link
306
+ links
307
+ end
308
+
309
+ def build_errors
310
+ errors = []
311
+ errors << build_page_offset_error if page_offset_error?
312
+ errors << build_page_limit_error if page_limit_error?
313
+ errors
314
+ end
315
+
316
+ def build_page_offset_error
317
+ case @page_offset_error
318
+ when ERROR_NOT_INTEGER
319
+ build_page_offset_not_integer
320
+ when ERROR_OUT_OF_RANGE_TOO_LARGE
321
+ build_page_offset_too_large
322
+ when ERROR_OUT_OF_RANGE_TOO_SMALL
323
+ build_page_offset_too_small
324
+ end
325
+ end
326
+
327
+ def build_page_limit_error
328
+ case @page_limit_error
329
+ when ERROR_NOT_INTEGER
330
+ build_page_limit_not_integer
331
+ when ERROR_OUT_OF_RANGE_TOO_SMALL
332
+ build_page_limit_too_small
333
+ end
334
+ end
335
+
336
+ def build_page_offset_not_integer
337
+ {
338
+ "status" => "200",
339
+ "source" => { "page_offset" => @requested_page_offset },
340
+ "title" => "Page Offset Invalid",
341
+ "detail" => "Page offset #{@requested_page_offset} is not an Integer. Returning empty results."
342
+ }
343
+ end
344
+
345
+ def build_page_offset_too_large
346
+ {
347
+ "status" => "200",
348
+ "source" => { "page_offset" => @requested_page_offset },
349
+ "title" => "Page Offset Out of Range",
350
+ "detail" => "Page offset #{@requested_page_offset} > #{total_num_found} (total number of results). Returning empty results."
351
+ }
352
+ end
353
+
354
+ def build_page_offset_too_small
355
+ {
356
+ "status" => "200",
357
+ "source" => { "page_offset" => page_offset.to_s },
358
+ "title" => "Page Offset Out of Range",
359
+ "detail" => "Page offset #{@requested_page_offset} < 1 (first result). Returning empty results."
360
+ }
361
+ end
362
+
363
+ def build_page_limit_not_integer
364
+ {
365
+ "status" => "200",
366
+ "source" => { "page_limit" => @requested_page_limit },
367
+ "title" => "Page Limit Invalid",
368
+ "detail" => "Page limit #{@requested_page_limit} is not an Integer. Returning empty results."
369
+ }
370
+ end
371
+
372
+ def build_page_limit_too_small
373
+ {
374
+ "status" => "200",
375
+ "source" => { "page_limit" => @requested_page_limit.to_s },
376
+ "title" => "Page Limit Out of Range",
377
+ "detail" => "Page limit #{@requested_page_limit} < 1 (minimum limit). Returning empty results."
378
+ }
379
+ end
380
+
381
+ def request_params
382
+ @request_params ||= @request.params
383
+ end
384
+
385
+ def request_query_params
386
+ @request_query_params ||= @request.query_parameters
387
+ end
388
+
389
+ def request_query_string
390
+ @request_query_string ||= @request.query_string
391
+ end
392
+
393
+ def request_base_url
394
+ @request_base_url ||= @request.base_url
395
+ end
396
+
397
+ def request_path
398
+ @request_path ||= @request.path
399
+ end
400
+
401
+ # @return [Boolean] true if results should be formatted according to JSON API standard
402
+ def json_api?
403
+ format == :jsonapi
404
+ end
405
+
406
+ # @param [Symbol] The requested format of the response (default=:json)
407
+ # @note Supported formats include [:json | :json-api]. For backward compatibility,
408
+ # it defaults to :json.
409
+ def format
410
+ return @format if @format.present?
411
+ return @format = @requested_format if [:json, :jsonapi].include? @requested_format
412
+ Rails.logger.warn("Format '#{@requested_format}' is not a valid format for search. Supported formats are [:json, :jsonapi]. Defaulting to :json.")
413
+ @format = :json
414
+ end
415
+
416
+ # @return [Integer] The first record to include in the response data. (default=1).
417
+ # @note The first record may be out of range (i.e., < 1 or > total_num_of_results),
418
+ # but it will always be numeric, defaulting to 1 if not specified or not an Integer.
419
+ def page_offset
420
+ return @page_offset if @page_offset.present?
421
+ return @page_offset = 1 unless page_offset_specified?
422
+ @page_offset = validated_request_page_offset || 1
423
+ end
424
+
425
+ # @return [Boolean] true if the request specifies the page offset; otherwise, false
426
+ def page_offset_specified?
427
+ request_params.keys.include?("page_offset") || request_params.keys.include?("startRecord")
428
+ end
429
+
430
+ # @return [Integer] The page offset as specified in the request_params, nil if invalid.
431
+ # @note The page offset can be specified with page_offset (preferred) or
432
+ # startRecord (deprecated, supported for backward compatibility with
433
+ # linked_data module pagination).
434
+ def requested_page_offset
435
+ return @requested_page_offset if @requested_page_offset.present?
436
+ @requested_page_offset = (request_params['page_offset'] || request_params['startRecord'])
437
+ end
438
+
439
+ # @return [Integer] The first record to include in the response data as
440
+ # requested as long as it is an Integer; otherwise, returns nil.
441
+ def validated_request_page_offset
442
+ @page_offset_error = false
443
+ offset = Integer(requested_page_offset)
444
+ return offset if offset == 1
445
+ @page_offset_error = ERROR_OUT_OF_RANGE_TOO_SMALL if offset < 1
446
+ @page_offset_error = ERROR_OUT_OF_RANGE_TOO_LARGE if offset > total_num_found
447
+ offset
448
+ rescue ArgumentError
449
+ @page_offset_error = ERROR_NOT_INTEGER
450
+ nil
451
+ end
452
+
453
+ # @return [Integer] The requested maximum number of results to return (default=DEFAULT_PAGE_LIMIT | ALL)
454
+ # @see #default_page_limit
455
+ # @see DEFAULT_PAGE_LIMIT
456
+ def page_limit
457
+ return @page_limit if @page_limit.present?
458
+ return @page_limit = default_page_limit unless page_limit_specified?
459
+ @page_limit = validated_request_page_limit || default_page_limit
460
+ end
461
+
462
+ # @return [Boolean] true if the request specifies the page limit; otherwise, false
463
+ def page_limit_specified?
464
+ request_params.keys.include?("page_limit") || request_params.keys.include?("maxRecords")
465
+ end
466
+
467
+ # @return [Integer] The max number of records for a page as specified in the request_params.
468
+ # @note The page size limit can be specified with page_limit (preferred) or
469
+ # maxRecords (deprecated, supported for backward compatibility with
470
+ # linked_data module pagination).
471
+ def requested_page_limit
472
+ return @requested_page_limit if @requested_page_limit.present?
473
+ @requested_page_limit ||= (request_params['page_limit'] || request_params['maxRecords'])
474
+ end
475
+
476
+ # @return [Integer] The max number of records to include in response data as
477
+ # requested as long as it is a positive Integer; otherwise, returns nil.
478
+ def validated_request_page_limit
479
+ @page_limit_error = false
480
+ limit = Integer(requested_page_limit)
481
+ @page_limit_error = ERROR_OUT_OF_RANGE_TOO_SMALL if limit < 1
482
+ limit.positive? ? limit : nil
483
+ rescue ArgumentError
484
+ @page_limit_error = ERROR_NOT_INTEGER
485
+ nil
486
+ end
487
+
488
+ # @return [Integer] The default to use when page_limit isn't specified.
489
+ # @note To maintain backward compatibility, the limit will be all results
490
+ # if format is json and neither page_limit nor page_offset were specified.
491
+ def default_page_limit
492
+ return total_num_found unless json_api? || page_offset_specified? || page_limit_specified?
493
+ DEFAULT_PAGE_LIMIT
494
+ end
495
+
496
+ # @return [Integer] the index into the terms Array for the first record to
497
+ # include in the page data
498
+ # @note page_offset begins counting at 1 and the Array index begins at 0.
499
+ def start_index
500
+ @start_index ||= page_offset - 1
501
+ end
502
+
503
+ # @return [Integer] the index into the terms Array for the last record to
504
+ # include in the page data
505
+ def last_index
506
+ return @last_index if @last_index.present?
507
+ return @last_index = start_index if start_index >= last_possible_index
508
+ last_index = start_index + page_limit - 1
509
+ @last_index = last_index <= last_possible_index ? last_index : last_possible_index
510
+ end
511
+
512
+ # @return [Integer] the index for the last term in the Array
513
+ def last_possible_index
514
+ total_num_found - 1
515
+ end
516
+
517
+ # @return [Integer] actual number of results in the returned page of results;
518
+ # 0 if request is out of range
519
+ def actual_page_size
520
+ @actual_page_size ||= start_index <= last_possible_index ? last_index - start_index + 1 : 0
521
+ end
522
+
523
+ # @return [Integer] total number of terms matching the search query
524
+ def total_num_found
525
+ @results.length
526
+ end
527
+
528
+ # @return the URL to current page of results
529
+ def self_link
530
+ url_with(page_offset: page_offset)
531
+ end
532
+
533
+ # @return the URL to the first page of results
534
+ def first_link
535
+ url_with(page_offset: 1)
536
+ end
537
+
538
+ # @return the URL to the last page of results
539
+ def last_link
540
+ last_start = (total_num_found / page_limit) * page_limit + 1
541
+ last_start -= page_limit if last_start > total_num_found
542
+ last_start = 1 if last_start < 1
543
+ url_with(page_offset: last_start)
544
+ end
545
+
546
+ # @return the URL to the next page of results; nil if on last page
547
+ def next_link
548
+ next_start = page_offset + page_limit
549
+ next_start <= total_num_found ? url_with(page_offset: next_start) : nil
550
+ end
551
+
552
+ # @return the URL to the previous page of results; nil if on first page
553
+ def prev_link
554
+ return if page_offset == 1
555
+ prev_start = page_offset - page_limit
556
+ prev_start >= 1 ? url_with(page_offset: prev_start) : url_with(page_offset: 1)
557
+ end
558
+
559
+ # @return the original URL without the parameters
560
+ def url_without_parameters
561
+ URI.parse(request_base_url + request_path)
562
+ end
563
+
564
+ # Generate a URL based off the original URL and parameter values with page_offset
565
+ # updated based on the passed in value.
566
+ # @param page_offset [Integer] the value to use for page_offset
567
+ # @return [String] a full URL with the updated page_offset
568
+ def url_with(page_offset:)
569
+ updated_params = update_parameters(page_offset)
570
+
571
+ uri = url_without_parameters
572
+ uri.query = URI.encode_www_form(updated_params)
573
+ uri.to_s
574
+ end
575
+
576
+ # @param page_offset [Integer] the value to use for page_offset
577
+ # @return [Hash] parameter key-value pairs formatted for the URL using
578
+ # the preferred parameter name and updated page_offset value
579
+ def update_parameters(page_offset)
580
+ updated_params = request_query_params.except('page_offset', 'page_limit')
581
+ updated_params['page_limit'] = page_limit
582
+ updated_params['page_offset'] = page_offset
583
+ updated_params
584
+ end
585
+ end
586
+ end
@@ -1 +1,3 @@
1
- Qa::Authorities::Local.load_config(File.expand_path("../../authorities.yml", __FILE__))
1
+ Rails.application.reloader.to_prepare do
2
+ Qa::Authorities::Local.load_config(File.expand_path("../../authorities.yml", __FILE__))
3
+ end