sanger-jsonapi-resources 0.1.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/LICENSE.txt +22 -0
- data/README.md +53 -0
- data/lib/generators/jsonapi/USAGE +13 -0
- data/lib/generators/jsonapi/controller_generator.rb +14 -0
- data/lib/generators/jsonapi/resource_generator.rb +14 -0
- data/lib/generators/jsonapi/templates/jsonapi_controller.rb +4 -0
- data/lib/generators/jsonapi/templates/jsonapi_resource.rb +4 -0
- data/lib/jsonapi/acts_as_resource_controller.rb +320 -0
- data/lib/jsonapi/cached_resource_fragment.rb +127 -0
- data/lib/jsonapi/callbacks.rb +51 -0
- data/lib/jsonapi/compiled_json.rb +36 -0
- data/lib/jsonapi/configuration.rb +258 -0
- data/lib/jsonapi/error.rb +47 -0
- data/lib/jsonapi/error_codes.rb +60 -0
- data/lib/jsonapi/exceptions.rb +563 -0
- data/lib/jsonapi/formatter.rb +169 -0
- data/lib/jsonapi/include_directives.rb +100 -0
- data/lib/jsonapi/link_builder.rb +152 -0
- data/lib/jsonapi/mime_types.rb +41 -0
- data/lib/jsonapi/naive_cache.rb +30 -0
- data/lib/jsonapi/operation.rb +24 -0
- data/lib/jsonapi/operation_dispatcher.rb +88 -0
- data/lib/jsonapi/operation_result.rb +65 -0
- data/lib/jsonapi/operation_results.rb +35 -0
- data/lib/jsonapi/paginator.rb +209 -0
- data/lib/jsonapi/processor.rb +328 -0
- data/lib/jsonapi/relationship.rb +94 -0
- data/lib/jsonapi/relationship_builder.rb +167 -0
- data/lib/jsonapi/request_parser.rb +678 -0
- data/lib/jsonapi/resource.rb +1255 -0
- data/lib/jsonapi/resource_controller.rb +5 -0
- data/lib/jsonapi/resource_controller_metal.rb +16 -0
- data/lib/jsonapi/resource_serializer.rb +531 -0
- data/lib/jsonapi/resources/version.rb +5 -0
- data/lib/jsonapi/response_document.rb +135 -0
- data/lib/jsonapi/routing_ext.rb +262 -0
- data/lib/jsonapi-resources.rb +27 -0
- metadata +223 -0
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'jsonapi/formatter'
|
2
|
+
require 'jsonapi/processor'
|
3
|
+
require 'concurrent'
|
4
|
+
|
5
|
+
module JSONAPI
|
6
|
+
class Configuration
|
7
|
+
attr_reader :json_key_format,
|
8
|
+
:resource_key_type,
|
9
|
+
:route_format,
|
10
|
+
:raise_if_parameters_not_allowed,
|
11
|
+
:allow_include,
|
12
|
+
:allow_sort,
|
13
|
+
:allow_filter,
|
14
|
+
:default_paginator,
|
15
|
+
:default_page_size,
|
16
|
+
:maximum_page_size,
|
17
|
+
:default_processor_klass,
|
18
|
+
:use_text_errors,
|
19
|
+
:top_level_links_include_pagination,
|
20
|
+
:top_level_meta_include_record_count,
|
21
|
+
:top_level_meta_record_count_key,
|
22
|
+
:top_level_meta_include_page_count,
|
23
|
+
:top_level_meta_page_count_key,
|
24
|
+
:allow_transactions,
|
25
|
+
:include_backtraces_in_errors,
|
26
|
+
:exception_class_whitelist,
|
27
|
+
:whitelist_all_exceptions,
|
28
|
+
:always_include_to_one_linkage_data,
|
29
|
+
:always_include_to_many_linkage_data,
|
30
|
+
:cache_formatters,
|
31
|
+
:use_relationship_reflection,
|
32
|
+
:resource_cache,
|
33
|
+
:default_resource_cache_field,
|
34
|
+
:resource_cache_digest_function,
|
35
|
+
:resource_cache_usage_report_function
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
#:underscored_key, :camelized_key, :dasherized_key, or custom
|
39
|
+
self.json_key_format = :dasherized_key
|
40
|
+
|
41
|
+
#:underscored_route, :camelized_route, :dasherized_route, or custom
|
42
|
+
self.route_format = :dasherized_route
|
43
|
+
|
44
|
+
#:integer, :uuid, :string, or custom (provide a proc)
|
45
|
+
self.resource_key_type = :integer
|
46
|
+
|
47
|
+
# optional request features
|
48
|
+
self.allow_include = true
|
49
|
+
self.allow_sort = true
|
50
|
+
self.allow_filter = true
|
51
|
+
|
52
|
+
self.raise_if_parameters_not_allowed = true
|
53
|
+
|
54
|
+
# :none, :offset, :paged, or a custom paginator name
|
55
|
+
self.default_paginator = :none
|
56
|
+
|
57
|
+
# Output pagination links at top level
|
58
|
+
self.top_level_links_include_pagination = true
|
59
|
+
|
60
|
+
self.default_page_size = 10
|
61
|
+
self.maximum_page_size = 20
|
62
|
+
|
63
|
+
# Metadata
|
64
|
+
# Output record count in top level meta for find operation
|
65
|
+
self.top_level_meta_include_record_count = false
|
66
|
+
self.top_level_meta_record_count_key = :record_count
|
67
|
+
|
68
|
+
self.top_level_meta_include_page_count = false
|
69
|
+
self.top_level_meta_page_count_key = :page_count
|
70
|
+
|
71
|
+
self.use_text_errors = false
|
72
|
+
|
73
|
+
# Whether or not to include exception backtraces in JSONAPI error
|
74
|
+
# responses. Defaults to `false` in production, and `true` otherwise.
|
75
|
+
self.include_backtraces_in_errors = !Rails.env.production?
|
76
|
+
|
77
|
+
# List of classes that should not be rescued by the operations processor.
|
78
|
+
# For example, if you use Pundit for authorization, you might
|
79
|
+
# raise a Pundit::NotAuthorizedError at some point during operations
|
80
|
+
# processing. If you want to use Rails' `rescue_from` macro to
|
81
|
+
# catch this error and render a 403 status code, you should add
|
82
|
+
# the `Pundit::NotAuthorizedError` to the `exception_class_whitelist`.
|
83
|
+
self.exception_class_whitelist = []
|
84
|
+
|
85
|
+
# If enabled, will override configuration option `exception_class_whitelist`
|
86
|
+
# and whitelist all exceptions.
|
87
|
+
self.whitelist_all_exceptions = false
|
88
|
+
|
89
|
+
# Resource Linkage
|
90
|
+
# Controls the serialization of resource linkage for non compound documents
|
91
|
+
# NOTE: always_include_to_many_linkage_data is not currently implemented
|
92
|
+
self.always_include_to_one_linkage_data = false
|
93
|
+
self.always_include_to_many_linkage_data = false
|
94
|
+
|
95
|
+
# The default Operation Processor to use if one is not defined specifically
|
96
|
+
# for a Resource.
|
97
|
+
self.default_processor_klass = JSONAPI::Processor
|
98
|
+
|
99
|
+
# Allows transactions for creating and updating records
|
100
|
+
# Set this to false if your backend does not support transactions (e.g. Mongodb)
|
101
|
+
self.allow_transactions = true
|
102
|
+
|
103
|
+
# Formatter Caching
|
104
|
+
# Set to false to disable caching of string operations on keys and links.
|
105
|
+
# Note that unlike the resource cache, formatter caching is always done
|
106
|
+
# internally in-memory and per-thread; no ActiveSupport::Cache is used.
|
107
|
+
self.cache_formatters = true
|
108
|
+
|
109
|
+
# Relationship reflection invokes the related resource when updates
|
110
|
+
# are made to a has_many relationship. By default relationship_reflection
|
111
|
+
# is turned off because it imposes a small performance penalty.
|
112
|
+
self.use_relationship_reflection = false
|
113
|
+
|
114
|
+
# Resource cache
|
115
|
+
# An ActiveSupport::Cache::Store or similar, used by Resources with caching enabled.
|
116
|
+
# Set to `nil` (the default) to disable caching, or to `Rails.cache` to use the
|
117
|
+
# Rails cache store.
|
118
|
+
self.resource_cache = nil
|
119
|
+
|
120
|
+
# Default resource cache field
|
121
|
+
# On Resources with caching enabled, this field will be used to check for out-of-date
|
122
|
+
# cache entries, unless overridden on a specific Resource. Defaults to "updated_at".
|
123
|
+
self.default_resource_cache_field = :updated_at
|
124
|
+
|
125
|
+
# Resource cache digest function
|
126
|
+
# Provide a callable that returns a unique value for string inputs with
|
127
|
+
# low chance of collision. The default is SHA256 base64.
|
128
|
+
self.resource_cache_digest_function = Digest::SHA2.new.method(:base64digest)
|
129
|
+
|
130
|
+
# Resource cache usage reporting
|
131
|
+
# Optionally provide a callable which JSONAPI will call with information about cache
|
132
|
+
# performance. Should accept three arguments: resource name, hits count, misses count.
|
133
|
+
self.resource_cache_usage_report_function = nil
|
134
|
+
end
|
135
|
+
|
136
|
+
def cache_formatters=(bool)
|
137
|
+
@cache_formatters = bool
|
138
|
+
if bool
|
139
|
+
@key_formatter_tlv = Concurrent::ThreadLocalVar.new
|
140
|
+
@route_formatter_tlv = Concurrent::ThreadLocalVar.new
|
141
|
+
else
|
142
|
+
@key_formatter_tlv = nil
|
143
|
+
@route_formatter_tlv = nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def json_key_format=(format)
|
148
|
+
@json_key_format = format
|
149
|
+
if defined?(@cache_formatters)
|
150
|
+
@key_formatter_tlv = Concurrent::ThreadLocalVar.new
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def route_format=(format)
|
155
|
+
@route_format = format
|
156
|
+
if defined?(@cache_formatters)
|
157
|
+
@route_formatter_tlv = Concurrent::ThreadLocalVar.new
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def key_formatter
|
162
|
+
if self.cache_formatters
|
163
|
+
formatter = @key_formatter_tlv.value
|
164
|
+
return formatter if formatter
|
165
|
+
end
|
166
|
+
|
167
|
+
formatter = JSONAPI::Formatter.formatter_for(self.json_key_format)
|
168
|
+
|
169
|
+
if self.cache_formatters
|
170
|
+
formatter = @key_formatter_tlv.value = formatter.cached
|
171
|
+
end
|
172
|
+
|
173
|
+
return formatter
|
174
|
+
end
|
175
|
+
|
176
|
+
def resource_key_type=(key_type)
|
177
|
+
@resource_key_type = key_type
|
178
|
+
end
|
179
|
+
|
180
|
+
def route_formatter
|
181
|
+
if self.cache_formatters
|
182
|
+
formatter = @route_formatter_tlv.value
|
183
|
+
return formatter if formatter
|
184
|
+
end
|
185
|
+
|
186
|
+
formatter = JSONAPI::Formatter.formatter_for(self.route_format)
|
187
|
+
|
188
|
+
if self.cache_formatters
|
189
|
+
formatter = @route_formatter_tlv.value = formatter.cached
|
190
|
+
end
|
191
|
+
|
192
|
+
return formatter
|
193
|
+
end
|
194
|
+
|
195
|
+
def exception_class_whitelisted?(e)
|
196
|
+
@whitelist_all_exceptions ||
|
197
|
+
@exception_class_whitelist.flatten.any? { |k| e.class.ancestors.map(&:to_s).include?(k.to_s) }
|
198
|
+
end
|
199
|
+
|
200
|
+
def default_processor_klass=(default_processor_klass)
|
201
|
+
@default_processor_klass = default_processor_klass
|
202
|
+
end
|
203
|
+
|
204
|
+
attr_writer :allow_include, :allow_sort, :allow_filter
|
205
|
+
|
206
|
+
attr_writer :default_paginator
|
207
|
+
|
208
|
+
attr_writer :default_page_size
|
209
|
+
|
210
|
+
attr_writer :maximum_page_size
|
211
|
+
|
212
|
+
attr_writer :use_text_errors
|
213
|
+
|
214
|
+
attr_writer :top_level_links_include_pagination
|
215
|
+
|
216
|
+
attr_writer :top_level_meta_include_record_count
|
217
|
+
|
218
|
+
attr_writer :top_level_meta_record_count_key
|
219
|
+
|
220
|
+
attr_writer :top_level_meta_include_page_count
|
221
|
+
|
222
|
+
attr_writer :top_level_meta_page_count_key
|
223
|
+
|
224
|
+
attr_writer :allow_transactions
|
225
|
+
|
226
|
+
attr_writer :include_backtraces_in_errors
|
227
|
+
|
228
|
+
attr_writer :exception_class_whitelist
|
229
|
+
|
230
|
+
attr_writer :whitelist_all_exceptions
|
231
|
+
|
232
|
+
attr_writer :always_include_to_one_linkage_data
|
233
|
+
|
234
|
+
attr_writer :always_include_to_many_linkage_data
|
235
|
+
|
236
|
+
attr_writer :raise_if_parameters_not_allowed
|
237
|
+
|
238
|
+
attr_writer :use_relationship_reflection
|
239
|
+
|
240
|
+
attr_writer :resource_cache
|
241
|
+
|
242
|
+
attr_writer :default_resource_cache_field
|
243
|
+
|
244
|
+
attr_writer :resource_cache_digest_function
|
245
|
+
|
246
|
+
attr_writer :resource_cache_usage_report_function
|
247
|
+
end
|
248
|
+
|
249
|
+
class << self
|
250
|
+
attr_accessor :configuration
|
251
|
+
end
|
252
|
+
|
253
|
+
@configuration ||= Configuration.new
|
254
|
+
|
255
|
+
def self.configure
|
256
|
+
yield(@configuration)
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
class Error
|
3
|
+
attr_accessor :title, :detail, :id, :href, :code, :source, :links, :status, :meta
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@title = options[:title]
|
7
|
+
@detail = options[:detail]
|
8
|
+
@id = options[:id]
|
9
|
+
@href = options[:href]
|
10
|
+
@code = if JSONAPI.configuration.use_text_errors
|
11
|
+
TEXT_ERRORS[options[:code]]
|
12
|
+
else
|
13
|
+
options[:code]
|
14
|
+
end
|
15
|
+
@source = options[:source]
|
16
|
+
@links = options[:links]
|
17
|
+
|
18
|
+
@status = Rack::Utils::SYMBOL_TO_STATUS_CODE[options[:status]].to_s
|
19
|
+
@meta = options[:meta]
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
hash = {}
|
24
|
+
instance_variables.each {|var| hash[var.to_s.delete('@')] = instance_variable_get(var) unless instance_variable_get(var).nil? }
|
25
|
+
hash
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Warning
|
30
|
+
attr_accessor :title, :detail, :code
|
31
|
+
def initialize(options = {})
|
32
|
+
@title = options[:title]
|
33
|
+
@detail = options[:detail]
|
34
|
+
@code = if JSONAPI.configuration.use_text_errors
|
35
|
+
TEXT_ERRORS[options[:code]]
|
36
|
+
else
|
37
|
+
options[:code]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_hash
|
42
|
+
hash = {}
|
43
|
+
instance_variables.each {|var| hash[var.to_s.delete('@')] = instance_variable_get(var) unless instance_variable_get(var).nil? }
|
44
|
+
hash
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
VALIDATION_ERROR = '100'
|
3
|
+
INVALID_RESOURCE = '101'
|
4
|
+
FILTER_NOT_ALLOWED = '102'
|
5
|
+
INVALID_FIELD_VALUE = '103'
|
6
|
+
INVALID_FIELD = '104'
|
7
|
+
PARAM_NOT_ALLOWED = '105'
|
8
|
+
PARAM_MISSING = '106'
|
9
|
+
INVALID_FILTER_VALUE = '107'
|
10
|
+
KEY_ORDER_MISMATCH = '109'
|
11
|
+
KEY_NOT_INCLUDED_IN_URL = '110'
|
12
|
+
INVALID_INCLUDE = '112'
|
13
|
+
RELATION_EXISTS = '113'
|
14
|
+
INVALID_SORT_CRITERIA = '114'
|
15
|
+
INVALID_LINKS_OBJECT = '115'
|
16
|
+
TYPE_MISMATCH = '116'
|
17
|
+
INVALID_PAGE_OBJECT = '117'
|
18
|
+
INVALID_PAGE_VALUE = '118'
|
19
|
+
INVALID_FIELD_FORMAT = '119'
|
20
|
+
INVALID_FILTERS_SYNTAX = '120'
|
21
|
+
SAVE_FAILED = '121'
|
22
|
+
INVALID_DATA_FORMAT = '122'
|
23
|
+
BAD_REQUEST = '400'
|
24
|
+
FORBIDDEN = '403'
|
25
|
+
RECORD_NOT_FOUND = '404'
|
26
|
+
NOT_ACCEPTABLE = '406'
|
27
|
+
UNSUPPORTED_MEDIA_TYPE = '415'
|
28
|
+
LOCKED = '423'
|
29
|
+
INTERNAL_SERVER_ERROR = '500'
|
30
|
+
|
31
|
+
TEXT_ERRORS =
|
32
|
+
{ VALIDATION_ERROR => 'VALIDATION_ERROR',
|
33
|
+
INVALID_RESOURCE => 'INVALID_RESOURCE',
|
34
|
+
FILTER_NOT_ALLOWED => 'FILTER_NOT_ALLOWED',
|
35
|
+
INVALID_FIELD_VALUE => 'INVALID_FIELD_VALUE',
|
36
|
+
INVALID_FIELD => 'INVALID_FIELD',
|
37
|
+
PARAM_NOT_ALLOWED => 'PARAM_NOT_ALLOWED',
|
38
|
+
PARAM_MISSING => 'PARAM_MISSING',
|
39
|
+
INVALID_FILTER_VALUE => 'INVALID_FILTER_VALUE',
|
40
|
+
KEY_ORDER_MISMATCH => 'KEY_ORDER_MISMATCH',
|
41
|
+
KEY_NOT_INCLUDED_IN_URL => 'KEY_NOT_INCLUDED_IN_URL',
|
42
|
+
INVALID_INCLUDE => 'INVALID_INCLUDE',
|
43
|
+
RELATION_EXISTS => 'RELATION_EXISTS',
|
44
|
+
INVALID_SORT_CRITERIA => 'INVALID_SORT_CRITERIA',
|
45
|
+
INVALID_LINKS_OBJECT => 'INVALID_LINKS_OBJECT',
|
46
|
+
TYPE_MISMATCH => 'TYPE_MISMATCH',
|
47
|
+
INVALID_PAGE_OBJECT => 'INVALID_PAGE_OBJECT',
|
48
|
+
INVALID_PAGE_VALUE => 'INVALID_PAGE_VALUE',
|
49
|
+
INVALID_FIELD_FORMAT => 'INVALID_FIELD_FORMAT',
|
50
|
+
INVALID_FILTERS_SYNTAX => 'INVALID_FILTERS_SYNTAX',
|
51
|
+
SAVE_FAILED => 'SAVE_FAILED',
|
52
|
+
INVALID_DATA_FORMAT => 'INVALID_DATA_FORMAT',
|
53
|
+
FORBIDDEN => 'FORBIDDEN',
|
54
|
+
RECORD_NOT_FOUND => 'RECORD_NOT_FOUND',
|
55
|
+
NOT_ACCEPTABLE => 'NOT_ACCEPTABLE',
|
56
|
+
UNSUPPORTED_MEDIA_TYPE => 'UNSUPPORTED_MEDIA_TYPE',
|
57
|
+
LOCKED => 'LOCKED',
|
58
|
+
INTERNAL_SERVER_ERROR => 'INTERNAL_SERVER_ERROR'
|
59
|
+
}
|
60
|
+
end
|