qa 5.5.2 → 5.8.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.
@@ -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
@@ -0,0 +1,4 @@
1
+ Mime::Type.register 'application/vnd.api+json', :jsonapi
2
+ Mime::Type.register 'application/ld+json', :jsonld
3
+ Mime::Type.register 'text/n3', :n3
4
+ Mime::Type.register 'application/n-triples', :ntriples
data/config/routes.rb CHANGED
@@ -9,6 +9,8 @@ Qa::Engine.routes.draw do
9
9
  get "/search/:vocab(/:subauthority)", controller: :terms, action: :search
10
10
  get "/show/:vocab/:id", controller: :terms, action: :show
11
11
  get "/show/:vocab/:subauthority/:id", controller: :terms, action: :show
12
+ get "/fetch/:vocab", controller: :terms, action: :fetch
13
+ get "/fetch/:vocab/:subauthority", controller: :terms, action: :fetch
12
14
 
13
15
  match "/search/linked_data/:vocab(/:subauthority)", to: 'application#options', via: [:options]
14
16
  match "/show/linked_data/:vocab/:id", to: 'application#options', via: [:options]
@@ -17,4 +19,6 @@ Qa::Engine.routes.draw do
17
19
  match "/search/:vocab(/:subauthority)", to: 'application#options', via: [:options]
18
20
  match "/show/:vocab/:id", to: 'application#options', via: [:options]
19
21
  match "/show/:vocab/:subauthority/:id", to: 'application#options', via: [:options]
22
+ match "/fetch/:vocab", to: 'application#options', via: [:options]
23
+ match "/fetch/:vocab/:subauthority", to: 'application#options', via: [:options]
20
24
  end
@@ -8,13 +8,14 @@ module Qa::Authorities
8
8
  def search(q)
9
9
  r = q.blank? ? [] : terms.select { |term| /\b#{q.downcase}/.match(term[:term].downcase) }
10
10
  r.map do |res|
11
- { id: res[:id], label: res[:term] }.with_indifferent_access
11
+ { id: res[:id], label: res[:term], uri: res.fetch(:uri, nil) }.compact.with_indifferent_access
12
12
  end
13
13
  end
14
14
 
15
15
  def all
16
16
  terms.map do |res|
17
- { id: res[:id], label: res[:term], active: res.fetch(:active, true) }.with_indifferent_access
17
+ { id: res[:id], label: res[:term], active: res.fetch(:active, true), uri: res.fetch(:uri, nil) }
18
+ .compact.with_indifferent_access
18
19
  end
19
20
  end
20
21
 
data/lib/qa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Qa
2
- VERSION = "5.5.2".freeze
2
+ VERSION = "5.8.1".freeze
3
3
  end