apia 3.0.0
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/VERSION +1 -0
- data/lib/apia.rb +21 -0
- data/lib/apia/api.rb +100 -0
- data/lib/apia/argument_set.rb +221 -0
- data/lib/apia/authenticator.rb +57 -0
- data/lib/apia/callable_with_environment.rb +43 -0
- data/lib/apia/controller.rb +32 -0
- data/lib/apia/defineable.rb +60 -0
- data/lib/apia/definition.rb +27 -0
- data/lib/apia/definitions/api.rb +51 -0
- data/lib/apia/definitions/argument.rb +77 -0
- data/lib/apia/definitions/argument_set.rb +33 -0
- data/lib/apia/definitions/authenticator.rb +46 -0
- data/lib/apia/definitions/controller.rb +41 -0
- data/lib/apia/definitions/endpoint.rb +74 -0
- data/lib/apia/definitions/enum.rb +31 -0
- data/lib/apia/definitions/error.rb +59 -0
- data/lib/apia/definitions/field.rb +117 -0
- data/lib/apia/definitions/lookup_argument_set.rb +27 -0
- data/lib/apia/definitions/object.rb +29 -0
- data/lib/apia/definitions/polymorph.rb +29 -0
- data/lib/apia/definitions/polymorph_option.rb +53 -0
- data/lib/apia/definitions/scalar.rb +23 -0
- data/lib/apia/definitions/type.rb +109 -0
- data/lib/apia/dsl.rb +23 -0
- data/lib/apia/dsls/api.rb +37 -0
- data/lib/apia/dsls/argument.rb +27 -0
- data/lib/apia/dsls/argument_set.rb +35 -0
- data/lib/apia/dsls/authenticator.rb +38 -0
- data/lib/apia/dsls/concerns/has_fields.rb +38 -0
- data/lib/apia/dsls/controller.rb +34 -0
- data/lib/apia/dsls/endpoint.rb +79 -0
- data/lib/apia/dsls/enum.rb +19 -0
- data/lib/apia/dsls/error.rb +26 -0
- data/lib/apia/dsls/field.rb +27 -0
- data/lib/apia/dsls/lookup_argument_set.rb +24 -0
- data/lib/apia/dsls/object.rb +19 -0
- data/lib/apia/dsls/polymorph.rb +19 -0
- data/lib/apia/dsls/route_group.rb +43 -0
- data/lib/apia/dsls/route_set.rb +40 -0
- data/lib/apia/dsls/scalar.rb +23 -0
- data/lib/apia/dsls/scope_descriptions.rb +17 -0
- data/lib/apia/endpoint.rb +110 -0
- data/lib/apia/enum.rb +43 -0
- data/lib/apia/environment_error_handling.rb +74 -0
- data/lib/apia/error.rb +61 -0
- data/lib/apia/error_set.rb +15 -0
- data/lib/apia/errors/error_exception_error.rb +32 -0
- data/lib/apia/errors/field_spec_parse_error.rb +23 -0
- data/lib/apia/errors/invalid_argument_error.rb +68 -0
- data/lib/apia/errors/invalid_enum_option_error.rb +21 -0
- data/lib/apia/errors/invalid_helper_error.rb +6 -0
- data/lib/apia/errors/invalid_json_error.rb +23 -0
- data/lib/apia/errors/invalid_polymorph_value_error.rb +21 -0
- data/lib/apia/errors/invalid_scalar_value_error.rb +21 -0
- data/lib/apia/errors/manifest_error.rb +43 -0
- data/lib/apia/errors/missing_argument_error.rb +40 -0
- data/lib/apia/errors/null_field_value_error.rb +37 -0
- data/lib/apia/errors/parse_error.rb +10 -0
- data/lib/apia/errors/runtime_error.rb +30 -0
- data/lib/apia/errors/scope_not_granted_error.rb +15 -0
- data/lib/apia/errors/standard_error.rb +6 -0
- data/lib/apia/field_set.rb +76 -0
- data/lib/apia/field_spec.rb +155 -0
- data/lib/apia/helpers.rb +34 -0
- data/lib/apia/hook_set.rb +30 -0
- data/lib/apia/lookup_argument_set.rb +57 -0
- data/lib/apia/lookup_environment.rb +27 -0
- data/lib/apia/manifest_errors.rb +62 -0
- data/lib/apia/mock_request.rb +18 -0
- data/lib/apia/object.rb +68 -0
- data/lib/apia/object_set.rb +21 -0
- data/lib/apia/pagination_object.rb +34 -0
- data/lib/apia/polymorph.rb +50 -0
- data/lib/apia/rack.rb +184 -0
- data/lib/apia/rack_error.rb +17 -0
- data/lib/apia/request.rb +67 -0
- data/lib/apia/request_environment.rb +84 -0
- data/lib/apia/request_headers.rb +42 -0
- data/lib/apia/response.rb +64 -0
- data/lib/apia/route.rb +61 -0
- data/lib/apia/route_group.rb +20 -0
- data/lib/apia/route_set.rb +89 -0
- data/lib/apia/scalar.rb +52 -0
- data/lib/apia/scalars.rb +25 -0
- data/lib/apia/scalars/base64.rb +31 -0
- data/lib/apia/scalars/boolean.rb +37 -0
- data/lib/apia/scalars/date.rb +45 -0
- data/lib/apia/scalars/decimal.rb +36 -0
- data/lib/apia/scalars/integer.rb +34 -0
- data/lib/apia/scalars/string.rb +24 -0
- data/lib/apia/scalars/unix_time.rb +40 -0
- data/lib/apia/schema/api_controller_schema_type.rb +17 -0
- data/lib/apia/schema/api_schema_type.rb +43 -0
- data/lib/apia/schema/argument_schema_type.rb +28 -0
- data/lib/apia/schema/argument_set_schema_type.rb +21 -0
- data/lib/apia/schema/authenticator_schema_type.rb +22 -0
- data/lib/apia/schema/controller.rb +39 -0
- data/lib/apia/schema/controller_endpoint_schema_type.rb +17 -0
- data/lib/apia/schema/controller_schema_type.rb +32 -0
- data/lib/apia/schema/endpoint_schema_type.rb +35 -0
- data/lib/apia/schema/enum_schema_type.rb +20 -0
- data/lib/apia/schema/enum_value_schema_type.rb +14 -0
- data/lib/apia/schema/error_schema_type.rb +23 -0
- data/lib/apia/schema/field_schema_type.rb +38 -0
- data/lib/apia/schema/field_spec_options_schema_type.rb +16 -0
- data/lib/apia/schema/lookup_argument_set_schema_type.rb +25 -0
- data/lib/apia/schema/object_schema_polymorph.rb +31 -0
- data/lib/apia/schema/object_schema_type.rb +21 -0
- data/lib/apia/schema/polymorph_option_schema_type.rb +16 -0
- data/lib/apia/schema/polymorph_schema_type.rb +20 -0
- data/lib/apia/schema/request_method_enum.rb +21 -0
- data/lib/apia/schema/route_group_schema_type.rb +19 -0
- data/lib/apia/schema/route_schema_type.rb +31 -0
- data/lib/apia/schema/route_set_schema_type.rb +20 -0
- data/lib/apia/schema/scalar_schema_type.rb +15 -0
- data/lib/apia/schema/scope_type.rb +14 -0
- data/lib/apia/version.rb +12 -0
- metadata +188 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Apia
|
6
|
+
class ObjectSet < ::Set
|
7
|
+
|
8
|
+
def add_object(object)
|
9
|
+
return self if include?(object)
|
10
|
+
|
11
|
+
self << object
|
12
|
+
if object.respond_to?(:collate_objects)
|
13
|
+
# Attempt to add any other objects if the object responds to
|
14
|
+
# collate_objects.
|
15
|
+
object.collate_objects(self)
|
16
|
+
end
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'apia/object'
|
4
|
+
require 'apia/scalars/integer'
|
5
|
+
require 'apia/scalars/boolean'
|
6
|
+
|
7
|
+
module Apia
|
8
|
+
class PaginationObject < Apia::Object
|
9
|
+
|
10
|
+
name 'Pagination Details'
|
11
|
+
description 'Provides information about how data has been paginated'
|
12
|
+
|
13
|
+
field :current_page, type: Scalars::Integer do
|
14
|
+
description 'The current page'
|
15
|
+
end
|
16
|
+
|
17
|
+
field :total_pages, type: Scalars::Integer, null: true do
|
18
|
+
description 'The total number of pages'
|
19
|
+
end
|
20
|
+
|
21
|
+
field :total, type: Scalars::Integer, null: true do
|
22
|
+
description 'The total number of items across all pages'
|
23
|
+
end
|
24
|
+
|
25
|
+
field :per_page, type: Scalars::Integer do
|
26
|
+
description 'The number of items per page'
|
27
|
+
end
|
28
|
+
|
29
|
+
field :large_set, type: Scalars::Boolean do
|
30
|
+
description 'Is this a large set and therefore the total number of records cannot be returned?'
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'apia/helpers'
|
4
|
+
require 'apia/defineable'
|
5
|
+
require 'apia/definitions/polymorph'
|
6
|
+
require 'apia/errors/invalid_polymorph_value_error'
|
7
|
+
|
8
|
+
module Apia
|
9
|
+
class Polymorph
|
10
|
+
|
11
|
+
extend Defineable
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
# Return the definition for this polymorph
|
16
|
+
#
|
17
|
+
# @return [Apia::Definitions::Polymorph]
|
18
|
+
def definition
|
19
|
+
@definition ||= Definitions::Polymorph.new(Helpers.class_name_to_id(name))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Collate all objects that this polymorph references and add them to the
|
23
|
+
# given object set
|
24
|
+
#
|
25
|
+
# @param set [Apia::ObjectSet]
|
26
|
+
# @return [void]
|
27
|
+
def collate_objects(set)
|
28
|
+
definition.options.each_value do |opt|
|
29
|
+
set.add_object(opt.type.klass) if opt.type.usable_for_field?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return the type which should be returned for the given value by running
|
34
|
+
# through each of the matchers to find the appropriate type.
|
35
|
+
def option_for_value(value)
|
36
|
+
option = definition.options.values.find do |opt|
|
37
|
+
opt.matches?(value)
|
38
|
+
end
|
39
|
+
|
40
|
+
if option.nil?
|
41
|
+
raise InvalidPolymorphValueError.new(self, value)
|
42
|
+
end
|
43
|
+
|
44
|
+
option
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/apia/rack.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'apia/rack_error'
|
5
|
+
require 'apia/request'
|
6
|
+
require 'apia/response'
|
7
|
+
|
8
|
+
module Apia
|
9
|
+
class Rack
|
10
|
+
|
11
|
+
def initialize(app, api, namespace, **options)
|
12
|
+
@app = app
|
13
|
+
@api = api
|
14
|
+
@namespace = '/' + namespace.sub(/\A\/+/, '').sub(/\/+\z/, '')
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
# Is this supposed to be running in development? This will validate the whole
|
19
|
+
# API on each request as well as being more verbose about internal server
|
20
|
+
# errors that are encountered.
|
21
|
+
#
|
22
|
+
# @return [Boolean]
|
23
|
+
def development?
|
24
|
+
env_is_dev = ENV['RACK_ENV'] == 'development'
|
25
|
+
return true if env_is_dev && @options[:development].nil?
|
26
|
+
|
27
|
+
@options[:development] == true
|
28
|
+
end
|
29
|
+
|
30
|
+
# Parse a given full path and return nil if it doesn't match our
|
31
|
+
# namespace or return a hash with the controller and endpoint
|
32
|
+
# named as available.
|
33
|
+
#
|
34
|
+
# @param path [String] /core/v1/controller/endpoint
|
35
|
+
# @return [nil, Hash]
|
36
|
+
def find_route(method, path)
|
37
|
+
return if api.nil?
|
38
|
+
|
39
|
+
api.definition.route_set.find(method.to_s.downcase.to_sym, path).first
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return the API object
|
43
|
+
#
|
44
|
+
# @return [Apia::API]
|
45
|
+
def api
|
46
|
+
return Object.const_get(@api) if @api.is_a?(String) && development?
|
47
|
+
return @cached_api ||= Object.const_get(@api) if @api.is_a?(String)
|
48
|
+
|
49
|
+
@api
|
50
|
+
end
|
51
|
+
|
52
|
+
# Actually make the request
|
53
|
+
#
|
54
|
+
# @param env [Hash]
|
55
|
+
# @return [Array] a rack triplet
|
56
|
+
def call(env)
|
57
|
+
if @options[:hosts]&.none? { |host| host == env['HTTP_HOST'] }
|
58
|
+
return @app.call(env)
|
59
|
+
end
|
60
|
+
|
61
|
+
unless env['PATH_INFO'] =~ /\A#{Regexp.escape(@namespace)}\/([a-z].*)\z/i
|
62
|
+
return @app.call(env)
|
63
|
+
end
|
64
|
+
|
65
|
+
api_path = Regexp.last_match(1)
|
66
|
+
|
67
|
+
triplet = handle_request(env, api_path)
|
68
|
+
add_cors_headers(env, triplet)
|
69
|
+
triplet
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def handle_request(env, api_path)
|
75
|
+
if env['REQUEST_METHOD'].upcase == 'OPTIONS'
|
76
|
+
return [204, {}, ['']]
|
77
|
+
end
|
78
|
+
|
79
|
+
validate_api if development?
|
80
|
+
|
81
|
+
route = find_route(env['REQUEST_METHOD'], api_path)
|
82
|
+
if route.nil?
|
83
|
+
raise RackError.new(404, 'route_not_found', "No route matches '#{api_path}' for #{env['REQUEST_METHOD']}")
|
84
|
+
end
|
85
|
+
|
86
|
+
request = Apia::Request.new(env)
|
87
|
+
request.api_path = api_path
|
88
|
+
request.namespace = @namespace
|
89
|
+
request.api = api
|
90
|
+
request.controller = route.controller
|
91
|
+
request.endpoint = route.endpoint
|
92
|
+
request.route = route
|
93
|
+
|
94
|
+
response = request.endpoint.execute(request)
|
95
|
+
response.rack_triplet
|
96
|
+
rescue ::StandardError => e
|
97
|
+
if e.is_a?(RackError) || e.is_a?(Apia::ManifestError)
|
98
|
+
return e.triplet
|
99
|
+
end
|
100
|
+
|
101
|
+
api.definition.exception_handlers.call(e, {
|
102
|
+
env: env,
|
103
|
+
api: api,
|
104
|
+
request: defined?(request) ? request : nil
|
105
|
+
})
|
106
|
+
|
107
|
+
if development?
|
108
|
+
return triplet_for_exception(e)
|
109
|
+
end
|
110
|
+
|
111
|
+
self.class.error_triplet('unhandled_exception', status: 500)
|
112
|
+
end
|
113
|
+
|
114
|
+
def validate_api
|
115
|
+
api.validate_all.raise_if_needed
|
116
|
+
end
|
117
|
+
|
118
|
+
def triplet_for_exception(exception)
|
119
|
+
self.class.error_triplet(
|
120
|
+
'unhandled_exception',
|
121
|
+
description: 'This is an exception that has occurred and not been handled.',
|
122
|
+
detail: {
|
123
|
+
class: exception.class.name,
|
124
|
+
message: exception.message,
|
125
|
+
backtrace: exception.backtrace
|
126
|
+
},
|
127
|
+
status: 500
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Add cross origin headers to the response triplet
|
132
|
+
#
|
133
|
+
# @param env [Hash]
|
134
|
+
# @param triplet [Array]
|
135
|
+
# @return [void]
|
136
|
+
def add_cors_headers(env, triplet)
|
137
|
+
triplet[1]['Access-Control-Allow-Origin'] = '*'
|
138
|
+
triplet[1]['Access-Control-Allow-Methods'] = '*'
|
139
|
+
if env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']
|
140
|
+
triplet[1]['Access-Control-Allow-Headers'] = env['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']
|
141
|
+
end
|
142
|
+
|
143
|
+
true
|
144
|
+
end
|
145
|
+
|
146
|
+
class << self
|
147
|
+
|
148
|
+
# Return a JSON-ready triplet for the given body.
|
149
|
+
#
|
150
|
+
# @param body [Hash, Array]
|
151
|
+
# @param status [Integer]
|
152
|
+
# @param headers [Hash]
|
153
|
+
# @return [Array]
|
154
|
+
def json_triplet(body, status: 200, headers: {})
|
155
|
+
body_as_json = body.to_json
|
156
|
+
[
|
157
|
+
status,
|
158
|
+
headers.merge('content-type' => 'application/json', 'content-length' => body_as_json.bytesize.to_s),
|
159
|
+
[body_as_json]
|
160
|
+
]
|
161
|
+
end
|
162
|
+
|
163
|
+
# Return a triplet for a given error using the standard error schema
|
164
|
+
#
|
165
|
+
# @param code [String]
|
166
|
+
# @param description [String]
|
167
|
+
# @param detail [Hash]
|
168
|
+
# @param status [Integer]
|
169
|
+
# @param headers [Hash]
|
170
|
+
# @return [Array]
|
171
|
+
def error_triplet(code, description: nil, detail: {}, status: 500, headers: {})
|
172
|
+
json_triplet({
|
173
|
+
error: {
|
174
|
+
code: code,
|
175
|
+
description: description,
|
176
|
+
detail: detail
|
177
|
+
}
|
178
|
+
}, status: status, headers: headers.merge('x-api-schema' => 'json-error'))
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apia
|
4
|
+
class RackError < StandardError
|
5
|
+
|
6
|
+
def initialize(http_status, code, message)
|
7
|
+
@http_status = http_status
|
8
|
+
@code = code
|
9
|
+
@message = message
|
10
|
+
end
|
11
|
+
|
12
|
+
def triplet
|
13
|
+
Rack.error_triplet(@code, description: @message, status: @http_status)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/lib/apia/request.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack/request'
|
4
|
+
require 'apia/request_headers'
|
5
|
+
require 'apia/errors/invalid_json_error'
|
6
|
+
|
7
|
+
module Apia
|
8
|
+
class Request < Rack::Request
|
9
|
+
|
10
|
+
attr_accessor :api
|
11
|
+
attr_accessor :controller
|
12
|
+
attr_accessor :endpoint
|
13
|
+
attr_accessor :identity
|
14
|
+
attr_writer :arguments
|
15
|
+
attr_accessor :authenticator
|
16
|
+
attr_accessor :namespace
|
17
|
+
attr_accessor :route
|
18
|
+
attr_accessor :api_path
|
19
|
+
|
20
|
+
def self.empty(options: {})
|
21
|
+
new(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def arguments
|
25
|
+
@arguments ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def headers
|
29
|
+
@headers ||= RequestHeaders.create_from_request(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
def json_body
|
33
|
+
return @json_body if instance_variable_defined?('@json_body')
|
34
|
+
|
35
|
+
@json_body = get_json_body_from_body || get_json_body_from_params
|
36
|
+
end
|
37
|
+
|
38
|
+
def body?
|
39
|
+
has_header?('rack.input')
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def parse_json_from_string(body)
|
45
|
+
return {} if body.empty?
|
46
|
+
|
47
|
+
JSON.parse(body)
|
48
|
+
rescue JSON::ParserError => e
|
49
|
+
raise InvalidJSONError, e.message
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_json_body_from_body
|
53
|
+
return unless content_type =~ /\Aapplication\/json/
|
54
|
+
return unless body?
|
55
|
+
|
56
|
+
parse_json_from_string(body.read)
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_json_body_from_params
|
60
|
+
return unless body?
|
61
|
+
return unless params['_arguments'].is_a?(String)
|
62
|
+
|
63
|
+
parse_json_from_string(params['_arguments'])
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'apia/environment_error_handling'
|
4
|
+
require 'apia/errors/invalid_helper_error'
|
5
|
+
|
6
|
+
module Apia
|
7
|
+
class RequestEnvironment
|
8
|
+
|
9
|
+
attr_reader :request
|
10
|
+
attr_reader :response
|
11
|
+
|
12
|
+
include EnvironmentErrorHandling
|
13
|
+
|
14
|
+
def initialize(request, response)
|
15
|
+
@request = request
|
16
|
+
@response = response
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(*args, &block)
|
20
|
+
return unless block_given?
|
21
|
+
|
22
|
+
instance_exec(@request, @response, *args, &block)
|
23
|
+
rescue ::StandardError => e
|
24
|
+
raise_exception(e)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Call a helper
|
28
|
+
#
|
29
|
+
# @param name [Symbol]
|
30
|
+
# @return [Object, nil]
|
31
|
+
def helper(name, *args)
|
32
|
+
helper = @request.controller.definition.helpers[name.to_sym]
|
33
|
+
if helper.nil?
|
34
|
+
raise InvalidHelperError, "No helper found with name #{name}"
|
35
|
+
end
|
36
|
+
|
37
|
+
instance_exec(*args, &helper)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Set appropriate pagination for the given set based on the configuration
|
41
|
+
# specified for the endpoint
|
42
|
+
#
|
43
|
+
# @param set [#limit, #count, #page, #per, #to_a, #total_pages, #current_page, #without_count]
|
44
|
+
# @param large_set [Boolean] whether or not this is expected to be a large set
|
45
|
+
# @return [void]
|
46
|
+
def paginate(set, potentially_large_set: false)
|
47
|
+
paginated_field = @request.endpoint.definition.paginated_field
|
48
|
+
if paginated_field.nil?
|
49
|
+
raise Apia::RuntimeError, 'Could not paginate response because no pagination has been configured for the endpoint'
|
50
|
+
end
|
51
|
+
|
52
|
+
paginated = set.page(@request.arguments[:page] || 1)
|
53
|
+
paginated = paginated.per(@request.arguments[:per_page] || 30)
|
54
|
+
|
55
|
+
large_set = false
|
56
|
+
if potentially_large_set
|
57
|
+
total_count = set.limit(1001).count
|
58
|
+
if total_count > 1000
|
59
|
+
large_set = true
|
60
|
+
paginated = paginated.without_count
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
@response.add_field paginated_field, paginated.to_a
|
65
|
+
|
66
|
+
pagination_info = {}
|
67
|
+
pagination_info[:current_page] = paginated.current_page
|
68
|
+
pagination_info[:per_page] = paginated.limit_value
|
69
|
+
pagination_info[:large_set] = large_set
|
70
|
+
unless large_set
|
71
|
+
pagination_info[:total] = paginated.total_count
|
72
|
+
pagination_info[:total_pages] = paginated.total_pages
|
73
|
+
end
|
74
|
+
@response.add_field :pagination, pagination_info
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def potential_error_sources
|
80
|
+
[@request.endpoint, @request.authenticator].compact
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|