introspective_grape 0.2.5 → 0.2.6
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/CHANGELOG.md +12 -0
- data/README.md +20 -1
- data/lib/introspective_grape/api.rb +16 -4
- data/lib/introspective_grape/validators.rb +34 -0
- data/lib/introspective_grape/version.rb +1 -1
- data/spec/dummy/app/api/dummy/company_api.rb +1 -1
- data/spec/dummy/app/models/company.rb +21 -0
- data/spec/dummy/app/models/role.rb +6 -1
- data/spec/dummy/app/models/user.rb +1 -1
- data/spec/requests/company_api_spec.rb +15 -0
- data/spec/requests/role_api_spec.rb +5 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a21e9540c7f4929e4dced1d88eb599edb4e887b
|
4
|
+
data.tar.gz: e97148d4dd00bba79807d6b52a79a29073fbbc9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4236508d67aac2b65c1ed4123c4bfd63ab76851c1a4cbc17d943326bc01530ac9cc57f336954cfde83713fbb4020751a1e201dbe58a585e919aa3a7c23a7f7aa
|
7
|
+
data.tar.gz: d80ccf25f1db8187717fb57f6e8a8f82f2fb8889154efc0b96121bdf5a022f193de94380fc222ec8aeecc19d1041f99edbbd10234a2ba8ca50e1c4c15ee03ee1
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
|
2
|
+
0.2.6 11/01/2016
|
3
|
+
==============
|
4
|
+
|
5
|
+
Change the Model.attribute_param_types class method lookup (for specifying custom param types attributes on a model) to "grape_param_types" to make it clearer that the method is used in the API.
|
6
|
+
|
7
|
+
### Features
|
8
|
+
|
9
|
+
Add support for custom validations on a model using a `self.grape_validations` hash of fields and validations to apply.
|
10
|
+
|
11
|
+
Add custom validators to verify that JSON strings parse as a JSON object, an array, or a hash.
|
12
|
+
|
13
|
+
|
2
14
|
0.2.5 10/28/2016
|
3
15
|
==============
|
4
16
|
|
data/README.md
CHANGED
@@ -110,13 +110,32 @@ type from model introspection, define a class method in the model with the param
|
|
110
110
|
types for the attributes specified in a hash, e.g.:
|
111
111
|
|
112
112
|
```
|
113
|
-
def self.
|
113
|
+
def self.grape_param_types
|
114
114
|
{ "<attribute name 1>" => String,
|
115
115
|
"<attribute name 2>" => Integer,
|
116
116
|
"<attribute name 3>" => Virtus::Attribute::Boolean }
|
117
117
|
end
|
118
118
|
```
|
119
119
|
|
120
|
+
To add additional validations on API inputs you can define a hash of hashes in the model in a
|
121
|
+
class method ("grape_validations") that will be applied to that field's param declaration:
|
122
|
+
|
123
|
+
```
|
124
|
+
def self.grape_validations
|
125
|
+
{ field1: { values: %w(red blue green) },
|
126
|
+
field2: { json_array: true },
|
127
|
+
field3: { regexp: /\w+/ }
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
IntrospectiveGrape provides the following custom grape validators:
|
132
|
+
|
133
|
+
```
|
134
|
+
json: true # validates that the JSON string parses
|
135
|
+
json_array: true # validates that the JSON string parses and returns an Array
|
136
|
+
json_hash: true # validates that the JSON string parses and returns a Hash
|
137
|
+
```
|
138
|
+
|
120
139
|
For nested models declared in Rails' strong params both the Grape params for the
|
121
140
|
nested params as well as nested routes will be declared, allowing for
|
122
141
|
a good deal of flexibility for API consumers out of the box, such as implicitly
|
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'action_controller'
|
2
2
|
require 'grape-kaminari'
|
3
|
+
require 'introspective_grape/validators'
|
4
|
+
|
5
|
+
class IntrospectiveGrapeError < StandardError
|
6
|
+
end
|
3
7
|
|
4
8
|
module IntrospectiveGrape
|
5
9
|
class API < Grape::API
|
@@ -28,7 +32,7 @@ module IntrospectiveGrape
|
|
28
32
|
# type from model introspection, define a class method in the model with the param
|
29
33
|
# types for the attributes specified in a hash:
|
30
34
|
#
|
31
|
-
# def self.
|
35
|
+
# def self.grape_param_types
|
32
36
|
# { "<attribute name>" => Virtus::Attribute::Boolean,
|
33
37
|
# "<attribute name>" => Integer,
|
34
38
|
# "<attribute name>" => String }
|
@@ -71,6 +75,9 @@ module IntrospectiveGrape
|
|
71
75
|
#end
|
72
76
|
|
73
77
|
def restful(model, strong_params=[], routes=[])
|
78
|
+
if model.respond_to?(:attribute_param_types)
|
79
|
+
raise IntrospectiveGrapeError.new("#{model.name}'s attribute_param_types class method needs to be changed to grape_param_types")
|
80
|
+
end
|
74
81
|
# Recursively define endpoints for the model and any nested models.
|
75
82
|
#
|
76
83
|
# model: the model class for the API
|
@@ -337,13 +344,18 @@ module IntrospectiveGrape
|
|
337
344
|
# All params are optional on an update, only require them during creation.
|
338
345
|
# Updating a record with new child models will have to rely on ActiveRecord
|
339
346
|
# validations:
|
340
|
-
dsl.requires field, type: param_type(model,field)
|
347
|
+
dsl.requires field, { type: param_type(model,field) }.merge( validations(model, field) )
|
341
348
|
else
|
342
|
-
dsl.optional field,
|
349
|
+
#dsl.optional field, *options
|
350
|
+
dsl.optional field, { type: param_type(model,field) }.merge( validations(model, field) )
|
343
351
|
end
|
344
352
|
end
|
345
353
|
end
|
346
354
|
|
355
|
+
def validations(model, field)
|
356
|
+
(model.try(:grape_validations) || {}).with_indifferent_access[field] || {}
|
357
|
+
end
|
358
|
+
|
347
359
|
def generate_nested_params(dsl,action,model,fields)
|
348
360
|
klass = self
|
349
361
|
fields.each do |r,v|
|
@@ -391,7 +403,7 @@ module IntrospectiveGrape
|
|
391
403
|
# Check if it's a file attachment, look for an override class from the model,
|
392
404
|
# check Pg2Ruby, use the database type, or fail over to a String:
|
393
405
|
( is_file_attachment?(model,f) && Rack::Multipart::UploadedFile ) ||
|
394
|
-
(model.try(:
|
406
|
+
(model.try(:grape_param_types)||{}).with_indifferent_access[f] ||
|
395
407
|
Pg2Ruby[db_type] ||
|
396
408
|
begin db_type.to_s.camelize.constantize rescue nil end ||
|
397
409
|
String
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'grape/validations'
|
2
|
+
module Grape::Validators
|
3
|
+
|
4
|
+
class Json < Grape::Validations::Base
|
5
|
+
def validate_param!(field, params)
|
6
|
+
begin
|
7
|
+
JSON.parse( params[field] )
|
8
|
+
rescue
|
9
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(field)], message: 'must be valid JSON!'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class JsonArray < Grape::Validations::Base
|
15
|
+
def validate_param!(field, params)
|
16
|
+
begin
|
17
|
+
raise unless JSON.parse( params[field] ).kind_of? Array
|
18
|
+
rescue
|
19
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(field)], message: 'must be valid JSON array!'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class JsonHash < Grape::Validations::Base
|
25
|
+
def validate_param!(field, params)
|
26
|
+
begin
|
27
|
+
raise unless JSON.parse( params[field] ).kind_of? Hash
|
28
|
+
rescue
|
29
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(field)], message: 'must be valid JSON hash!'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -11,4 +11,25 @@ class Company < AbstractAdapter
|
|
11
11
|
|
12
12
|
validates_length_of :name, maximum: 256
|
13
13
|
validates_length_of :short_name, maximum: 10
|
14
|
+
|
15
|
+
def self.grape_validations
|
16
|
+
{
|
17
|
+
gizmos: { json: true },
|
18
|
+
widgets: { json_array: true },
|
19
|
+
sprockets: { json_hash: true }
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def gizmos=(json)
|
24
|
+
JSON.parse(json)
|
25
|
+
end
|
26
|
+
|
27
|
+
def widgets=(json_array)
|
28
|
+
JSON.parse(json_array)
|
29
|
+
end
|
30
|
+
|
31
|
+
def sprockets=(json_hash)
|
32
|
+
JSON.parse(json_hash)
|
33
|
+
end
|
34
|
+
|
14
35
|
end
|
@@ -3,13 +3,18 @@ class Role < AbstractAdapter
|
|
3
3
|
belongs_to :ownable, polymorphic: true
|
4
4
|
|
5
5
|
validates_uniqueness_of :user_id, scope: [:ownable_type,:ownable_id], unless: "user_id.nil?", message: "user has already been assigned that role"
|
6
|
-
|
6
|
+
OWNABLE_TYPES = %w(SuperUser Company Project).freeze
|
7
|
+
validates_inclusion_of :ownable_type, in: OWNABLE_TYPES
|
7
8
|
|
8
9
|
delegate :email, to: :user, allow_nil: true
|
9
10
|
def attributes
|
10
11
|
super.merge(email: email)
|
11
12
|
end
|
12
13
|
|
14
|
+
def self.grape_validations
|
15
|
+
{ ownable_type: { values: OWNABLE_TYPES } }
|
16
|
+
end
|
17
|
+
|
13
18
|
def ownable
|
14
19
|
# return the SuperUser null object
|
15
20
|
ownable_type == 'SuperUser' ? SuperUser.new : super
|
@@ -86,4 +86,19 @@ describe Dummy::CompanyAPI, type: :request do
|
|
86
86
|
json['error'].should == "Name: is too long (maximum is 256 characters), Short Name: is too long (maximum is 10 characters)"
|
87
87
|
end
|
88
88
|
|
89
|
+
it "should validate json parameters" do
|
90
|
+
put "/api/v1/companies/#{company.id}", { gizmos: "garbage" }
|
91
|
+
json["error"].should eq "gizmos must be valid JSON!"
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should validate json array parameters" do
|
95
|
+
put "/api/v1/companies/#{company.id}", { widgets: "[garbage[\"A\",\"B\"]" }
|
96
|
+
json["error"].should eq "widgets must be valid JSON array!"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should validate json hash parameters" do
|
100
|
+
put "/api/v1/companies/#{company.id}", { sprockets: "{\"foo\":\"bar\"}garbage}" }
|
101
|
+
json["error"].should eq "sprockets must be valid JSON hash!"
|
102
|
+
end
|
103
|
+
|
89
104
|
end
|
@@ -34,4 +34,9 @@ describe Dummy::RoleAPI, type: :request do
|
|
34
34
|
json['error'].should =~ /user has already been assigned that role/
|
35
35
|
end
|
36
36
|
|
37
|
+
it 'validates ownable type value specified in grape_validations' do
|
38
|
+
post '/api/v1/roles', { user_id: user.id, ownable_type: 'NotSuperUser' }
|
39
|
+
response.code.should == '400'
|
40
|
+
json['error'].should eq "ownable_type does not have a valid value"
|
41
|
+
end
|
37
42
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: introspective_grape
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Buermann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -276,6 +276,7 @@ files:
|
|
276
276
|
- lib/introspective_grape/formatter/camel_json.rb
|
277
277
|
- lib/introspective_grape/helpers.rb
|
278
278
|
- lib/introspective_grape/traversal.rb
|
279
|
+
- lib/introspective_grape/validators.rb
|
279
280
|
- lib/introspective_grape/version.rb
|
280
281
|
- lib/tasks/introspective_grape_tasks.rake
|
281
282
|
- spec/.DS_Store
|