json_api_server 0.0.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.
- checksums.yaml +7 -0
- data/.dockerignore +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.rubocop.yml +35 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Dockerfile +9 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +432 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config/locales/en.yml +32 -0
- data/docker-compose.yml +10 -0
- data/json_api_server.gemspec +50 -0
- data/lib/json_api_server.rb +76 -0
- data/lib/json_api_server/api_version.rb +8 -0
- data/lib/json_api_server/attributes_builder.rb +135 -0
- data/lib/json_api_server/base_serializer.rb +169 -0
- data/lib/json_api_server/builder.rb +201 -0
- data/lib/json_api_server/cast.rb +89 -0
- data/lib/json_api_server/configuration.rb +99 -0
- data/lib/json_api_server/controller/error_handling.rb +164 -0
- data/lib/json_api_server/engine.rb +5 -0
- data/lib/json_api_server/error.rb +64 -0
- data/lib/json_api_server/errors.rb +50 -0
- data/lib/json_api_server/exceptions.rb +6 -0
- data/lib/json_api_server/fields.rb +76 -0
- data/lib/json_api_server/filter.rb +255 -0
- data/lib/json_api_server/filter_builders.rb +135 -0
- data/lib/json_api_server/filter_config.rb +71 -0
- data/lib/json_api_server/filter_parser.rb +88 -0
- data/lib/json_api_server/include.rb +158 -0
- data/lib/json_api_server/meta_builder.rb +39 -0
- data/lib/json_api_server/mime_types.rb +21 -0
- data/lib/json_api_server/pagination.rb +189 -0
- data/lib/json_api_server/paginator.rb +134 -0
- data/lib/json_api_server/relationships_builder.rb +215 -0
- data/lib/json_api_server/resource_serializer.rb +245 -0
- data/lib/json_api_server/resources_serializer.rb +131 -0
- data/lib/json_api_server/serializer.rb +34 -0
- data/lib/json_api_server/sort.rb +156 -0
- data/lib/json_api_server/sort_configs.rb +63 -0
- data/lib/json_api_server/validation_errors.rb +51 -0
- data/lib/json_api_server/version.rb +3 -0
- metadata +259 -0
@@ -0,0 +1,201 @@
|
|
1
|
+
module JsonApiServer # :nodoc:
|
2
|
+
# This class integrates JSON API features -- pagination, sorting, filters, inclusion of
|
3
|
+
# related resources, and sparse fieldsets -- in one place. It collects data to be used
|
4
|
+
# by serializers.
|
5
|
+
#
|
6
|
+
# - It merges JSON API sub-queries (i.e, filters, pagination, sorting, etc.) into a query.
|
7
|
+
# - It provides convenience methods to requested sparse fields, includes and pagination.
|
8
|
+
#
|
9
|
+
# === Usage:
|
10
|
+
#
|
11
|
+
# The Builder class takes two arguments, (1) the controller request and (2) an
|
12
|
+
# initial query (i.e., MyModel.all, MyModel.authorized_to(current_user), etc.).
|
13
|
+
#
|
14
|
+
# Add JSON API features appropriate for the request. These methods are chainable.
|
15
|
+
# Except for sparse fields, JSON API features are configured; filter, include
|
16
|
+
# and sort configurations whitelist attributes/behavior while pagination sets
|
17
|
+
# defaults and limits on number of records to return.
|
18
|
+
#
|
19
|
+
# - <tt>add_pagination(pagination_options)</tt> - for collections
|
20
|
+
# - <tt>add_filter(filter_options)</tt> - for collections
|
21
|
+
# - <tt>add_include(include_options)</tt>
|
22
|
+
# - <tt>add_sort(sort_options)</tt> - for collections
|
23
|
+
# - <tt>add_fields</tt>
|
24
|
+
#
|
25
|
+
# Once features are added, their corresponding values can be accessed:
|
26
|
+
#
|
27
|
+
# - <tt>query</tt> - memoized merged query (merges initial request with sort, pagination, filters, includes sub-queries if any)
|
28
|
+
# - <tt>paginator</tt> - Paginator class for pagination links.
|
29
|
+
# - <tt>includes</tt> - Array of requested (whitelisted) includes (i.e, <tt>['comments', 'comment.author']</tt>)
|
30
|
+
# - <tt>sparse_fields</tt> - Hash of type/fields (i.e., {'articles => ['title', 'body', 'author'], 'people' => ['name']})
|
31
|
+
#
|
32
|
+
# ===== Example
|
33
|
+
# attr_accessor :pagination_options, :sort_options, :filter_options, :include_options
|
34
|
+
#
|
35
|
+
# before_action do |c|
|
36
|
+
# c.pagination_options = { default_per_page: 10, max_per_page: 60 }
|
37
|
+
# c.sort_options = {
|
38
|
+
# permitted: [:character, :location, :published],
|
39
|
+
# default: { id: :desc }
|
40
|
+
# }
|
41
|
+
# c.filter_options = [
|
42
|
+
# { id: { type: 'Integer' } },
|
43
|
+
# { published: { type: 'Date' } },
|
44
|
+
# :location,
|
45
|
+
# { book: { wildcard: :both } }
|
46
|
+
# ]
|
47
|
+
# c.include_options = [
|
48
|
+
# {'publisher': -> { includes(:publisher) }},
|
49
|
+
# {'comments': -> { includes(:comments) }},
|
50
|
+
# 'comment.author'
|
51
|
+
# ]
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# # A collection.
|
55
|
+
# def index
|
56
|
+
# builder = JsonApiServer::Builder.new(request, Topic.current)
|
57
|
+
# .add_pagination(pagination_options)
|
58
|
+
# .add_filter(filter_options)
|
59
|
+
# .add_include(include_options)
|
60
|
+
# .add_sort(sort_options)
|
61
|
+
# .add_fields
|
62
|
+
#
|
63
|
+
# serializer = TopicsSerializer.from_builder(builder)
|
64
|
+
# render json: serializer.to_json, status: :ok
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# # A resource.
|
68
|
+
# def show
|
69
|
+
# builder = JsonApiServer::Builder.new(request, Topic.find(params[:id]))
|
70
|
+
# .add_include(['publisher', 'comments', 'comments.includes'])
|
71
|
+
# .add_fields
|
72
|
+
#
|
73
|
+
# serializer = TopicSerializer.from_builder(builder)
|
74
|
+
# render json: serializer.to_json, status: :ok
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
class Builder
|
78
|
+
# ActionDispatch::Request passed in constructor.
|
79
|
+
attr_reader :request
|
80
|
+
|
81
|
+
# ActiveRecord::Base model extracted from initial query passed in constructor.
|
82
|
+
attr_reader :model
|
83
|
+
|
84
|
+
# JsonApiServer::Fields instance if #add_fields was called. nil otherwise.
|
85
|
+
attr_reader :fields
|
86
|
+
|
87
|
+
# JsonApiServer::Pagination instance if #add_pagination was called. nil otherwise.
|
88
|
+
attr_reader :pagination
|
89
|
+
|
90
|
+
# JsonApiServer::Filter instance if #add_filter was called. nil otherwise.
|
91
|
+
attr_reader :filter
|
92
|
+
|
93
|
+
# JsonApiServer::Include instance if #add_include was called. nil otherwise.
|
94
|
+
attr_reader :include
|
95
|
+
|
96
|
+
# JsonApiServer::Sort instance if #add_sort was called. nil otherwise.
|
97
|
+
attr_reader :sort
|
98
|
+
|
99
|
+
# Arguments:
|
100
|
+
# - request - an ActionDispatch::Request
|
101
|
+
# - query (ActiveRecord::Relation) - Initial query.
|
102
|
+
def initialize(request, query)
|
103
|
+
@request = request
|
104
|
+
@initial_query = query
|
105
|
+
@model = model_from_query(@initial_query)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Merges pagination, filter, sort, and include sub-queries (if defined)
|
109
|
+
# into the initial query. Returns an ActiveRecord::Relation object.
|
110
|
+
def relation
|
111
|
+
@relation ||= begin
|
112
|
+
return @initial_query unless @initial_query.respond_to?(:where)
|
113
|
+
|
114
|
+
%i[pagination filter include sort].each_with_object(@initial_query) do |method, query|
|
115
|
+
frag = send(method).try(:relation)
|
116
|
+
query.merge!(frag) if frag
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
alias query relation
|
122
|
+
|
123
|
+
# Creates JsonApiServer::Pagination instance based on request, initial query
|
124
|
+
# model and pagination options. Instance is available through
|
125
|
+
# the #pagination attribute. For collections only.
|
126
|
+
#
|
127
|
+
# - <tt>options</tt> - JsonApiServer::Pagination options.
|
128
|
+
def add_pagination(**options)
|
129
|
+
@pagination = JsonApiServer::Pagination.new(request, model, options)
|
130
|
+
self
|
131
|
+
end
|
132
|
+
|
133
|
+
# Creates JsonApiServer::Filter instance based on request, initial query
|
134
|
+
# model and filter configs. Instance is available through
|
135
|
+
# the #filter attribute.
|
136
|
+
#
|
137
|
+
# - <tt>permitted</tt> - JsonApiServer::Filter configs.
|
138
|
+
def add_filter(permitted = [])
|
139
|
+
@filter = JsonApiServer::Filter.new(request, model, permitted)
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
# Creates JsonApiServer::Include instance based on request, initial query
|
144
|
+
# model and include configs. Instance is available through
|
145
|
+
# the #include attribute.
|
146
|
+
#
|
147
|
+
# - <tt>permitted</tt> - JsonApiServer::Include configs.
|
148
|
+
def add_include(permitted = [])
|
149
|
+
@include = JsonApiServer::Include.new(request, model, permitted)
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
# Creates JsonApiServer::Sort instance based on request, initial query
|
154
|
+
# model and sort options. Instance is available through
|
155
|
+
# the #sort attribute.
|
156
|
+
#
|
157
|
+
# - <tt>options</tt> - JsonApiServer::Sort options.
|
158
|
+
def add_sort(**options)
|
159
|
+
@sort = JsonApiServer::Sort.new(request, model, options)
|
160
|
+
self
|
161
|
+
end
|
162
|
+
|
163
|
+
# Creates JsonApiServer::Fields instance based on request. Instance is
|
164
|
+
# available through the #fields attribute.
|
165
|
+
def add_fields
|
166
|
+
@fields = JsonApiServer::Fields.new(request)
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
# JsonApiServer::Paginator instance for collection if #add_pagination
|
171
|
+
# was called previously, nil otherwise.
|
172
|
+
def paginator
|
173
|
+
@pagination.try(:paginator_for, relation)
|
174
|
+
end
|
175
|
+
|
176
|
+
# (Array or nil) Whitelisted includes. An array of relationships (strings)
|
177
|
+
# if #add_include was called previously, nil otherwise.
|
178
|
+
# i.e,
|
179
|
+
# ['comments', 'comments.author']
|
180
|
+
def includes
|
181
|
+
@include.try(:includes)
|
182
|
+
end
|
183
|
+
|
184
|
+
# (Hash or nil) Sparse fields. Available if #add_fields was previously called.
|
185
|
+
# i.e.,
|
186
|
+
# {'articles => ['title', 'body', 'author'], 'people' => ['name']}
|
187
|
+
def sparse_fields
|
188
|
+
@fields.try(:sparse_fields)
|
189
|
+
end
|
190
|
+
|
191
|
+
protected
|
192
|
+
|
193
|
+
def model_from_query(query)
|
194
|
+
if query.respond_to?(:klass)
|
195
|
+
query.klass
|
196
|
+
elsif query.respond_to?(:class)
|
197
|
+
query.class
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'bigdecimal/util'
|
3
|
+
require 'date_core' # DateTime
|
4
|
+
|
5
|
+
#--
|
6
|
+
# TODO: verify it works in Rails, esp in_time_zone.
|
7
|
+
#++
|
8
|
+
module JsonApiServer # :nodoc:
|
9
|
+
# Converts string params to data types.
|
10
|
+
class Cast
|
11
|
+
class << self
|
12
|
+
def to(value, type = 'String')
|
13
|
+
case type.to_s
|
14
|
+
when 'String'
|
15
|
+
apply_cast(value, :to_string)
|
16
|
+
when 'Integer'
|
17
|
+
apply_cast(value, :to_integer)
|
18
|
+
when 'Date'
|
19
|
+
apply_cast(value, :to_date)
|
20
|
+
when 'DateTime'
|
21
|
+
apply_cast(value, :to_datetime)
|
22
|
+
when 'Float'
|
23
|
+
apply_cast(value, :to_float)
|
24
|
+
when 'BigDecimal'
|
25
|
+
apply_cast(value, :to_decimal)
|
26
|
+
else
|
27
|
+
apply_cast(value, :to_string)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Calls to_s on object.
|
32
|
+
def to_string(string)
|
33
|
+
string.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
# Calls to_i on object. Returns zero if it can't be converted.
|
37
|
+
def to_integer(string)
|
38
|
+
string.to_i
|
39
|
+
rescue
|
40
|
+
0
|
41
|
+
end
|
42
|
+
|
43
|
+
# Calls to_f on object. Returns zero if it can't be converted.
|
44
|
+
def to_float(string)
|
45
|
+
string.to_f
|
46
|
+
rescue
|
47
|
+
0.0
|
48
|
+
end
|
49
|
+
|
50
|
+
# Converts to BigDecimal and calls to_f on it. Returns
|
51
|
+
# zero if it can't be converted.
|
52
|
+
def to_decimal(string)
|
53
|
+
d = BigDecimal.new(string)
|
54
|
+
d.to_f
|
55
|
+
rescue
|
56
|
+
0.0
|
57
|
+
end
|
58
|
+
|
59
|
+
# Calls Date.parse on string.
|
60
|
+
# https://ruby-doc.org/stdlib-2.4.0/libdoc/date/rdoc/Date.html#method-c-parse
|
61
|
+
def to_date(string)
|
62
|
+
Date.parse(string)
|
63
|
+
rescue
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
# Calls DateTime.parse on string. If datetime responds to
|
68
|
+
# :in_time_zone[http://apidock.com/rails/v4.2.1/ActiveSupport/TimeWithZone/in_time_zone)],
|
69
|
+
# it calls it.
|
70
|
+
def to_datetime(string)
|
71
|
+
d = DateTime.parse(string)
|
72
|
+
d.respond_to?(:in_time_zone) ? d.in_time_zone : d
|
73
|
+
rescue
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
# If val is an array, it casts each value. Otherwise, it casts the value.
|
80
|
+
def apply_cast(val, cast_method)
|
81
|
+
if val.respond_to?(:map)
|
82
|
+
val.map { |v| send(cast_method, v) }
|
83
|
+
else
|
84
|
+
send(cast_method, val)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module JsonApiServer # :nodoc:
|
2
|
+
# ==== Description
|
3
|
+
#
|
4
|
+
# Configurations for the gem.
|
5
|
+
#
|
6
|
+
# - Be sure to configure :base_url which defaults to nil.
|
7
|
+
# - Logger defaults to Logger.new(STDOUT). If using Rails, configure to Rails.logger.
|
8
|
+
# - Custom builders can be added to :filter_builders.
|
9
|
+
# - Default builders can be substituted.
|
10
|
+
#
|
11
|
+
# ===== Example
|
12
|
+
#
|
13
|
+
# # config/initializers/json_api_server.rb
|
14
|
+
#
|
15
|
+
# # example of custom filter builder
|
16
|
+
# module JsonApiServer
|
17
|
+
# class MyCustomFilter < FilterBuilder
|
18
|
+
# def to_query(model)
|
19
|
+
# model.where("#{column_name} LIKE :val", val: "%#{value}%")
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# JsonApiServer.configure do |c|
|
25
|
+
# c.base_url = 'http://localhost:3001' # or ENV['HOSTNAME']
|
26
|
+
# c.filter_builders = c.filter_builders
|
27
|
+
# .merge({my_custom_builder: JsonApiServer::MyCustomFilter})
|
28
|
+
# c.logger = Rails.logger
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
class Configuration
|
32
|
+
# Root url i.e., http://www.example.com. Used in pagination links.
|
33
|
+
attr_accessor :base_url
|
34
|
+
|
35
|
+
# Pagination option. Default maximum number of records to show per page.
|
36
|
+
# Defaults to 100.
|
37
|
+
attr_accessor :default_max_per_page
|
38
|
+
|
39
|
+
# Pagination option. Default number of records to show per page.
|
40
|
+
# Defaults to 20.
|
41
|
+
attr_accessor :default_per_page
|
42
|
+
|
43
|
+
# JSON is serialized with OJ gem. Options are defined in DEFAULT_SERIALIZER_OPTIONS.
|
44
|
+
attr_accessor :serializer_options
|
45
|
+
|
46
|
+
# Defaults to sql_like: JsonApiServer::SqlLike. If using Postgres,
|
47
|
+
# it can be replaced with pg_ilike: JsonApiServer::PgIlike.
|
48
|
+
attr_accessor :default_like_builder
|
49
|
+
|
50
|
+
# Defaults to sql_in: JsonApiServer::SqlIn. For IN (x,y,z) queries.
|
51
|
+
attr_accessor :default_in_builder
|
52
|
+
|
53
|
+
# Defaults to sql_comparison: JsonApiServer::SqlComp.
|
54
|
+
# For <,>, <=, >=, etc. queries.
|
55
|
+
attr_accessor :default_comparison_builder
|
56
|
+
|
57
|
+
# Defaults to sql_eql: JsonApiServer::SqlEql.
|
58
|
+
attr_accessor :default_builder
|
59
|
+
|
60
|
+
# Defaults to DEFAULT_FILTER_BUILDERS.
|
61
|
+
attr_accessor :filter_builders
|
62
|
+
|
63
|
+
# Defaults to Logger.new(STDOUT)
|
64
|
+
attr_accessor :logger
|
65
|
+
|
66
|
+
# Serializer options for the OJ gem.
|
67
|
+
DEFAULT_SERIALIZER_OPTIONS = {
|
68
|
+
escape_mode: :xss_safe,
|
69
|
+
time: :xmlschema,
|
70
|
+
mode: :compat
|
71
|
+
}.freeze
|
72
|
+
|
73
|
+
# Default filter builders. For generating queries based on
|
74
|
+
# on requested filters.
|
75
|
+
DEFAULT_FILTER_BUILDERS = {
|
76
|
+
sql_eql: JsonApiServer::SqlEql,
|
77
|
+
sql_comparison: JsonApiServer::SqlComp,
|
78
|
+
sql_in: JsonApiServer::SqlIn,
|
79
|
+
sql_like: JsonApiServer::SqlLike,
|
80
|
+
pg_ilike: JsonApiServer::PgIlike,
|
81
|
+
pg_jsonb_array: JsonApiServer::PgJsonbArray,
|
82
|
+
pg_jsonb_ilike_array: JsonApiServer::PgJsonbIlikeArray,
|
83
|
+
model_query: JsonApiServer::ModelQuery
|
84
|
+
}.freeze
|
85
|
+
|
86
|
+
def initialize
|
87
|
+
@base_url = nil
|
88
|
+
@default_max_per_page = 100
|
89
|
+
@default_per_page = 20
|
90
|
+
@default_like_builder = :sql_like
|
91
|
+
@default_in_builder = :sql_in
|
92
|
+
@default_comparison_builder = :sql_comparison
|
93
|
+
@default_builder = :sql_eql
|
94
|
+
@serializer_options = DEFAULT_SERIALIZER_OPTIONS
|
95
|
+
@filter_builders = DEFAULT_FILTER_BUILDERS
|
96
|
+
@logger = Logger.new(STDOUT)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module JsonApiServer # :nodoc:
|
2
|
+
module Controller # :nodoc:
|
3
|
+
# Handles common controller errors. Returns JsonApi errors http://jsonapi.org/format/#errors.
|
4
|
+
#
|
5
|
+
# === Usage
|
6
|
+
#
|
7
|
+
# To use, include the JsonApiServer::Controller::ErrorHandling module in your
|
8
|
+
# base API controller class:
|
9
|
+
#
|
10
|
+
# i.e.,
|
11
|
+
#
|
12
|
+
# class Api::BaseController < ApplicationController
|
13
|
+
# include JsonApiServer::Controller::ErrorHandling
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# === I18n
|
17
|
+
#
|
18
|
+
# Messages are defined in config/locales/en.yml.
|
19
|
+
#
|
20
|
+
# Customize ActiveRecord::RecordNotFound and ActiveRecord::RecordNotUnique
|
21
|
+
# messages to reference a resource by name.
|
22
|
+
#
|
23
|
+
# *Example:*
|
24
|
+
#
|
25
|
+
# # Given a sandwiches controller...
|
26
|
+
# class Api::V1::SandwichesController < Api::BaseController
|
27
|
+
# ...
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# # config/locales/en.yml
|
31
|
+
# en:
|
32
|
+
# json_api_server:
|
33
|
+
# controller:
|
34
|
+
# sandwiches:
|
35
|
+
# name: 'sandwich'
|
36
|
+
#
|
37
|
+
# # messages now become:
|
38
|
+
# # 404 => "This sandwich does not exist."
|
39
|
+
# # 409 => "This sandwich already exists."
|
40
|
+
module ErrorHandling
|
41
|
+
def self.included(base)
|
42
|
+
# Overrides of exception handling.
|
43
|
+
base.rescue_from StandardError, with: :render_500
|
44
|
+
base.rescue_from JsonApiServer::BadRequest, with: :render_400
|
45
|
+
base.rescue_from ActionController::BadRequest, with: :render_400
|
46
|
+
base.rescue_from ActiveRecord::RecordNotFound, with: :render_404
|
47
|
+
base.rescue_from ActiveRecord::RecordNotUnique, with: :render_409
|
48
|
+
base.rescue_from ActionController::RoutingError, with: :render_404
|
49
|
+
base.rescue_from ActionController::UrlGenerationError, with: :render_404
|
50
|
+
base.rescue_from ActionController::UnknownController, with: :render_404
|
51
|
+
base.rescue_from ActionController::UnknownFormat, with: :render_unknown_format
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
# Render 400 json and status.
|
57
|
+
def render_400(exception = nil)
|
58
|
+
message = (exception && known?(exception) && exception.message) || I18n.t('json_api_server.render_400.detail')
|
59
|
+
errors = JsonApiServer.errors(
|
60
|
+
status: 400,
|
61
|
+
title: I18n.t('json_api_server.render_400.title'),
|
62
|
+
detail: message
|
63
|
+
)
|
64
|
+
render json: errors.to_json, status: 400
|
65
|
+
end
|
66
|
+
|
67
|
+
# Render 401 json and status.
|
68
|
+
def render_401
|
69
|
+
errors = JsonApiServer.errors(
|
70
|
+
status: 401,
|
71
|
+
title: I18n.t('json_api_server.render_401.title'),
|
72
|
+
detail: I18n.t('json_api_server.render_401.detail')
|
73
|
+
)
|
74
|
+
render json: errors.to_json, status: 401
|
75
|
+
end
|
76
|
+
|
77
|
+
# Render 403 json and status.
|
78
|
+
def render_403
|
79
|
+
errors = JsonApiServer.errors(
|
80
|
+
status: 403,
|
81
|
+
title: I18n.t('json_api_server.render_403.title'),
|
82
|
+
detail: I18n.t('json_api_server.render_403.detail')
|
83
|
+
)
|
84
|
+
render json: errors.to_json, status: 403
|
85
|
+
end
|
86
|
+
|
87
|
+
# Render 404 json and status. Message customizable (see class description).
|
88
|
+
def render_404(_exception = nil)
|
89
|
+
errors = JsonApiServer.errors(
|
90
|
+
status: 404,
|
91
|
+
title: I18n.t('json_api_server.render_404.title'),
|
92
|
+
detail: I18n.t('json_api_server.render_404.detail', name: _i18n_name)
|
93
|
+
)
|
94
|
+
render json: errors.to_json, status: 404
|
95
|
+
end
|
96
|
+
|
97
|
+
# Render 409 json and status. Message customizable (see class description).
|
98
|
+
def render_409(_exception = nil)
|
99
|
+
errors = JsonApiServer.errors(
|
100
|
+
status: 409,
|
101
|
+
title: I18n.t('json_api_server.render_409.title'),
|
102
|
+
detail: I18n.t('json_api_server.render_409.detail', name: _i18n_name)
|
103
|
+
)
|
104
|
+
render json: errors.to_json, status: 409
|
105
|
+
end
|
106
|
+
|
107
|
+
# Render 422 json and status. For model validation error.
|
108
|
+
def render_422(object)
|
109
|
+
errors = JsonApiServer.validation_errors(object)
|
110
|
+
render json: errors.to_json, status: 422
|
111
|
+
end
|
112
|
+
|
113
|
+
# Render 500 json. Logs exception.
|
114
|
+
def render_500(exception = nil)
|
115
|
+
JsonApiServer.logger.error(exception.try(:message))
|
116
|
+
JsonApiServer.logger.error(exception.try(:backtrace))
|
117
|
+
|
118
|
+
errors = JsonApiServer.errors(
|
119
|
+
status: 500,
|
120
|
+
title: I18n.t('json_api_server.render_500.title'),
|
121
|
+
detail: I18n.t('json_api_server.render_500.detail')
|
122
|
+
)
|
123
|
+
render json: errors.to_json, status: 500
|
124
|
+
end
|
125
|
+
|
126
|
+
# Render 406 status code and message that the format is not supported.
|
127
|
+
def render_unknown_format
|
128
|
+
format = sanitize(params[:format]) || ''
|
129
|
+
errors = JsonApiServer.errors(
|
130
|
+
status: 406,
|
131
|
+
title: I18n.t('json_api_server.render_unknown_format.title'),
|
132
|
+
detail: I18n.t('json_api_server.render_unknown_format.detail', name: format)
|
133
|
+
)
|
134
|
+
render json: errors.to_json, status: 406
|
135
|
+
end
|
136
|
+
|
137
|
+
# Render 503 json and status (service unavailable).
|
138
|
+
def render_503(message = nil)
|
139
|
+
errors = JsonApiServer.errors(
|
140
|
+
status: 500,
|
141
|
+
title: I18n.t('json_api_server.render_503.title'),
|
142
|
+
detail: message || I18n.t('json_api_server.render_503.detail')
|
143
|
+
)
|
144
|
+
render json: errors.to_json, status: 503
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def known?(exception)
|
150
|
+
!(exception.class.name =~ /JsonApiServer::BadRequest/).nil?
|
151
|
+
end
|
152
|
+
|
153
|
+
def sanitize(string)
|
154
|
+
ActionController::Base.helpers.sanitize(string.to_s)
|
155
|
+
end
|
156
|
+
|
157
|
+
def _i18n_name
|
158
|
+
I18n.t("json_api_server.controller.#{controller_name}.name", raise: true)
|
159
|
+
rescue
|
160
|
+
I18n.t('json_api_server.variables.defaults.name')
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|