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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +53 -0
  4. data/lib/generators/jsonapi/USAGE +13 -0
  5. data/lib/generators/jsonapi/controller_generator.rb +14 -0
  6. data/lib/generators/jsonapi/resource_generator.rb +14 -0
  7. data/lib/generators/jsonapi/templates/jsonapi_controller.rb +4 -0
  8. data/lib/generators/jsonapi/templates/jsonapi_resource.rb +4 -0
  9. data/lib/jsonapi/acts_as_resource_controller.rb +320 -0
  10. data/lib/jsonapi/cached_resource_fragment.rb +127 -0
  11. data/lib/jsonapi/callbacks.rb +51 -0
  12. data/lib/jsonapi/compiled_json.rb +36 -0
  13. data/lib/jsonapi/configuration.rb +258 -0
  14. data/lib/jsonapi/error.rb +47 -0
  15. data/lib/jsonapi/error_codes.rb +60 -0
  16. data/lib/jsonapi/exceptions.rb +563 -0
  17. data/lib/jsonapi/formatter.rb +169 -0
  18. data/lib/jsonapi/include_directives.rb +100 -0
  19. data/lib/jsonapi/link_builder.rb +152 -0
  20. data/lib/jsonapi/mime_types.rb +41 -0
  21. data/lib/jsonapi/naive_cache.rb +30 -0
  22. data/lib/jsonapi/operation.rb +24 -0
  23. data/lib/jsonapi/operation_dispatcher.rb +88 -0
  24. data/lib/jsonapi/operation_result.rb +65 -0
  25. data/lib/jsonapi/operation_results.rb +35 -0
  26. data/lib/jsonapi/paginator.rb +209 -0
  27. data/lib/jsonapi/processor.rb +328 -0
  28. data/lib/jsonapi/relationship.rb +94 -0
  29. data/lib/jsonapi/relationship_builder.rb +167 -0
  30. data/lib/jsonapi/request_parser.rb +678 -0
  31. data/lib/jsonapi/resource.rb +1255 -0
  32. data/lib/jsonapi/resource_controller.rb +5 -0
  33. data/lib/jsonapi/resource_controller_metal.rb +16 -0
  34. data/lib/jsonapi/resource_serializer.rb +531 -0
  35. data/lib/jsonapi/resources/version.rb +5 -0
  36. data/lib/jsonapi/response_document.rb +135 -0
  37. data/lib/jsonapi/routing_ext.rb +262 -0
  38. data/lib/jsonapi-resources.rb +27 -0
  39. 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