json_apiable 0.1.0 → 0.2.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 +4 -4
- data/.gitignore +2 -0
- data/Gemfile.lock +2 -2
- data/README.md +134 -3
- data/json_apiable.gemspec +1 -1
- data/lib/json_apiable/configuration.rb +1 -1
- data/lib/json_apiable/json_apiable.rb +14 -10
- data/lib/json_apiable/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02ceaeecfcdb306933da14c8f4596e355958a6bd6902dc37a365295e409d085e
|
4
|
+
data.tar.gz: b5dc5338fcb3cd7fd1991f4dc18871ffdc3ed4d63c99d0e826469b558dba8dc0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c5ddc873cee6e321ab133f8422d22dfe3cc2c46ae57af5c2a1bbfc9b759e00c50b333e11af758ef80f89c8f1b1a5edcbbbd8edb2dd79c13f4659ae20e8a5d4a
|
7
|
+
data.tar.gz: 8e9cbdf0d269a0682b2f863b50fdcc71ed00a991174be19d293d75463578cada37390254d45d7a389b6447ece782d6c922b9bef79bc8d9518ede3bbbbf24188f
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
json_apiable (0.
|
4
|
+
json_apiable (0.2.0)
|
5
5
|
activerecord (>= 4.2)
|
6
6
|
activesupport (>= 4.2)
|
7
7
|
fast_jsonapi (~> 1.5)
|
@@ -186,7 +186,7 @@ DEPENDENCIES
|
|
186
186
|
rails
|
187
187
|
rails-controller-testing
|
188
188
|
rake (~> 10.0)
|
189
|
-
rspec-rails
|
189
|
+
rspec-rails (~> 3.9)
|
190
190
|
sqlite3
|
191
191
|
|
192
192
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# JsonApiable
|
2
2
|
|
3
|
-
|
3
|
+
JsonApiable is a Ruby module that makes it easier for Rails API controllers to handle JSON:API parameter and relationship parsing,
|
4
|
+
strong parameter validation, returning well-structured errors and more - all in a Rails-friendly way.
|
4
5
|
|
5
|
-
|
6
|
+
JsonApiable doesn't assume anything about other JSON:API gems you may be using.
|
7
|
+
Feel free to use it in conjunction with fast_jsonapi, active_model_serializer, jsonapi-resources or any other library.
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
@@ -21,8 +23,137 @@ Or install it yourself as:
|
|
21
23
|
$ gem install json_apiable
|
22
24
|
|
23
25
|
## Usage
|
26
|
+
### Basics
|
27
|
+
```ruby
|
28
|
+
# include JsonApiable in your Base API controller
|
29
|
+
class API::BaseController < ActionController::Base
|
30
|
+
# By including JsonApiable, you get the following before/after actions in your controllers:
|
31
|
+
#
|
32
|
+
# before_action :ensure_jsonapi_content_type - Ensure correct Content-Type (application/vnd.api+json) is set in
|
33
|
+
# the request, and return error othewrise
|
34
|
+
# before_action :ensure_jsonapi_valid_query_params - Ensure only valid query parameters are used
|
35
|
+
# before_action :parse_jsonapi_pagination - Parse "?page:{number:1, size:25}" query hash, set defaults and
|
36
|
+
# return errors when invalid values received
|
37
|
+
# Read more: https://jsonapi.org/format/#fetching-pagination
|
38
|
+
# before_action :parse_jsonapi_include - Parse "?include=posts.author" include directives .
|
39
|
+
# Read more: https://jsonapi.org/format/#fetching-includes
|
40
|
+
# after_action :set_jsonapi_content_type - Ensure correct Content-Type (application/vnd.api+json) is set
|
41
|
+
# in the response
|
42
|
+
|
43
|
+
# By including JsonApiable, you get the following exceptions handled automatically:
|
44
|
+
# rescue_from ArgumentError
|
45
|
+
# rescue_from ActionController::UnpermittedParameters
|
46
|
+
# rescue_from MalformedRequestError
|
47
|
+
# rescue_from UnprocessableEntityError
|
48
|
+
# rescue_from ActiveRecord::RecordNotFound
|
49
|
+
include JsonApiable
|
50
|
+
end
|
51
|
+
|
52
|
+
class API::PostsController < API::BaseController
|
53
|
+
|
54
|
+
# GET /v1/posts
|
55
|
+
def index
|
56
|
+
# pass page and include info to your logic
|
57
|
+
posts = GetPostsService.call(jsonapi_page, jsonapi_include)
|
58
|
+
# some other gem, such as fast_jsonapi is assumed to produce the json:api output
|
59
|
+
render json: posts
|
60
|
+
end
|
61
|
+
|
62
|
+
# PATCH /v1/posts/123/update
|
63
|
+
# { "data":
|
64
|
+
# { "type": "post",
|
65
|
+
# "attributes": {
|
66
|
+
# "title": "My New Title"
|
67
|
+
# },
|
68
|
+
# "relationships": {
|
69
|
+
# "author": {
|
70
|
+
# "data": {
|
71
|
+
# "type": "user",
|
72
|
+
# "id": "4528"
|
73
|
+
# },
|
74
|
+
# "comments": {
|
75
|
+
# "data": [
|
76
|
+
# { "type": "comment", "id": "1489" },
|
77
|
+
# { "type": "comment", "id": "1490" }
|
78
|
+
# ]
|
79
|
+
# }
|
80
|
+
# }
|
81
|
+
# }
|
82
|
+
# }
|
83
|
+
# }
|
84
|
+
def update
|
85
|
+
@post = Post.find(params[:id])
|
86
|
+
|
87
|
+
# turn relationships into Rails associations and assign them together with attributes
|
88
|
+
# as you would normally do in Rails
|
89
|
+
#
|
90
|
+
# jsonapi_assign_params =>
|
91
|
+
# { "title"=>"My New Title",
|
92
|
+
# "author_id" => 4528,
|
93
|
+
# "comments_attributes"=>{
|
94
|
+
# "0"=>{"id"=>"1489", "_destroy"=>"false"},
|
95
|
+
# "1"=>{"id"=>"1490", "_destroy"=>"false"}},
|
96
|
+
# "comment_ids"=>["1489", "1490"],
|
97
|
+
#
|
98
|
+
# }
|
99
|
+
@post.update_attributes!(jsonapi_assign_params)
|
100
|
+
render json: @user
|
101
|
+
end
|
102
|
+
|
103
|
+
def create
|
104
|
+
# use jsonapi_attribute_present? to quickly test presence of specific attributes
|
105
|
+
raise UnprocessableEntityError, 'No title!' unless jsonapi_attribute_present?(:title)
|
106
|
+
# use jsonapi_attribute_value to get attribute values. If non-existent, nil would be returned
|
107
|
+
@title = jsonapi_attribute_value(:title)
|
108
|
+
# exclude 'author' attribute from assign params, for example because it's a separate table on the DB level)
|
109
|
+
@author_name = jsonapi_exclude_attribute(:author_name)
|
110
|
+
# exclude 'comments' relationship from assign params, for example because we want to filter which ones are added to post
|
111
|
+
@comments_hash = jsonapi_exclude_relationship(:comments)
|
112
|
+
do_some_logc_with_excluded_params
|
113
|
+
# jsonapi_assign_params wouldn't include 'author' attribute and 'comments' relationship
|
114
|
+
User.create!(jsonapi_assign_params)
|
115
|
+
end
|
116
|
+
|
117
|
+
protected
|
118
|
+
|
119
|
+
# declare which attributes should be allowed to be assigned. Complex attributes are allowed
|
120
|
+
def jsonapi_allowed_attributes
|
121
|
+
[:title,
|
122
|
+
:body,
|
123
|
+
dates: %i[first_drafted published last_edited]]
|
124
|
+
end
|
125
|
+
|
126
|
+
# declare which relationships should be allowed to be assigned
|
127
|
+
def jsonapi_allowed_relationships
|
128
|
+
%i[comments contributors]
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
end
|
134
|
+
````
|
135
|
+
### Configuration
|
136
|
+
Add an initializer to your app with the following config block:
|
137
|
+
```ruby
|
138
|
+
JsonApiable.configure do |config|
|
139
|
+
# white-list query params that should be allowed
|
140
|
+
config.valid_query_params = %w[ id access_token user_id organization_id ]
|
141
|
+
|
142
|
+
# by default, error is returned if the request Content-Type isn't valid JSON-API. Override the behaviour by using this block:
|
143
|
+
config.supported_media_type_proc = proc do |request|
|
144
|
+
request.content_type == JsonApiable::JSONAPI_CONTENT_TYPE || request.headers['My-Special-Header'].present?
|
145
|
+
end
|
146
|
+
|
147
|
+
# by default, ActiveRecord::RecordNotFound is caught by JsonApiable and turned into an error response. If your backend raises a different class of exception, set it here
|
148
|
+
config.not_found_exception_class = MyExceptionClass
|
149
|
+
|
150
|
+
end
|
151
|
+
```
|
24
152
|
|
25
|
-
|
153
|
+
### Limitations
|
154
|
+
- `has_one` associations are currently not supported by `jsonapi_assign_params`. So if the updated resource
|
155
|
+
contains an association whose foreign key exists in
|
156
|
+
- complex attributes currently don't work
|
26
157
|
|
27
158
|
## Development
|
28
159
|
|
data/json_apiable.gemspec
CHANGED
@@ -36,6 +36,6 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_development_dependency 'rails'
|
37
37
|
spec.add_development_dependency 'rails-controller-testing'
|
38
38
|
spec.add_development_dependency 'rake', '~> 10.0'
|
39
|
-
spec.add_development_dependency 'rspec-rails'
|
39
|
+
spec.add_development_dependency 'rspec-rails', '~> 3.9'
|
40
40
|
spec.add_development_dependency 'sqlite3'
|
41
41
|
end
|
@@ -8,7 +8,7 @@ module JsonApiable
|
|
8
8
|
MAX_PAGE_SIZE = 214_748_364
|
9
9
|
|
10
10
|
def initialize
|
11
|
-
@valid_query_params = %w[id access_token filter include page]
|
11
|
+
@valid_query_params = %w[id user_id access_token filter include page]
|
12
12
|
@supported_media_type_proc = nil
|
13
13
|
@not_found_exception_class = ActiveRecord::RecordNotFound
|
14
14
|
@page_size = DEFAULT_PAGE_SIZE
|
@@ -9,12 +9,12 @@ module JsonApiable
|
|
9
9
|
:jsonapi_exclude_attributes, :jsonapi_exclude_relationships
|
10
10
|
|
11
11
|
included do
|
12
|
-
before_action :
|
13
|
-
before_action :
|
14
|
-
before_action :
|
15
|
-
before_action :
|
12
|
+
before_action :ensure_jsonapi_content_type
|
13
|
+
before_action :ensure_jsonapi_valid_query_params
|
14
|
+
before_action :parse_jsonapi_pagination
|
15
|
+
before_action :parse_jsonapi_include
|
16
16
|
|
17
|
-
after_action :
|
17
|
+
after_action :set_jsonapi_content_type
|
18
18
|
|
19
19
|
rescue_from ArgumentError, with: :respond_to_bad_argument
|
20
20
|
rescue_from ActionController::UnpermittedParameters, with: :respond_to_bad_argument
|
@@ -46,6 +46,10 @@ module JsonApiable
|
|
46
46
|
jsonapi_build_params.dig(:data, :attributes, attrib_key).present?
|
47
47
|
end
|
48
48
|
|
49
|
+
def jsonapi_attribute_value(attrib_key)
|
50
|
+
jsonapi_build_params.dig(:data, :attributes, attrib_key)
|
51
|
+
end
|
52
|
+
|
49
53
|
def jsonapi_assign_params
|
50
54
|
@jsonapi_assign_params ||= ParamsParser.parse_body_params(request,
|
51
55
|
jsonapi_build_params,
|
@@ -87,11 +91,11 @@ module JsonApiable
|
|
87
91
|
%i[]
|
88
92
|
end
|
89
93
|
|
90
|
-
def
|
94
|
+
def ensure_jsonapi_content_type
|
91
95
|
respond_to_unsupported_media_type unless supported_media_type?
|
92
96
|
end
|
93
97
|
|
94
|
-
def
|
98
|
+
def ensure_jsonapi_valid_query_params
|
95
99
|
invalid_params = request.query_parameters.keys.reject { |k| JsonApiable.configuration.valid_query_params.include?(k) }
|
96
100
|
respond_to_bad_argument(invalid_params.first) if invalid_params.present?
|
97
101
|
end
|
@@ -104,15 +108,15 @@ module JsonApiable
|
|
104
108
|
end
|
105
109
|
end
|
106
110
|
|
107
|
-
def
|
111
|
+
def set_jsonapi_content_type
|
108
112
|
response.headers['Content-Type'] = JSONAPI_CONTENT_TYPE
|
109
113
|
end
|
110
114
|
|
111
|
-
def
|
115
|
+
def parse_jsonapi_pagination
|
112
116
|
@jsonapi_page = PaginationParser.parse_pagination!(query_params, jsonapi_default_page_size)
|
113
117
|
end
|
114
118
|
|
115
|
-
def
|
119
|
+
def parse_jsonapi_include
|
116
120
|
@jsonapi_include = query_params[:include].presence&.gsub(/ /, '')&.split(',')&.map(&:to_sym).to_a
|
117
121
|
end
|
118
122
|
|
data/lib/json_apiable/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: json_apiable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Polischuk
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -168,16 +168,16 @@ dependencies:
|
|
168
168
|
name: rspec-rails
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
|
-
- - "
|
171
|
+
- - "~>"
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version: '
|
173
|
+
version: '3.9'
|
174
174
|
type: :development
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
|
-
- - "
|
178
|
+
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version: '
|
180
|
+
version: '3.9'
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: sqlite3
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|