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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f52e3f6200f277c190a76450e09f65c582a9e56d
4
- data.tar.gz: f2779c6afdfa697bc4d051d3ab461d9afcb91e6f
3
+ metadata.gz: 4a21e9540c7f4929e4dced1d88eb599edb4e887b
4
+ data.tar.gz: e97148d4dd00bba79807d6b52a79a29073fbbc9f
5
5
  SHA512:
6
- metadata.gz: fc38af247ee9f040212122583701537567fc3a6551339c35034f88a0edef5acea6ed79b020fb7d5f3d16dddcc299bd12b882870d1a90826220bf00b6d8bfbb51
7
- data.tar.gz: 08b47cd609c1501a9eeeb4293d5dc3dee9a97028c814aef22a9930f543ba529439358258f2b75a0f7af0d00597d5b73fb6d58ed57738683b6f7c397f384296e9
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.attribute_param_types
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.attribute_param_types
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, type: param_type(model,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(:attribute_param_types)||{})[f] ||
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
@@ -1,3 +1,3 @@
1
1
  module IntrospectiveGrape
2
- VERSION = "0.2.5".freeze
2
+ VERSION = "0.2.6".freeze
3
3
  end
@@ -1,7 +1,7 @@
1
1
  class Dummy::CompanyAPI < IntrospectiveGrape::API
2
2
  paginate
3
3
 
4
- restful Company do
4
+ restful Company, [:id, :name, :short_name, :gizmos, :widgets, :sprockets] do
5
5
 
6
6
  desc "Test default values in an extra endpoint"
7
7
  params do
@@ -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
- validates_inclusion_of :ownable_type, in: ['SuperUser', 'Company', 'Project']
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
@@ -69,7 +69,7 @@ class User < AbstractAdapter
69
69
  avatar.try(:file).try(:url,size)
70
70
  end
71
71
 
72
- def self.attribute_param_types
72
+ def self.grape_param_types
73
73
  { "skip_confirmation_email" => Virtus::Attribute::Boolean }
74
74
  end
75
75
 
@@ -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.5
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-10-28 00:00:00.000000000 Z
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