introspective_grape 0.5.0 → 0.5.5
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/.github/workflows/security.yml +32 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +22 -0
- data/README.md +1 -2
- data/introspective_grape.gemspec +2 -2
- data/lib/introspective_grape/api.rb +3 -3
- data/lib/introspective_grape/filters.rb +1 -1
- data/lib/introspective_grape/traversal.rb +56 -56
- data/lib/introspective_grape/version.rb +1 -1
- data/spec/dummy/.ruby-version +1 -0
- data/spec/dummy/Gemfile +3 -3
- data/spec/dummy/app/api/dummy/company_api.rb +9 -0
- data/spec/requests/company_api_spec.rb +9 -0
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e942af5ab00cc32f0611bfe27ec28ad6dbeb3d5b6f20d847dae3f1dd66cd5d2
|
4
|
+
data.tar.gz: 489588ff1f420618c18566e111933dab186cdc6d16493ec8a3f17cfad8ece9d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 783bdfd1b17ba6709e2d86146d27e427579051e71de65fb76bebfe4f77ca445093fd03176f6adacf6749accf918ceb9a0a0fbb205ef06329da18535962ad98bd
|
7
|
+
data.tar.gz: 60bbbbf652648b841091fa37b6494b87a87fe6b72732b0d1f0540ad5b52ec258684b4ccd19bcee99f285b608a73a08f75975e0c6777900774b23d1b1d724e2d5
|
@@ -0,0 +1,32 @@
|
|
1
|
+
name: security
|
2
|
+
on: push
|
3
|
+
jobs:
|
4
|
+
security:
|
5
|
+
name: Security
|
6
|
+
runs-on: ubuntu-latest
|
7
|
+
steps:
|
8
|
+
- name: Checkout Repository
|
9
|
+
uses: actions/checkout@v2
|
10
|
+
- name: Set up Ruby
|
11
|
+
uses: ruby/setup-ruby@v1
|
12
|
+
with:
|
13
|
+
ruby-version: 2.6.2
|
14
|
+
- name: Cache Ruby Gems
|
15
|
+
uses: actions/cache@v2
|
16
|
+
with:
|
17
|
+
path: vendor/bundle
|
18
|
+
key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
|
19
|
+
restore-keys: |
|
20
|
+
${{ runner.os }}-gems-
|
21
|
+
- name: Bundle Install
|
22
|
+
run: |
|
23
|
+
gem install bundler
|
24
|
+
bundle config path vendor/bundle
|
25
|
+
bundle install --jobs 4 --retry 3
|
26
|
+
- name: Security Checks
|
27
|
+
run: |
|
28
|
+
bundle exec brakeman -z
|
29
|
+
gem install bundle-audit
|
30
|
+
bundle-audit update
|
31
|
+
bundle-audit
|
32
|
+
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.7.3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
|
2
|
+
0.5.5 02/24/2022
|
3
|
+
================
|
4
|
+
|
5
|
+
Update the local ruby in the gem to 2.7.3 to clear security warning cruft.
|
6
|
+
|
7
|
+
Sanitize some sql.
|
8
|
+
|
9
|
+
Target the still-supported 'kt-paperclip' in the gemspec (many pardons while we're planning the switch over to activestorage support).
|
10
|
+
|
11
|
+
Fix bug from looking for the ActiveRecord columns_hash on non-ActiveRecord objects.
|
12
|
+
|
13
|
+
0.5.2 02/07/2022
|
14
|
+
================
|
15
|
+
|
16
|
+
Lock down compatible Grape version to 1.6.0, as the next version has breaking changes for classes inheriting from Grape::Validator::Base.
|
17
|
+
|
18
|
+
0.5.1 02/07/2022
|
19
|
+
================
|
20
|
+
|
21
|
+
Removed stray require of 'byebug'.
|
22
|
+
|
1
23
|
0.5.0 10/20/2021
|
2
24
|
================
|
3
25
|
|
data/README.md
CHANGED
@@ -305,8 +305,7 @@ myself, but would be trivial to make more atomistic.
|
|
305
305
|
## Documenting Endpoints
|
306
306
|
|
307
307
|
If you wish to provide additional documentation for end points you can define
|
308
|
-
`self.<index,show,update,create,destroy>_documentation` class methods in the API class (or extend them
|
309
|
-
from a documentation module, which would be preferable).
|
308
|
+
`self.<index,show,update,create,destroy>_documentation(name)` class methods in the API class (or extend them from a documentation module, which would be preferable).
|
310
309
|
|
311
310
|
## Grape Hooks - The Precedence of Declaration Matters
|
312
311
|
|
data/introspective_grape.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
|
|
28
28
|
s.add_runtime_dependency 'schema_validations'
|
29
29
|
s.add_runtime_dependency 'rack'
|
30
30
|
|
31
|
-
s.add_runtime_dependency 'grape'
|
31
|
+
s.add_runtime_dependency 'grape', '1.6.0'
|
32
32
|
s.add_runtime_dependency 'dry-types'
|
33
33
|
s.add_runtime_dependency 'grape-entity'
|
34
34
|
s.add_runtime_dependency 'grape-swagger'
|
@@ -57,7 +57,7 @@ Gem::Specification.new do |s|
|
|
57
57
|
s.add_development_dependency 'machinist_redux'
|
58
58
|
|
59
59
|
# dummy app dependencies
|
60
|
-
s.add_development_dependency 'paperclip'
|
60
|
+
s.add_development_dependency 'kt-paperclip'
|
61
61
|
s.add_development_dependency 'rufus-mnemo'
|
62
62
|
s.add_development_dependency 'devise'
|
63
63
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'action_controller'
|
2
2
|
require 'kaminari'
|
3
|
-
require 'byebug'
|
3
|
+
#require 'byebug'
|
4
4
|
require 'grape-kaminari'
|
5
5
|
require 'introspective_grape/validators'
|
6
6
|
|
@@ -403,12 +403,12 @@ module IntrospectiveGrape
|
|
403
403
|
def param_type(model, field)
|
404
404
|
# Translate from the AR type to the GrapeParam types
|
405
405
|
field = field.to_s
|
406
|
-
db_type = (model&.columns_hash || {})[field]&.type
|
406
|
+
db_type = (model&.try(:columns_hash) || {})[field]&.type
|
407
407
|
|
408
408
|
# Check if it's a file attachment, look for an override class from the model,
|
409
409
|
# check PG2RUBY, use the database type, or fail over to a String:
|
410
410
|
uploaded_file?(model, field) ||
|
411
|
-
check_model_for_type(model, field)
|
411
|
+
check_model_for_type(model, field) ||
|
412
412
|
PG2RUBY[db_type] ||
|
413
413
|
db_type_constant(db_type) ||
|
414
414
|
String # default to String if nothing else works
|
@@ -91,7 +91,7 @@ module IntrospectiveGrape
|
|
91
91
|
|
92
92
|
if timestamp_filter(klass, model, field)
|
93
93
|
op = field.ends_with?('_start') ? '>=' : '<='
|
94
|
-
records.where("#{timestamp_filter(klass, model, field)} #{op} ?", Time.zone.parse(params[field]))
|
94
|
+
records.where(ActiveRecord::Base.sanitize_sql_array(["#{timestamp_filter(klass, model, field)} #{op} ?", Time.zone.parse(params[field])]))
|
95
95
|
elsif model.respond_to?("#{field}=")
|
96
96
|
records.send("#{field}=", params[field])
|
97
97
|
else
|
@@ -1,56 +1,56 @@
|
|
1
|
-
module IntrospectiveGrape
|
2
|
-
module Traversal
|
3
|
-
# For deeply nested endpoints we want to present the record being affected, these
|
4
|
-
# methods traverse down from the parent instance to the child model associations
|
5
|
-
# of the deeply nested route.
|
6
|
-
|
7
|
-
def find_leaves(routes, record, params)
|
8
|
-
# Traverse down our route and find the leaf's siblings from its parent, e.g.
|
9
|
-
# project/#/teams/#/team_users ~> project.find.teams.find.team_users
|
10
|
-
# (the traversal of the intermediate nodes occurs in find_leaf())
|
11
|
-
return record if routes.size < 2 # the leaf is the root
|
12
|
-
|
13
|
-
record = find_leaf(routes, record, params) || return
|
14
|
-
|
15
|
-
assoc = routes.last
|
16
|
-
if assoc.many?
|
17
|
-
leaves = record.send( assoc.reflection.name ).includes( default_includes(assoc.model) )
|
18
|
-
verify_records_found(leaves, routes)
|
19
|
-
leaves
|
20
|
-
else
|
21
|
-
# has_one associations don't return a CollectionProxy and so don't support
|
22
|
-
# eager loading.
|
23
|
-
record.send( assoc.reflection.name )
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def verify_records_found(leaves, routes)
|
28
|
-
return if (leaves.map(&:class) - [routes.last.model]).empty?
|
29
|
-
|
30
|
-
raise ActiveRecord::RecordNotFound.new("Records contain the wrong models, they should all be #{routes.last.model.name}, found #{records.map(&:class).map(&:name).join(',')}")
|
31
|
-
end
|
32
|
-
|
33
|
-
def find_leaf(routes, record, params)
|
34
|
-
return record unless routes.size > 1
|
35
|
-
|
36
|
-
# For deeply nested routes we need to search from the root of the API to the leaf
|
37
|
-
# of its nested associations in order to guarantee the validity of the relationship,
|
38
|
-
# the authorization on the parent model, and the sanity of passed parameters.
|
39
|
-
routes[1..-1].each do |r|
|
40
|
-
if record && params[r.key]
|
41
|
-
ref = r.reflection
|
42
|
-
record = record.send(ref.name).where( id: params[r.key] ).first if ref
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
verify_record_found(routes, params, record)
|
47
|
-
record
|
48
|
-
end
|
49
|
-
|
50
|
-
def verify_record_found(routes, params, record)
|
51
|
-
return unless params[routes.last.key] && record.class != routes.last.model
|
52
|
-
|
53
|
-
raise ActiveRecord::RecordNotFound.new("No #{routes.last.model.name} with ID '#{params[routes.last.key]}'")
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
1
|
+
module IntrospectiveGrape
|
2
|
+
module Traversal
|
3
|
+
# For deeply nested endpoints we want to present the record being affected, these
|
4
|
+
# methods traverse down from the parent instance to the child model associations
|
5
|
+
# of the deeply nested route.
|
6
|
+
|
7
|
+
def find_leaves(routes, record, params)
|
8
|
+
# Traverse down our route and find the leaf's siblings from its parent, e.g.
|
9
|
+
# project/#/teams/#/team_users ~> project.find.teams.find.team_users
|
10
|
+
# (the traversal of the intermediate nodes occurs in find_leaf())
|
11
|
+
return record if routes.size < 2 # the leaf is the root
|
12
|
+
|
13
|
+
record = find_leaf(routes, record, params) || return
|
14
|
+
|
15
|
+
assoc = routes.last
|
16
|
+
if assoc.many?
|
17
|
+
leaves = record.send( assoc.reflection.name ).includes( default_includes(assoc.model) )
|
18
|
+
verify_records_found(leaves, routes)
|
19
|
+
leaves
|
20
|
+
else
|
21
|
+
# has_one associations don't return a CollectionProxy and so don't support
|
22
|
+
# eager loading.
|
23
|
+
record.send( assoc.reflection.name )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def verify_records_found(leaves, routes)
|
28
|
+
return if (leaves.map(&:class) - [routes.last.model]).empty?
|
29
|
+
|
30
|
+
raise ActiveRecord::RecordNotFound.new("Records contain the wrong models, they should all be #{routes.last.model.name}, found #{records.map(&:class).map(&:name).join(',')}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_leaf(routes, record, params)
|
34
|
+
return record unless routes.size > 1
|
35
|
+
|
36
|
+
# For deeply nested routes we need to search from the root of the API to the leaf
|
37
|
+
# of its nested associations in order to guarantee the validity of the relationship,
|
38
|
+
# the authorization on the parent model, and the sanity of passed parameters.
|
39
|
+
routes[1..-1].each do |r|
|
40
|
+
if record && params[r.key]
|
41
|
+
ref = r.reflection
|
42
|
+
record = record.send(ref.name).where( id: params[r.key] ).first if ref
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
verify_record_found(routes, params, record)
|
47
|
+
record
|
48
|
+
end
|
49
|
+
|
50
|
+
def verify_record_found(routes, params, record)
|
51
|
+
return unless params[routes.last.key] && record.class != routes.last.model
|
52
|
+
|
53
|
+
raise ActiveRecord::RecordNotFound.new("No #{routes.last.model.name} with ID '#{params[routes.last.key]}'")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
2.6.0
|
data/spec/dummy/Gemfile
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
|
-
gem 'rails', '5.2.6'
|
2
|
+
gem 'rails', '5.2.6.2'
|
3
3
|
|
4
4
|
gem 'byebug'
|
5
5
|
gem 'camel_snake_keys'
|
6
6
|
|
7
7
|
gem 'devise'
|
8
8
|
gem 'delayed_paperclip'
|
9
|
-
|
9
|
+
gem 'kt-paperclip'
|
10
10
|
|
11
11
|
gem 'grape'
|
12
12
|
gem 'grape-entity'
|
13
13
|
gem 'grape-kaminari'
|
14
14
|
gem 'grape-swagger'
|
15
15
|
gem 'grape-swagger-entity'
|
16
|
-
gem 'introspective_grape'
|
16
|
+
gem 'introspective_grape' #, path: '~/Dropbox/contrib/introspective_grape'
|
17
17
|
|
18
18
|
gem 'paperclip'
|
19
19
|
gem 'pundit'
|
@@ -14,6 +14,15 @@ class Dummy::CompanyAPI < IntrospectiveGrape::API
|
|
14
14
|
present params
|
15
15
|
end
|
16
16
|
|
17
|
+
desc "Test kaminari pagination in a custom index"
|
18
|
+
params do
|
19
|
+
use :pagination
|
20
|
+
end
|
21
|
+
get '/paginated/list' do
|
22
|
+
authorize Company.new, :index?
|
23
|
+
companies = Company.all
|
24
|
+
present paginate(companies), using: CompanyEntity
|
25
|
+
end
|
17
26
|
end
|
18
27
|
|
19
28
|
class CompanyEntity < Grape::Entity
|
@@ -27,6 +27,15 @@ describe Dummy::CompanyAPI, type: :request do
|
|
27
27
|
json.first['id'].should eq Company.first.id
|
28
28
|
response.headers.slice("X-Total", "X-Total-Pages", "X-Per-Page", "X-Page", "X-Next-Page", "X-Prev-Page", "X-Offset").values.should eq ["30", "2", "25", "1", "2", "", "0"]
|
29
29
|
end
|
30
|
+
|
31
|
+
it "should allow pagination in a custom index" do
|
32
|
+
Company.destroy_all
|
33
|
+
30.times { Company.make! }
|
34
|
+
|
35
|
+
get '/api/v1/companies/paginated/list'
|
36
|
+
response.should be_successful
|
37
|
+
response.headers.slice("X-Total", "X-Total-Pages", "X-Per-Page", "X-Page", "X-Next-Page", "X-Prev-Page", "X-Offset").values.should eq ["30", "2", "25", "1", "2", "", "0"]
|
38
|
+
end
|
30
39
|
end
|
31
40
|
|
32
41
|
before :all do
|
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.5.
|
4
|
+
version: 0.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Buermann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -56,16 +56,16 @@ dependencies:
|
|
56
56
|
name: grape
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - '='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 1.6.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - '='
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 1.6.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: dry-types
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -291,7 +291,7 @@ dependencies:
|
|
291
291
|
- !ruby/object:Gem::Version
|
292
292
|
version: '0'
|
293
293
|
- !ruby/object:Gem::Dependency
|
294
|
-
name: paperclip
|
294
|
+
name: kt-paperclip
|
295
295
|
requirement: !ruby/object:Gem::Requirement
|
296
296
|
requirements:
|
297
297
|
- - ">="
|
@@ -344,6 +344,7 @@ extensions: []
|
|
344
344
|
extra_rdoc_files: []
|
345
345
|
files:
|
346
346
|
- ".github/workflows/lint.yml"
|
347
|
+
- ".github/workflows/security.yml"
|
347
348
|
- ".github/workflows/test.yml"
|
348
349
|
- ".gitignore"
|
349
350
|
- ".rubocop.yml"
|
@@ -375,6 +376,7 @@ files:
|
|
375
376
|
- lib/introspective_grape/validators.rb
|
376
377
|
- lib/introspective_grape/version.rb
|
377
378
|
- lib/tasks/introspective_grape_tasks.rake
|
379
|
+
- spec/dummy/.ruby-version
|
378
380
|
- spec/dummy/Gemfile
|
379
381
|
- spec/dummy/README.rdoc
|
380
382
|
- spec/dummy/Rakefile
|
@@ -538,12 +540,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
538
540
|
- !ruby/object:Gem::Version
|
539
541
|
version: '0'
|
540
542
|
requirements: []
|
541
|
-
|
542
|
-
rubygems_version: 2.7.3
|
543
|
+
rubygems_version: 3.1.6
|
543
544
|
signing_key:
|
544
545
|
specification_version: 4
|
545
546
|
summary: Quickly configure Grape APIs around your database schema and models.
|
546
547
|
test_files:
|
548
|
+
- spec/dummy/.ruby-version
|
547
549
|
- spec/dummy/Gemfile
|
548
550
|
- spec/dummy/README.rdoc
|
549
551
|
- spec/dummy/Rakefile
|