zero-params_processor 0.4.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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +91 -0
- data/.travis.yml +16 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +157 -0
- data/LICENSE.txt +21 -0
- data/README.md +120 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/params_processor.rb +90 -0
- data/lib/params_processor/config.rb +61 -0
- data/lib/params_processor/doc_converter.rb +76 -0
- data/lib/params_processor/param_doc.rb +65 -0
- data/lib/params_processor/type_convert.rb +81 -0
- data/lib/params_processor/validate.rb +198 -0
- data/lib/params_processor/version.rb +3 -0
- data/zero-params_processor.gemspec +34 -0
- metadata +176 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 8ec687b8215056d4e4197b1c0a2e11425e00460d84c6e204b93f756f4ece1b4e
|
|
4
|
+
data.tar.gz: a494c7786f8390b2f254c6b9bffffe6d303447aa859d390c1a25beb18052dc20
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a0e1025443eee3ba5a337dd7107c7621c716679cd790f7944ac25f1d6f3ad8ce1979debd97d4cae212bbbc61aac6e42f6e7b2e4cc86844364e66565d93b126d6
|
|
7
|
+
data.tar.gz: c50e9d5f36bccb010b1b23eea0557523df285a40fbcb9bd24accc2057fe67efa3308e924d480cba011979c4e57f36fccd8aef842076927222402728892f6e4b8
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# This is the configuration used to check the rubocop source code.
|
|
2
|
+
|
|
3
|
+
#require: rbocop/cop/internal_affairs
|
|
4
|
+
|
|
5
|
+
AllCops:
|
|
6
|
+
# Include common Ruby source files.
|
|
7
|
+
Exclude:
|
|
8
|
+
- 'spec/**/*'
|
|
9
|
+
- 'zero-params_processor.gemspec'
|
|
10
|
+
- 'Gemfile'
|
|
11
|
+
- 'bin/**/*'
|
|
12
|
+
DisplayCopNames: true
|
|
13
|
+
TargetRubyVersion: 2.4
|
|
14
|
+
TargetRailsVersion: 5.1
|
|
15
|
+
|
|
16
|
+
Style/FrozenStringLiteralComment:
|
|
17
|
+
Enabled: false
|
|
18
|
+
|
|
19
|
+
Metrics/LineLength:
|
|
20
|
+
Max: 1200
|
|
21
|
+
|
|
22
|
+
Metrics/ClassLength:
|
|
23
|
+
Max: 1200
|
|
24
|
+
|
|
25
|
+
Metrics/MethodLength:
|
|
26
|
+
Max: 1200
|
|
27
|
+
|
|
28
|
+
Metrics/ModuleLength:
|
|
29
|
+
Max: 1200
|
|
30
|
+
|
|
31
|
+
Metrics/BlockLength:
|
|
32
|
+
Max: 1200
|
|
33
|
+
|
|
34
|
+
Style/Documentation:
|
|
35
|
+
Enabled: false
|
|
36
|
+
|
|
37
|
+
Layout/SpaceInsideBrackets:
|
|
38
|
+
Enabled: false
|
|
39
|
+
|
|
40
|
+
Layout/SpaceInsideHashLiteralBraces:
|
|
41
|
+
Enabled: false
|
|
42
|
+
|
|
43
|
+
Layout/SpaceInsidePercentLiteralDelimiters:
|
|
44
|
+
Enabled: false
|
|
45
|
+
|
|
46
|
+
Style/AsciiComments:
|
|
47
|
+
Enabled: false
|
|
48
|
+
|
|
49
|
+
Layout/IndentHash:
|
|
50
|
+
Enabled: false
|
|
51
|
+
|
|
52
|
+
Layout/AlignHash:
|
|
53
|
+
Enabled: false
|
|
54
|
+
|
|
55
|
+
Style/HashSyntax:
|
|
56
|
+
Enabled: false
|
|
57
|
+
|
|
58
|
+
Style/ClassAndModuleChildren:
|
|
59
|
+
Enabled: false
|
|
60
|
+
|
|
61
|
+
Layout/EmptyLines:
|
|
62
|
+
Enabled: false
|
|
63
|
+
|
|
64
|
+
Lint/UnneededSplatExpansion:
|
|
65
|
+
Enabled: false
|
|
66
|
+
|
|
67
|
+
#Style/StringLiterals:
|
|
68
|
+
# Enabled: true
|
|
69
|
+
# EnforcedStyle: double_quotes
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
Style/UnneededPercentQ:
|
|
75
|
+
Enabled: false
|
|
76
|
+
|
|
77
|
+
Style/RedundantSelf:
|
|
78
|
+
Enabled: false
|
|
79
|
+
|
|
80
|
+
Metrics/CyclomaticComplexity:
|
|
81
|
+
Enabled: false
|
|
82
|
+
|
|
83
|
+
Metrics/AbcSize:
|
|
84
|
+
Enabled: false
|
|
85
|
+
|
|
86
|
+
Metrics/PerceivedComplexity:
|
|
87
|
+
Enabled: false
|
|
88
|
+
|
|
89
|
+
# False positives
|
|
90
|
+
Layout/EmptyLineBetweenDefs:
|
|
91
|
+
Enabled: false
|
data/.travis.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
sudo: false
|
|
2
|
+
language: ruby
|
|
3
|
+
rvm:
|
|
4
|
+
- 2.5.1
|
|
5
|
+
before_install: gem install bundler
|
|
6
|
+
env:
|
|
7
|
+
global:
|
|
8
|
+
- CC_TEST_REPORTER_ID=5f2477af6b38bd4db35abe49930cbae98e867cb3163b2da1a465176b2f0a361c
|
|
9
|
+
before_script:
|
|
10
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
|
11
|
+
- chmod +x ./cc-test-reporter
|
|
12
|
+
- ./cc-test-reporter before-build
|
|
13
|
+
script:
|
|
14
|
+
- bundle exec rspec
|
|
15
|
+
after_script:
|
|
16
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
zero-params_processor (0.4.0)
|
|
5
|
+
activesupport (>= 3)
|
|
6
|
+
multi_json
|
|
7
|
+
rails (>= 3)
|
|
8
|
+
zero-rails_openapi (>= 1.5.2)
|
|
9
|
+
|
|
10
|
+
GEM
|
|
11
|
+
remote: https://rubygems.org/
|
|
12
|
+
specs:
|
|
13
|
+
actioncable (5.2.1)
|
|
14
|
+
actionpack (= 5.2.1)
|
|
15
|
+
nio4r (~> 2.0)
|
|
16
|
+
websocket-driver (>= 0.6.1)
|
|
17
|
+
actionmailer (5.2.1)
|
|
18
|
+
actionpack (= 5.2.1)
|
|
19
|
+
actionview (= 5.2.1)
|
|
20
|
+
activejob (= 5.2.1)
|
|
21
|
+
mail (~> 2.5, >= 2.5.4)
|
|
22
|
+
rails-dom-testing (~> 2.0)
|
|
23
|
+
actionpack (5.2.1)
|
|
24
|
+
actionview (= 5.2.1)
|
|
25
|
+
activesupport (= 5.2.1)
|
|
26
|
+
rack (~> 2.0)
|
|
27
|
+
rack-test (>= 0.6.3)
|
|
28
|
+
rails-dom-testing (~> 2.0)
|
|
29
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
30
|
+
actionview (5.2.1)
|
|
31
|
+
activesupport (= 5.2.1)
|
|
32
|
+
builder (~> 3.1)
|
|
33
|
+
erubi (~> 1.4)
|
|
34
|
+
rails-dom-testing (~> 2.0)
|
|
35
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
36
|
+
activejob (5.2.1)
|
|
37
|
+
activesupport (= 5.2.1)
|
|
38
|
+
globalid (>= 0.3.6)
|
|
39
|
+
activemodel (5.2.1)
|
|
40
|
+
activesupport (= 5.2.1)
|
|
41
|
+
activerecord (5.2.1)
|
|
42
|
+
activemodel (= 5.2.1)
|
|
43
|
+
activesupport (= 5.2.1)
|
|
44
|
+
arel (>= 9.0)
|
|
45
|
+
activestorage (5.2.1)
|
|
46
|
+
actionpack (= 5.2.1)
|
|
47
|
+
activerecord (= 5.2.1)
|
|
48
|
+
marcel (~> 0.3.1)
|
|
49
|
+
activesupport (5.2.1)
|
|
50
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
51
|
+
i18n (>= 0.7, < 2)
|
|
52
|
+
minitest (~> 5.1)
|
|
53
|
+
tzinfo (~> 1.1)
|
|
54
|
+
arel (9.0.0)
|
|
55
|
+
builder (3.2.3)
|
|
56
|
+
concurrent-ruby (1.0.5)
|
|
57
|
+
crass (1.0.4)
|
|
58
|
+
diff-lcs (1.3)
|
|
59
|
+
docile (1.1.5)
|
|
60
|
+
erubi (1.7.1)
|
|
61
|
+
globalid (0.4.1)
|
|
62
|
+
activesupport (>= 4.2.0)
|
|
63
|
+
i18n (1.1.0)
|
|
64
|
+
concurrent-ruby (~> 1.0)
|
|
65
|
+
json (2.1.0)
|
|
66
|
+
loofah (2.2.2)
|
|
67
|
+
crass (~> 1.0.2)
|
|
68
|
+
nokogiri (>= 1.5.9)
|
|
69
|
+
mail (2.7.0)
|
|
70
|
+
mini_mime (>= 0.1.1)
|
|
71
|
+
marcel (0.3.2)
|
|
72
|
+
mimemagic (~> 0.3.2)
|
|
73
|
+
method_source (0.9.0)
|
|
74
|
+
mimemagic (0.3.2)
|
|
75
|
+
mini_mime (1.0.1)
|
|
76
|
+
mini_portile2 (2.3.0)
|
|
77
|
+
minitest (5.11.3)
|
|
78
|
+
multi_json (1.13.1)
|
|
79
|
+
nio4r (2.3.1)
|
|
80
|
+
nokogiri (1.8.4)
|
|
81
|
+
mini_portile2 (~> 2.3.0)
|
|
82
|
+
rack (2.0.5)
|
|
83
|
+
rack-test (1.1.0)
|
|
84
|
+
rack (>= 1.0, < 3)
|
|
85
|
+
rails (5.2.1)
|
|
86
|
+
actioncable (= 5.2.1)
|
|
87
|
+
actionmailer (= 5.2.1)
|
|
88
|
+
actionpack (= 5.2.1)
|
|
89
|
+
actionview (= 5.2.1)
|
|
90
|
+
activejob (= 5.2.1)
|
|
91
|
+
activemodel (= 5.2.1)
|
|
92
|
+
activerecord (= 5.2.1)
|
|
93
|
+
activestorage (= 5.2.1)
|
|
94
|
+
activesupport (= 5.2.1)
|
|
95
|
+
bundler (>= 1.3.0)
|
|
96
|
+
railties (= 5.2.1)
|
|
97
|
+
sprockets-rails (>= 2.0.0)
|
|
98
|
+
rails-dom-testing (2.0.3)
|
|
99
|
+
activesupport (>= 4.2.0)
|
|
100
|
+
nokogiri (>= 1.6)
|
|
101
|
+
rails-html-sanitizer (1.0.4)
|
|
102
|
+
loofah (~> 2.2, >= 2.2.2)
|
|
103
|
+
railties (5.2.1)
|
|
104
|
+
actionpack (= 5.2.1)
|
|
105
|
+
activesupport (= 5.2.1)
|
|
106
|
+
method_source
|
|
107
|
+
rake (>= 0.8.7)
|
|
108
|
+
thor (>= 0.19.0, < 2.0)
|
|
109
|
+
rake (10.5.0)
|
|
110
|
+
rspec (3.6.0)
|
|
111
|
+
rspec-core (~> 3.6.0)
|
|
112
|
+
rspec-expectations (~> 3.6.0)
|
|
113
|
+
rspec-mocks (~> 3.6.0)
|
|
114
|
+
rspec-core (3.6.0)
|
|
115
|
+
rspec-support (~> 3.6.0)
|
|
116
|
+
rspec-expectations (3.6.0)
|
|
117
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
118
|
+
rspec-support (~> 3.6.0)
|
|
119
|
+
rspec-mocks (3.6.0)
|
|
120
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
121
|
+
rspec-support (~> 3.6.0)
|
|
122
|
+
rspec-support (3.6.0)
|
|
123
|
+
simplecov (0.15.1)
|
|
124
|
+
docile (~> 1.1.0)
|
|
125
|
+
json (>= 1.8, < 3)
|
|
126
|
+
simplecov-html (~> 0.10.0)
|
|
127
|
+
simplecov-html (0.10.2)
|
|
128
|
+
sprockets (3.7.2)
|
|
129
|
+
concurrent-ruby (~> 1.0)
|
|
130
|
+
rack (> 1, < 3)
|
|
131
|
+
sprockets-rails (3.2.1)
|
|
132
|
+
actionpack (>= 4.0)
|
|
133
|
+
activesupport (>= 4.0)
|
|
134
|
+
sprockets (>= 3.0.0)
|
|
135
|
+
thor (0.20.0)
|
|
136
|
+
thread_safe (0.3.6)
|
|
137
|
+
tzinfo (1.2.5)
|
|
138
|
+
thread_safe (~> 0.1)
|
|
139
|
+
websocket-driver (0.7.0)
|
|
140
|
+
websocket-extensions (>= 0.1.0)
|
|
141
|
+
websocket-extensions (0.1.3)
|
|
142
|
+
zero-rails_openapi (1.5.2)
|
|
143
|
+
activesupport (>= 3)
|
|
144
|
+
rails (>= 3)
|
|
145
|
+
|
|
146
|
+
PLATFORMS
|
|
147
|
+
ruby
|
|
148
|
+
|
|
149
|
+
DEPENDENCIES
|
|
150
|
+
bundler (~> 1.16.a)
|
|
151
|
+
rake (~> 10.0)
|
|
152
|
+
rspec (~> 3.0)
|
|
153
|
+
simplecov
|
|
154
|
+
zero-params_processor!
|
|
155
|
+
|
|
156
|
+
BUNDLED WITH
|
|
157
|
+
1.16.3
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 zhandao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Zero::ParamsProcessor
|
|
2
|
+
|
|
3
|
+
[](https://travis-ci.org/zhandao/zero-params_processor)
|
|
4
|
+
[](https://codeclimate.com/github/zhandao/zero-params_processor/maintainability)
|
|
5
|
+
[](https://codeclimate.com/github/zhandao/zero-params_processor/test_coverage)
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
# declare aop callback which provided by zpp
|
|
9
|
+
before_action :process_params!
|
|
10
|
+
|
|
11
|
+
# if you defined following spec in your api doc
|
|
12
|
+
# `query!` bang method means it's a required param
|
|
13
|
+
query! :time, Date, gt: '2018/1/1'.to_date, permit: true
|
|
14
|
+
|
|
15
|
+
# THEN in your controller action, you will get:
|
|
16
|
+
# 1. param validate: require, Date type and range
|
|
17
|
+
# 2. value convert: JSON has not Date type, you must
|
|
18
|
+
# do a convert from String, but it can do it for you:
|
|
19
|
+
params[:time] = params[:time].to_date # after some format checkers
|
|
20
|
+
# 3. set instance variable: allows you get the param by `@time`
|
|
21
|
+
# instead of `params[:time]`
|
|
22
|
+
# 4. permitted: if you defined a lot of params with `permit: true`,
|
|
23
|
+
# you will be allowed to get them by calling `permitted`, like:
|
|
24
|
+
Book.create(permitted)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## ONLY FOR the RAILS app that using Zero-Rails_OpenApi, like [Zero-Rails](https://github.com/zhandao/zero-rails)
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
Add this line to your application's Gemfile:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
gem 'zero-params_processor'#, github: 'zhandao/zero-params_processor'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
And then execute:
|
|
38
|
+
|
|
39
|
+
$ bundle
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
before_action { process_params_by :validate!, :convert }
|
|
45
|
+
before_action :process_params! # all actions will be called
|
|
46
|
+
```
|
|
47
|
+
Action options: %i[ validate! convert set_instance_var set_permitted ]
|
|
48
|
+
|
|
49
|
+
### validate!
|
|
50
|
+
|
|
51
|
+
Check each input parameter based on `OpenApi.docs` (Zero-Rails_OpenApi's cattr), it will
|
|
52
|
+
raise `ParamsProcessor::ValidationFailed < StandardError` if check failed.
|
|
53
|
+
|
|
54
|
+
Note: If it did not find the corresponding open-api information in `OpenApi.docs`,
|
|
55
|
+
the check will be skipped.
|
|
56
|
+
|
|
57
|
+
### convert
|
|
58
|
+
|
|
59
|
+
Convert each input parameter to the specified type base on `OpenApi.docs`. For example:
|
|
60
|
+
|
|
61
|
+
We declare the parameter like this:
|
|
62
|
+
```ruby
|
|
63
|
+
query :price, Integer
|
|
64
|
+
query :like, Boolean
|
|
65
|
+
query :time, Date
|
|
66
|
+
```
|
|
67
|
+
In case params[:price] == `'1'`, the Converter will make it to be `1` (a Integer).
|
|
68
|
+
In case params[:like] == `0`, the Converter will make it to be `false`.
|
|
69
|
+
In case params[:time] == `'2018/1/1'`, the Converter will make it to be `Date.new(2018, 1, 1)`.
|
|
70
|
+
|
|
71
|
+
### set_instance_var
|
|
72
|
+
|
|
73
|
+
Let (converted) input parameters to be instance variables of the current controller.
|
|
74
|
+
|
|
75
|
+
After that, you can access the params value like: `@price`, `@like`, `@time`.
|
|
76
|
+
|
|
77
|
+
### set_permitted
|
|
78
|
+
|
|
79
|
+
Permit the parameters that having `permit: true` attribute in `OpenApi.docs`. Like this:
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
query :price, Integer, pmt: true
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Then, the :price parameter will be permitted.
|
|
86
|
+
|
|
87
|
+
After this action, you can access all the permitted input via method `permitted`. Like:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
Book.create(permitted)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Note: `not_permit: true` attribute will lead to a slightly different behavior: All the
|
|
94
|
+
parameter that are not having `not_permit: true` will be permitted. For example:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
query :price, Integer
|
|
98
|
+
query :like, Boolean, npmt: true
|
|
99
|
+
query :time, Date
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Then, the :price and :time will be permitted.
|
|
103
|
+
|
|
104
|
+
## Development
|
|
105
|
+
|
|
106
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
107
|
+
|
|
108
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
109
|
+
|
|
110
|
+
## Contributing
|
|
111
|
+
|
|
112
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/zero-params_processor. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
117
|
+
|
|
118
|
+
## Code of Conduct
|
|
119
|
+
|
|
120
|
+
Everyone interacting in the Zero::ParamsProcessor project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/zero-params_processor/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'zero/params_processor'
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require 'irb'
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
require 'params_processor/version'
|
|
2
|
+
require 'params_processor/validate'
|
|
3
|
+
require 'params_processor/type_convert'
|
|
4
|
+
|
|
5
|
+
module ParamsProcessor
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def process_params_by(*actions)
|
|
9
|
+
return if params_doc.blank?
|
|
10
|
+
pdocs = params_doc.map do |doc|
|
|
11
|
+
param_doc = ParamDoc.new(doc) # TODO: performance
|
|
12
|
+
_validate_param!(param_doc) if actions.index(:validate!)
|
|
13
|
+
_convert_param(param_doc) if actions.index(:convert)
|
|
14
|
+
_set_instance_var(param_doc) if actions.index(:set_instance_var)
|
|
15
|
+
param_doc
|
|
16
|
+
end
|
|
17
|
+
_set_permitted(pdocs) if actions.index(:set_permitted)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def process_params!
|
|
21
|
+
actions = Config.actions || %i[ validate! convert set_instance_var set_permitted ]
|
|
22
|
+
process_params_by *actions
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def _validate_param!(param_doc)
|
|
26
|
+
input = param_doc.in == 'header' ? request.headers[param_doc.name.to_s] : params[param_doc.name.to_sym]
|
|
27
|
+
error_class = "#{controller_name.camelize}Error".constantize rescue nil
|
|
28
|
+
Validate.(input, based_on: param_doc, raise: error_class)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def _convert_param(param_doc)
|
|
32
|
+
name = param_doc.name.to_sym
|
|
33
|
+
params[name] = param_doc.dft if params[name].nil? && !param_doc.dft.nil?
|
|
34
|
+
return if params[name].nil?
|
|
35
|
+
|
|
36
|
+
params[name] = TypeConvert.(params[name], based_on: param_doc)
|
|
37
|
+
# mapping param key
|
|
38
|
+
params[param_doc.as] = params.delete(name) if param_doc.as.present?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def _set_instance_var(param_doc)
|
|
42
|
+
key = param_doc.real_name
|
|
43
|
+
return if (value = params[key]).nil?
|
|
44
|
+
|
|
45
|
+
instance_variable_set("@#{key}", value)
|
|
46
|
+
_permit_hash_and_array(value) if param_doc.permit?
|
|
47
|
+
_auto_find_by_id(key, value) if @route_path.match?(/\{#{key}\}/) # e.g. "/examples/{id}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# If params[:data] == [{..}, {..}], each `{..}` in the array
|
|
51
|
+
# is an instance of ActionController::Parameters.
|
|
52
|
+
# So, if :data is allowed to permit, it's values should also permit.
|
|
53
|
+
def _permit_hash_and_array(value)
|
|
54
|
+
value.map!(&:permit!) if value.is_a?(Array) && value.first.is_a?(ActionController::Parameters)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def _auto_find_by_id(key, value)
|
|
58
|
+
whos_id = (key.to_sym == :id ? controller_name : key.to_s.sub('_id', '')).singularize
|
|
59
|
+
model = whos_id.camelize.constantize rescue return
|
|
60
|
+
model_instance = model.find_by(id: value) || self.class.error_cls.not_found! # TODO HACK
|
|
61
|
+
instance_variable_set("@#{whos_id}", model_instance)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def _set_permitted(params_docs)
|
|
65
|
+
exist_not_permit = params_docs.map(&:not_permit?).any?(&:present?)
|
|
66
|
+
keys = params_docs.map { |p| p.doced_permit? ? p.real_name : nil }.compact
|
|
67
|
+
keys = exist_not_permit ? params_docs.map(&:real_name) - keys : keys
|
|
68
|
+
@permitted = params.permit(*keys)
|
|
69
|
+
# @permitted = params.slice(*keys).to_unsafe_h
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def permitted; @permitted end
|
|
73
|
+
|
|
74
|
+
# TODO: performance
|
|
75
|
+
def params_doc
|
|
76
|
+
current_api = OpenApi.routes_index[self.class.controller_path]
|
|
77
|
+
return [ ] unless current_api
|
|
78
|
+
|
|
79
|
+
DocConverter.docs ||= DocConverter.new(OpenApi.docs)
|
|
80
|
+
@route_path = OpenApi::Generator.find_path_httpverb_by(self.class.controller_path, action_name).first
|
|
81
|
+
path_doc = DocConverter.docs[current_api][:paths][@route_path]
|
|
82
|
+
# nil check is for skipping this before_action when the action is not doced.
|
|
83
|
+
path_doc&.[](request.method.downcase)&.[](:parameters) || [ ]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ValidationFailed < StandardError
|
|
88
|
+
def info; { code: 400, msg: "#{Config.prefix}".concat(message), http_status: :bad_request }; end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'active_support/all'
|
|
2
|
+
|
|
3
|
+
module ParamsProcessor
|
|
4
|
+
module Config
|
|
5
|
+
cattr_accessor :actions do
|
|
6
|
+
nil
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
cattr_accessor :strict_check do
|
|
10
|
+
false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
cattr_accessor :prefix do
|
|
14
|
+
'parameter'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
cattr_accessor :production_msg do
|
|
18
|
+
# 'validation failed'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
cattr_accessor :not_passed do
|
|
22
|
+
'is required'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
cattr_accessor :is_blank do
|
|
26
|
+
'should not be blank'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
cattr_accessor :wrong_type do
|
|
30
|
+
'must be'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
cattr_accessor :wrong_size do
|
|
34
|
+
'length must be(in)'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
cattr_accessor :is_not_entity do
|
|
38
|
+
'must be'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
cattr_accessor :not_in_allowable_values do
|
|
42
|
+
'must in'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
cattr_accessor :not_match_pattern do
|
|
46
|
+
'must match'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
cattr_accessor :out_of_range do
|
|
50
|
+
'is out of range'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
cattr_accessor :wrong_combined_type do
|
|
54
|
+
'must be'
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
cattr_accessor :test do
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
|
2
|
+
|
|
3
|
+
module ParamsProcessor
|
|
4
|
+
class DocConverter < HashWithIndifferentAccess
|
|
5
|
+
cattr_accessor :docs
|
|
6
|
+
|
|
7
|
+
def initialize(inhert_hash = { })
|
|
8
|
+
super(inhert_hash)
|
|
9
|
+
convert
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def fill_with_ref(who, ref_to)
|
|
13
|
+
return unless who.key? '$ref'
|
|
14
|
+
ref_name = who.delete('$ref').split('/').last
|
|
15
|
+
who.merge! @api_components.fetch(ref_to).fetch(ref_name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# TODO: refactor
|
|
19
|
+
def convert
|
|
20
|
+
return if blank?
|
|
21
|
+
self.each do |_api, api_doc|
|
|
22
|
+
@api_components = api_doc[:components]
|
|
23
|
+
api_doc[:paths].each do |_path, path_doc|
|
|
24
|
+
path_doc.each do |_method, action_doc|
|
|
25
|
+
# 将 Reference Obj 填充进来
|
|
26
|
+
# body ref
|
|
27
|
+
request_body = action_doc[:requestBody]
|
|
28
|
+
fill_with_ref request_body, :requestBodies if request_body.present?
|
|
29
|
+
request_body&.[](:content)&.each do |_media, media_doc|
|
|
30
|
+
media_doc.each do |mtype, mtype_doc|
|
|
31
|
+
fill_with_ref mtype_doc, :schemas if mtype == 'schema'
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# 将 form-data 提到 parameters,方便统一访问接口
|
|
36
|
+
# 在 param ref 处理之前上提,使后续可以一并将 properties 中的 schma 进行处理
|
|
37
|
+
form = action_doc[:requestBody]&.[](:content)&.[]('multipart/form-data')
|
|
38
|
+
if form.present?
|
|
39
|
+
required = form[:schema][:required] || [ ]
|
|
40
|
+
permit = form[:schema][:permit] ? true : false
|
|
41
|
+
form[:schema][:properties]&.each do |name, prop_schema|
|
|
42
|
+
(action_doc[:parameters] ||= [ ]) << {
|
|
43
|
+
'name' => name,
|
|
44
|
+
'in' => 'form',
|
|
45
|
+
'required' => required.include?(name),
|
|
46
|
+
'schema' => prop_schema.reverse_merge!(permit: permit),
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# param ref
|
|
53
|
+
action_doc[:parameters]&.each do |param|
|
|
54
|
+
fill_with_ref param, :parameters
|
|
55
|
+
|
|
56
|
+
# schema ref
|
|
57
|
+
# TODO: Support nested scanning
|
|
58
|
+
fill_with_ref param[:schema], :schemas if param[:schema].present?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# response ref
|
|
63
|
+
action_doc[:responses]&.each do |_resp, resp_doc|
|
|
64
|
+
fill_with_ref resp_doc, :responses
|
|
65
|
+
resp_doc&.[](:content)&.each do |_media, media_doc|
|
|
66
|
+
media_doc.each do |_mtype, mtype_doc|
|
|
67
|
+
fill_with_ref mtype_doc, :schemas
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
|
2
|
+
|
|
3
|
+
module ParamsProcessor
|
|
4
|
+
class ParamDoc < HashWithIndifferentAccess
|
|
5
|
+
# Interfaces for directly taking the processed info what we focus on.
|
|
6
|
+
def range
|
|
7
|
+
return if (schema.keys & %w[ minimum maximum ]).blank?
|
|
8
|
+
{
|
|
9
|
+
min: schema[:minimum] || -Float::INFINITY,
|
|
10
|
+
max: schema[:maximum] || Float::INFINITY,
|
|
11
|
+
should_neq_min?: schema[:exclusiveMinimum] || false,
|
|
12
|
+
should_neq_max?: schema[:exclusiveMaximum] || false
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def size
|
|
17
|
+
return if (schema.keys & %w[ minItems maxItems minLength maxLength ]).blank?
|
|
18
|
+
size = if type.in? %w[ array object ]
|
|
19
|
+
[schema[:minItems], schema[:maxItems]]
|
|
20
|
+
else
|
|
21
|
+
[schema[:minLength], schema[:maxLength]]
|
|
22
|
+
end
|
|
23
|
+
{ min: size[0] || 0, max: size[1] || Float::INFINITY }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def combined
|
|
27
|
+
{ all_of: all_of, one_of: one_of, any_of: any_of, not: not_be }.keep_if { |_k, v| !v.nil? }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def combined?; combined.present? end
|
|
31
|
+
|
|
32
|
+
def combined_modes; combined.keys end
|
|
33
|
+
|
|
34
|
+
def real_name; as || name end
|
|
35
|
+
|
|
36
|
+
def doced_permit?; permit? || not_permit? end
|
|
37
|
+
|
|
38
|
+
{ # INTERFACE_MAPPING
|
|
39
|
+
name: %i[ name ],
|
|
40
|
+
required: %i[ required ],
|
|
41
|
+
in: %i[ in ],
|
|
42
|
+
schema: %i[ schema ],
|
|
43
|
+
enum: %i[ schema enum ],
|
|
44
|
+
pattern: %i[ schema pattern ],
|
|
45
|
+
regexp: %i[ schema pattern ],
|
|
46
|
+
type: %i[ schema type ],
|
|
47
|
+
format: %i[ schema format ],
|
|
48
|
+
all_of: %i[ schema allOf ],
|
|
49
|
+
one_of: %i[ schema oneOf ],
|
|
50
|
+
any_of: %i[ schema anyOf ],
|
|
51
|
+
not_be: %i[ schema not ],
|
|
52
|
+
is: %i[ schema is ],
|
|
53
|
+
dft: %i[ schema default ],
|
|
54
|
+
as: %i[ schema as ],
|
|
55
|
+
items: %i[ schema items ],
|
|
56
|
+
props: %i[ schema properties ],
|
|
57
|
+
blankable: %i[ schema blankable ],
|
|
58
|
+
permit?: %i[ schema permit ],
|
|
59
|
+
not_permit?: %i[ schema not_permit ],
|
|
60
|
+
}.each do |method, path|
|
|
61
|
+
define_method method do self.dig(*path) end
|
|
62
|
+
end
|
|
63
|
+
alias required? required
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module ParamsProcessor
|
|
2
|
+
class TypeConvert
|
|
3
|
+
class << self
|
|
4
|
+
def call(input, based_on:)
|
|
5
|
+
@input = input
|
|
6
|
+
@doc = based_on
|
|
7
|
+
convert
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# TODO: 循环和递归转换
|
|
11
|
+
def convert
|
|
12
|
+
send(@doc.type || @doc.combined_modes.first) # TODO
|
|
13
|
+
rescue NoMethodError
|
|
14
|
+
@input
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# int32 / int64
|
|
18
|
+
def integer
|
|
19
|
+
@input.to_i
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# float / double
|
|
23
|
+
def number
|
|
24
|
+
@input.to_f
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def boolean
|
|
28
|
+
@input.to_s.in?(%w[ true 1 ]) ? true : false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# date / date-time / base64
|
|
32
|
+
def string
|
|
33
|
+
case @doc.format
|
|
34
|
+
when 'date' then parse_time(Date)
|
|
35
|
+
when 'date-time' then parse_time(DateTime)
|
|
36
|
+
when 'base64' then @input # Base64.strict_decode64(@input)
|
|
37
|
+
else @input.to_s
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def array
|
|
42
|
+
@input
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def object
|
|
46
|
+
@input
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# combined TODO
|
|
50
|
+
|
|
51
|
+
def all_of
|
|
52
|
+
doc = ParamDoc.new name: @doc.name, schema: @doc.all_of.reduce({}, :merge)
|
|
53
|
+
TypeConvert.(@input, based_on: doc)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def one_of
|
|
57
|
+
doc = ParamDoc.new name: @doc.name, schema: @doc.all_of.reduce({}, :merge)
|
|
58
|
+
TypeConvert.(@input, based_on: doc)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def any_of
|
|
62
|
+
doc = ParamDoc.new name: @doc.name, schema: @doc.all_of.reduce({}, :merge)
|
|
63
|
+
TypeConvert.(@input, based_on: doc)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def not
|
|
67
|
+
@input
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# helpers
|
|
71
|
+
|
|
72
|
+
def parse_time(cls)
|
|
73
|
+
if @doc.pattern
|
|
74
|
+
cls.send(:strptime, @input, @doc.pattern)
|
|
75
|
+
else
|
|
76
|
+
cls.send(:parse, @input)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
require 'multi_json'
|
|
2
|
+
require 'params_processor/config'
|
|
3
|
+
require 'params_processor/doc_converter'
|
|
4
|
+
require 'params_processor/param_doc'
|
|
5
|
+
|
|
6
|
+
module ParamsProcessor
|
|
7
|
+
class Validate
|
|
8
|
+
class << self
|
|
9
|
+
def call(input, based_on:, raise: nil)
|
|
10
|
+
@error_class = raise
|
|
11
|
+
input(input).check! based_on
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def input(input)
|
|
15
|
+
@input = input
|
|
16
|
+
@str_input = input.to_s
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def check!(param_doc)
|
|
21
|
+
@doc = param_doc
|
|
22
|
+
check (if_is_passed do
|
|
23
|
+
check if_is_present
|
|
24
|
+
break if @input.nil?# || (@doc.blankable != false && @input.blank? && @input != false)
|
|
25
|
+
check_combined_types if @doc.combined?
|
|
26
|
+
check type if @doc.type
|
|
27
|
+
check size if @doc.size
|
|
28
|
+
check if_is_entity if @doc.is
|
|
29
|
+
check if_in_allowable_values if @doc.enum
|
|
30
|
+
check if_match_pattern if @doc.pattern
|
|
31
|
+
check if_is_in_range if @doc.range
|
|
32
|
+
check_each_element if @doc.type == 'array'
|
|
33
|
+
check_each_pair if @doc.type == 'object'
|
|
34
|
+
end)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def if_is_passed(&block)
|
|
38
|
+
return [:not_passed, ''] if @doc.required && @input.nil?
|
|
39
|
+
self.instance_eval(&block) unless @input.nil?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def if_is_present
|
|
43
|
+
[:is_blank, ''] if @doc.blankable == false && @input.blank? && @input != false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# TODO: combined type
|
|
47
|
+
def type
|
|
48
|
+
case @doc.type
|
|
49
|
+
when 'integer' then @str_input.match?(/^-?\d*$/)
|
|
50
|
+
when 'boolean' then @str_input.in? %w[ true 1 false 0 ]
|
|
51
|
+
when 'array' then @input.is_a? Array
|
|
52
|
+
when 'object' then @input.is_a?(ActionController::Parameters) || @input.is_a?(Hash)
|
|
53
|
+
when 'number' then _number_type
|
|
54
|
+
when 'string' then _string_type
|
|
55
|
+
else true # TODO
|
|
56
|
+
end or [:wrong_type, @doc.format.to_s]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def _number_type
|
|
60
|
+
case @doc.format
|
|
61
|
+
when 'float' then @str_input.match?(/^[-+]?\d*\.?\d+$/)
|
|
62
|
+
when 'double' then @str_input.match?(/^[-+]?\d*\.?\d+$/)
|
|
63
|
+
else true # TODO
|
|
64
|
+
end or [:wrong_type, @doc.format.to_s]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def _string_type
|
|
68
|
+
# return false unless @input.is_a? String
|
|
69
|
+
case @doc.format
|
|
70
|
+
when 'date' then parse_time!(Date)
|
|
71
|
+
when 'date-time' then parse_time!(DateTime)
|
|
72
|
+
when 'base64' then Base64.strict_decode64(@input)
|
|
73
|
+
when 'json' then MultiJson.load(@input)
|
|
74
|
+
else Config.strict_check ? @input.is_a?(String) : true
|
|
75
|
+
end
|
|
76
|
+
rescue ArgumentError, MultiJson::ParseError
|
|
77
|
+
false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def size
|
|
81
|
+
if @doc.type.in? %w[ array object ]
|
|
82
|
+
@input.size >= @doc.size[:min] && @input.size <= @doc.size[:max]
|
|
83
|
+
else
|
|
84
|
+
@str_input.length >= @doc.size[:min] && @str_input.length <= @doc.size[:max]
|
|
85
|
+
end or [:wrong_size, @doc.size.values.join('..')]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def if_is_entity
|
|
89
|
+
case @doc.is
|
|
90
|
+
when 'email'; @str_input.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]{2,}\z/i)
|
|
91
|
+
else true # TODO
|
|
92
|
+
end or [:is_not_entity, @doc.is.to_s]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def if_in_allowable_values
|
|
96
|
+
case @doc.type
|
|
97
|
+
when 'integer' then @doc.enum.include?(@input.to_i)
|
|
98
|
+
else @doc.enum.map(&:to_s).include?(@str_input)
|
|
99
|
+
end or [:not_in_allowable_values, @doc.enum.to_s.delete('\"')]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def if_match_pattern
|
|
103
|
+
unless @str_input.match?(Regexp.new(@doc.pattern))
|
|
104
|
+
[:not_match_pattern, "/#{@doc.pattern}/"]
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def if_is_in_range
|
|
109
|
+
rg = @doc.range
|
|
110
|
+
fmt = @doc.format.tr('-', '_').camelize.constantize if @doc.format&.match?('date')
|
|
111
|
+
min = fmt ? parse_time!(fmt, rg[:min] || '1-1-1') : rg[:min]
|
|
112
|
+
max = fmt ? parse_time!(fmt, rg[:max] || '9999-12-31') : rg[:max]
|
|
113
|
+
@input = fmt ? parse_time!(fmt, @input) : @input.to_f
|
|
114
|
+
|
|
115
|
+
left_op = rg[:should_neq_min?] ? :< : :<=
|
|
116
|
+
right_op = rg[:should_neq_max?] ? :< : :<=
|
|
117
|
+
is_in_range = min&.send(left_op, @input)
|
|
118
|
+
is_in_range &= @input.send(right_op, max)
|
|
119
|
+
[:out_of_range, "#{min} #{left_op} x #{right_op} #{max}"] unless is_in_range
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def check_combined_types
|
|
123
|
+
@doc.combined.each do |mode, schemas|
|
|
124
|
+
results = check_each_schema(schemas)
|
|
125
|
+
|
|
126
|
+
case mode
|
|
127
|
+
when :all_of then results.all?(&:present?)
|
|
128
|
+
when :one_of then results.count(true) == 1
|
|
129
|
+
when :any_of then results.include?(true)
|
|
130
|
+
when :not then results.all?(&:blank?)
|
|
131
|
+
end or raise ValidationFailed, " `#{@doc.name.to_sym}` #{Config.wrong_combined_type} #{mode} the specified schemas."
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def check_each_schema(schemas)
|
|
136
|
+
results = [ ]
|
|
137
|
+
strict_check = Config.strict_check.clone
|
|
138
|
+
_doc, Config.strict_check = @doc, true
|
|
139
|
+
schemas.each do |schema|
|
|
140
|
+
doc = ParamDoc.new name: @doc.name, schema: schema
|
|
141
|
+
begin
|
|
142
|
+
Validate.(@input, based_on: doc, raise: @error_class)
|
|
143
|
+
results << true
|
|
144
|
+
rescue ValidationFailed
|
|
145
|
+
results << false
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
@doc, Config.strict_check = _doc, strict_check
|
|
149
|
+
results
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def check_each_element
|
|
153
|
+
return if @doc.items.blank?
|
|
154
|
+
|
|
155
|
+
_doc, _input = @doc, @input
|
|
156
|
+
items_doc = ParamDoc.new name: @doc.name, schema: @doc.items
|
|
157
|
+
@input.each do |input|
|
|
158
|
+
Validate.(input, based_on: items_doc, raise: @error_class)
|
|
159
|
+
end
|
|
160
|
+
@doc, @input = _doc, _input
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def check_each_pair
|
|
164
|
+
return if @doc.props.blank?
|
|
165
|
+
|
|
166
|
+
_doc = @doc
|
|
167
|
+
required = (@doc[:schema][:required] || [ ]).map(&:to_s)
|
|
168
|
+
@doc.props.each do |name, schema|
|
|
169
|
+
prop_doc = ParamDoc.new name: name, required: required.include?(name), schema: schema
|
|
170
|
+
_input = @input
|
|
171
|
+
Validate.(@input[name] || @input[name.to_sym], based_on: prop_doc, raise: @error_class)
|
|
172
|
+
@input = _input
|
|
173
|
+
end
|
|
174
|
+
@doc = _doc
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def check msg
|
|
178
|
+
return unless msg.is_a? Array
|
|
179
|
+
@error_class.send("#{@doc.name}_#{msg.first}!") if @error_class.respond_to? "#{@doc.name}_#{msg.first}!"
|
|
180
|
+
@error_class.send("#{msg.first}!") if @error_class.respond_to? msg.first
|
|
181
|
+
raise ValidationFailed, Config.production_msg if Config.production_msg.present?
|
|
182
|
+
|
|
183
|
+
test_msg = Config.send(msg.first) if Config.test
|
|
184
|
+
msg = "#{Config.send(msg.first)}#{' ' + msg.last if msg.last.present?}"
|
|
185
|
+
msg = " `#{@doc.name.to_sym}` " << msg
|
|
186
|
+
raise ValidationFailed, test_msg || msg
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def parse_time!(cls, value = nil)
|
|
190
|
+
if @doc.pattern
|
|
191
|
+
cls.send(:strptime, value || @input, @doc.pattern)
|
|
192
|
+
else
|
|
193
|
+
cls.send(:parse, value || @input)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'params_processor/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'zero-params_processor'
|
|
8
|
+
spec.version = ParamsProcessor::VERSION
|
|
9
|
+
spec.authors = ['zhandao']
|
|
10
|
+
spec.email = ['x@skippingcat.com']
|
|
11
|
+
|
|
12
|
+
spec.summary = 'Process parameters base on OpenApi3 JSON documentation'
|
|
13
|
+
spec.description = 'Process parameters base on OpenApi3 JSON documentation, such as: ' \
|
|
14
|
+
'validation and type conversion'
|
|
15
|
+
spec.homepage = 'https://github.com/zhandao/zero-params_processor'
|
|
16
|
+
spec.license = 'MIT'
|
|
17
|
+
|
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
|
20
|
+
end
|
|
21
|
+
spec.bindir = 'exe'
|
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
23
|
+
spec.require_paths = ['lib']
|
|
24
|
+
|
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.16.a'
|
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
28
|
+
spec.add_development_dependency 'simplecov'
|
|
29
|
+
|
|
30
|
+
spec.add_runtime_dependency 'zero-rails_openapi', '>= 1.5.2'
|
|
31
|
+
spec.add_runtime_dependency 'rails', '>= 3'
|
|
32
|
+
spec.add_runtime_dependency 'activesupport', '>= 3'
|
|
33
|
+
spec.add_runtime_dependency 'multi_json'
|
|
34
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: zero-params_processor
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.4.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- zhandao
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-08-18 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 1.16.a
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 1.16.a
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '10.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '10.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '3.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: simplecov
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: zero-rails_openapi
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 1.5.2
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 1.5.2
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rails
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '3'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '3'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: activesupport
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '3'
|
|
104
|
+
type: :runtime
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '3'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: multi_json
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :runtime
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
description: 'Process parameters base on OpenApi3 JSON documentation, such as: validation
|
|
126
|
+
and type conversion'
|
|
127
|
+
email:
|
|
128
|
+
- x@skippingcat.com
|
|
129
|
+
executables: []
|
|
130
|
+
extensions: []
|
|
131
|
+
extra_rdoc_files: []
|
|
132
|
+
files:
|
|
133
|
+
- ".gitignore"
|
|
134
|
+
- ".rspec"
|
|
135
|
+
- ".rubocop.yml"
|
|
136
|
+
- ".travis.yml"
|
|
137
|
+
- Gemfile
|
|
138
|
+
- Gemfile.lock
|
|
139
|
+
- LICENSE.txt
|
|
140
|
+
- README.md
|
|
141
|
+
- Rakefile
|
|
142
|
+
- bin/console
|
|
143
|
+
- bin/setup
|
|
144
|
+
- lib/params_processor.rb
|
|
145
|
+
- lib/params_processor/config.rb
|
|
146
|
+
- lib/params_processor/doc_converter.rb
|
|
147
|
+
- lib/params_processor/param_doc.rb
|
|
148
|
+
- lib/params_processor/type_convert.rb
|
|
149
|
+
- lib/params_processor/validate.rb
|
|
150
|
+
- lib/params_processor/version.rb
|
|
151
|
+
- zero-params_processor.gemspec
|
|
152
|
+
homepage: https://github.com/zhandao/zero-params_processor
|
|
153
|
+
licenses:
|
|
154
|
+
- MIT
|
|
155
|
+
metadata: {}
|
|
156
|
+
post_install_message:
|
|
157
|
+
rdoc_options: []
|
|
158
|
+
require_paths:
|
|
159
|
+
- lib
|
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
161
|
+
requirements:
|
|
162
|
+
- - ">="
|
|
163
|
+
- !ruby/object:Gem::Version
|
|
164
|
+
version: '0'
|
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
|
+
requirements:
|
|
167
|
+
- - ">="
|
|
168
|
+
- !ruby/object:Gem::Version
|
|
169
|
+
version: '0'
|
|
170
|
+
requirements: []
|
|
171
|
+
rubyforge_project:
|
|
172
|
+
rubygems_version: 2.7.6
|
|
173
|
+
signing_key:
|
|
174
|
+
specification_version: 4
|
|
175
|
+
summary: Process parameters base on OpenApi3 JSON documentation
|
|
176
|
+
test_files: []
|