introspective_grape 0.2.5 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|