swalidate 0.1.0 → 0.1.1
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/.rubocop.yml +7 -0
- data/.travis.yml +2 -0
- data/README.md +8 -1
- data/Rakefile +6 -3
- data/bin/console +3 -3
- data/lib/swalidate/endpoint.rb +13 -0
- data/lib/swalidate/middleware.rb +51 -0
- data/lib/swalidate/schema.rb +17 -0
- data/lib/swalidate/types/main.rb +94 -0
- data/lib/swalidate/validator/main.rb +31 -0
- data/lib/swalidate/validator/parameter.rb +43 -0
- data/lib/swalidate/version.rb +1 -1
- data/lib/swalidate.rb +10 -1
- data/swalidate-0.1.0.gem +0 -0
- data/swalidate.gemspec +17 -12
- metadata +66 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3459b3c0c2f919ba4c550703f8398277a7b3869
|
4
|
+
data.tar.gz: 1d4bc1e86843aaf3f69874f97df329c4519b0785
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd7b44bf8a7ad42527403122cb12cbe38c9340a433e5ba92efbad604b606e38d458b89e11f5fa243e014c83a04a43fa18c3b221f8c663800e294df7777333134
|
7
|
+
data.tar.gz: aa91c4ad4bf33c4c220c000202f774c23fe6c46a3919954e6fd0cdc6d933551a34b2002a13d0e2f5d594378f502ff4060beeef06347c366165c2e2d9d0e84bc1
|
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -22,7 +22,14 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
|
25
|
+
```ruby
|
26
|
+
swalidate_config = {
|
27
|
+
file_path: 'path.yaml', # swagger config file. example of app root
|
28
|
+
subdomain: 'api', # validation applies only to this subdomain
|
29
|
+
path_prefix: '/v1' # validation applies only to the paths starting with path_prefix
|
30
|
+
}
|
31
|
+
config.middleware.insert_after ActionDispatch::ParamsParser, Swalidate::Middleware, swalidate_config
|
32
|
+
```
|
26
33
|
|
27
34
|
## Development
|
28
35
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
3
|
|
4
4
|
RSpec::Core::RakeTask.new(:spec)
|
5
5
|
|
6
|
-
|
6
|
+
require 'rubocop/rake_task'
|
7
|
+
RuboCop::RakeTask.new
|
8
|
+
|
9
|
+
task default: :spec
|
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'swalidate'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +10,5 @@ require "swalidate"
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start(__FILE__)
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Swalidate
|
2
|
+
class Middleware
|
3
|
+
def initialize(app, options = {})
|
4
|
+
@app = app
|
5
|
+
@schema = Swalidate::Schema.new(options[:file_path])
|
6
|
+
# TODO: puts a log message when not found and dont try to validate
|
7
|
+
|
8
|
+
@subdomain = options[:subdomain]
|
9
|
+
@path_prefix = options[:path_prefix]
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
@request = ActionDispatch::Request.new(env)
|
14
|
+
@endpoint = @schema.endpoint(@request.method, @request.path.gsub(@path_prefix.to_s, '').sub(/\.[^.]+\z/, ''))
|
15
|
+
|
16
|
+
if should_validate?
|
17
|
+
validator = Swalidate::Validator::Main.new(@endpoint, @request.params)
|
18
|
+
validator.call
|
19
|
+
return error_response(validator.errors) unless validator.valid?
|
20
|
+
end
|
21
|
+
|
22
|
+
@app.call(env)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def error_response(errors)
|
28
|
+
[
|
29
|
+
400,
|
30
|
+
{ 'Content-Type' => 'application/vnd.api+json' },
|
31
|
+
[{ errors: errors }.to_json]
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
def should_validate?
|
36
|
+
valid_subdomain? && valid_path_prefix? && valid_path?
|
37
|
+
end
|
38
|
+
|
39
|
+
def valid_subdomain?
|
40
|
+
@subdomain.to_s == @request.subdomain.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid_path_prefix?
|
44
|
+
@request.path =~ Regexp.new(@path_prefix.to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
def valid_path?
|
48
|
+
@endpoint.should_validate?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Swalidate
|
2
|
+
class Schema
|
3
|
+
attr_reader :paths, :definitions
|
4
|
+
|
5
|
+
def initialize(file_path)
|
6
|
+
swagger = Swagger.load(file_path)
|
7
|
+
# TODO: error handling for files
|
8
|
+
|
9
|
+
@paths = swagger.paths
|
10
|
+
@definitions = swagger.definitions
|
11
|
+
end
|
12
|
+
|
13
|
+
def endpoint(method, path)
|
14
|
+
Swalidate::Endpoint.new(paths[path] && paths[path][method.downcase])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types
|
2
|
+
# https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#fixed-fields-8
|
3
|
+
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
module Swalidate
|
7
|
+
module Types
|
8
|
+
class Main
|
9
|
+
attr_reader :param, :casted_value, :errors
|
10
|
+
|
11
|
+
def initialize(param, value)
|
12
|
+
@param = param
|
13
|
+
@errors = []
|
14
|
+
@casted_value = cast_value(value)
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid?
|
18
|
+
return true if casted_value == 'NilObject.new'
|
19
|
+
|
20
|
+
# TODO:
|
21
|
+
# errors << {
|
22
|
+
# parameter: param['name'],
|
23
|
+
# message: "'#{param['name']}' can be minimum #{param['minimum']}"
|
24
|
+
# }
|
25
|
+
|
26
|
+
case casted_value
|
27
|
+
when Numeric
|
28
|
+
if param['minimum'] && casted_value < param['minimum']
|
29
|
+
@errors << {
|
30
|
+
'message' => "'#{param['name']}' can be minimum #{param['minimum']}"
|
31
|
+
}
|
32
|
+
end
|
33
|
+
if param['maximum'] && casted_value > param['maximum']
|
34
|
+
@errors << {
|
35
|
+
'message' => "'#{param['name']}' can be maximum #{param['maximum']}"
|
36
|
+
}
|
37
|
+
end
|
38
|
+
when String
|
39
|
+
if param['minLength'] && casted_value.length < param['minLength']
|
40
|
+
@errors << {
|
41
|
+
'message' => "Length of '#{param['name']}' can be minimum #{param['minLength']}"
|
42
|
+
}
|
43
|
+
end
|
44
|
+
if param['maxLength'] && casted_value.length > param['maxLength']
|
45
|
+
@errors << {
|
46
|
+
'message' => "Length of '#{param['name']}' can be maximum #{param['maxLength']}"
|
47
|
+
}
|
48
|
+
end
|
49
|
+
if param['pattern'] && !(casted_value =~ Regexp.new(param['pattern']))
|
50
|
+
@errors << {
|
51
|
+
'message' => "'#{param['name']}' is not fit to pattern #{param['pattern']}"
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
errors.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def cast_value(value)
|
62
|
+
return 'NilObject.new' unless value # TODO: create a NilObject class
|
63
|
+
|
64
|
+
begin
|
65
|
+
case param['type']
|
66
|
+
when 'integer' then Integer(value)
|
67
|
+
when 'number' then Float(value)
|
68
|
+
when 'string'
|
69
|
+
case param['format']
|
70
|
+
# TODO: date parsing: https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14
|
71
|
+
# 2015-02-01T00:00:00+00:00
|
72
|
+
# Date.rfc3339(value)
|
73
|
+
# DateTime.rfc3339(value)
|
74
|
+
when 'date' then Date.parse(value)
|
75
|
+
when 'date-time' then DateTime.parse(value)
|
76
|
+
else String(value)
|
77
|
+
end
|
78
|
+
when 'boolean' then %w(true false).include?(value.to_s) ? value : raise(ArgumentError)
|
79
|
+
else fail 'type is not valid' # TODO: handle this
|
80
|
+
end
|
81
|
+
rescue ArgumentError, TypeError => error
|
82
|
+
@errors << {
|
83
|
+
'message' => "'#{value}' is not a valid #{param['type']}"
|
84
|
+
}
|
85
|
+
nil
|
86
|
+
rescue StandardError => error
|
87
|
+
fail error
|
88
|
+
nil
|
89
|
+
# TODO: handle
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Swalidate
|
2
|
+
module Validator
|
3
|
+
class Main
|
4
|
+
attr_reader :endpoint_params, :request_params, :errors
|
5
|
+
|
6
|
+
def initialize(endpoint, request_params)
|
7
|
+
@endpoint_params = endpoint.parameters
|
8
|
+
@request_params = request_params
|
9
|
+
@errors = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
# TODO: allow extra parameters?
|
14
|
+
endpoint_params.each do |param|
|
15
|
+
parameter = Swalidate::Validator::Parameter.new(param, request_params[param['name']])
|
16
|
+
if parameter.invalid?
|
17
|
+
@errors += parameter.errors
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid?
|
23
|
+
errors.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def invalid?
|
27
|
+
!valid?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Swalidate
|
2
|
+
module Validator
|
3
|
+
class Parameter
|
4
|
+
attr_reader :param, :value, :type
|
5
|
+
|
6
|
+
def initialize(param, value)
|
7
|
+
@param = param
|
8
|
+
@value = value
|
9
|
+
@type = Swalidate::Types::Main.new(param, value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid?
|
13
|
+
return false if required? && !exists?
|
14
|
+
|
15
|
+
if exists?
|
16
|
+
type.valid?
|
17
|
+
else
|
18
|
+
!required?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def errors
|
23
|
+
if exists?
|
24
|
+
type.errors
|
25
|
+
else
|
26
|
+
required? ? ["'#{param['name']}' can't be blank."] : []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def invalid?
|
31
|
+
!valid?
|
32
|
+
end
|
33
|
+
|
34
|
+
def required?
|
35
|
+
param['required']
|
36
|
+
end
|
37
|
+
|
38
|
+
def exists?
|
39
|
+
!!value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/swalidate/version.rb
CHANGED
data/lib/swalidate.rb
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
require
|
1
|
+
require 'forwardable'
|
2
|
+
require 'swagger'
|
3
|
+
require 'action_dispatch'
|
4
|
+
require 'swalidate/version'
|
5
|
+
require 'swalidate/schema'
|
6
|
+
require 'swalidate/endpoint'
|
7
|
+
require 'swalidate/types/main'
|
8
|
+
require 'swalidate/validator/parameter'
|
9
|
+
require 'swalidate/validator/main'
|
10
|
+
require 'swalidate/middleware'
|
2
11
|
|
3
12
|
module Swalidate
|
4
13
|
# Your code goes here...
|
data/swalidate-0.1.0.gem
ADDED
Binary file
|
data/swalidate.gemspec
CHANGED
@@ -4,15 +4,15 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'swalidate/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'swalidate'
|
8
8
|
spec.version = Swalidate::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Furkan Ayhan']
|
10
|
+
spec.email = ['furkanayhn@gmail.com']
|
11
11
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
12
|
+
spec.summary = 'Rack validation middleware using Swagger'
|
13
|
+
spec.description = 'Rack validation middleware using Swagger'
|
14
|
+
spec.homepage = 'https://github.com/furkanayhan/swalidate'
|
15
|
+
spec.license = 'MIT'
|
16
16
|
|
17
17
|
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
18
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
@@ -26,11 +26,16 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
27
|
f.match(%r{^(test|spec|features)/})
|
28
28
|
end
|
29
|
-
spec.bindir =
|
29
|
+
spec.bindir = 'exe'
|
30
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
-
spec.require_paths = [
|
31
|
+
spec.require_paths = ['lib']
|
32
32
|
|
33
|
-
spec.
|
34
|
-
spec.
|
35
|
-
|
33
|
+
spec.add_dependency 'actionpack'
|
34
|
+
spec.add_dependency 'swagger-core'
|
35
|
+
|
36
|
+
spec.add_development_dependency 'bundler', '~> 1.14'
|
37
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
38
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
39
|
+
spec.add_development_dependency 'rubocop'
|
40
|
+
spec.add_development_dependency 'pry'
|
36
41
|
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: swalidate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Furkan Ayhan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionpack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: swagger-core
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +80,34 @@ dependencies:
|
|
52
80
|
- - "~>"
|
53
81
|
- !ruby/object:Gem::Version
|
54
82
|
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
55
111
|
description: Rack validation middleware using Swagger
|
56
112
|
email:
|
57
113
|
- furkanayhn@gmail.com
|
@@ -61,6 +117,7 @@ extra_rdoc_files: []
|
|
61
117
|
files:
|
62
118
|
- ".gitignore"
|
63
119
|
- ".rspec"
|
120
|
+
- ".rubocop.yml"
|
64
121
|
- ".travis.yml"
|
65
122
|
- Gemfile
|
66
123
|
- LICENSE.txt
|
@@ -69,7 +126,14 @@ files:
|
|
69
126
|
- bin/console
|
70
127
|
- bin/setup
|
71
128
|
- lib/swalidate.rb
|
129
|
+
- lib/swalidate/endpoint.rb
|
130
|
+
- lib/swalidate/middleware.rb
|
131
|
+
- lib/swalidate/schema.rb
|
132
|
+
- lib/swalidate/types/main.rb
|
133
|
+
- lib/swalidate/validator/main.rb
|
134
|
+
- lib/swalidate/validator/parameter.rb
|
72
135
|
- lib/swalidate/version.rb
|
136
|
+
- swalidate-0.1.0.gem
|
73
137
|
- swalidate.gemspec
|
74
138
|
homepage: https://github.com/furkanayhan/swalidate
|
75
139
|
licenses:
|