json_api_server 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|