esplanade 0.1.0 → 1.0.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 +4 -4
- data/.rubocop.yml +10 -1
- data/.ruby-version +1 -1
- data/.travis.yml +4 -0
- data/README.md +72 -37
- data/esplanade.gemspec +2 -1
- data/lib/esplanade.rb +0 -8
- data/lib/esplanade/configuration.rb +4 -7
- data/lib/esplanade/error.rb +3 -0
- data/lib/esplanade/middleware.rb +8 -21
- data/lib/esplanade/request.rb +16 -18
- data/lib/esplanade/request/doc.rb +43 -0
- data/lib/esplanade/request/raw.rb +23 -0
- data/lib/esplanade/request/raw/body.rb +34 -0
- data/lib/esplanade/request/validation.rb +29 -0
- data/lib/esplanade/response.rb +17 -23
- data/lib/esplanade/response/doc.rb +36 -0
- data/lib/esplanade/response/raw.rb +21 -0
- data/lib/esplanade/response/raw/body.rb +39 -0
- data/lib/esplanade/response/validation.rb +52 -0
- data/lib/esplanade/version.rb +1 -1
- metadata +18 -13
- data/lib/esplanade/railtie.rb +0 -7
- data/lib/esplanade/request/body.rb +0 -14
- data/lib/esplanade/request/error.rb +0 -19
- data/lib/esplanade/response/body.rb +0 -32
- data/lib/esplanade/response/error.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 136faea6ccc9fb08600dfb9c8d597172bb72e270
|
4
|
+
data.tar.gz: 92d86b6dcf3cc3d8d7ad3622bfb99b0c45aabf07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1dfbc8ba44102440c149a1d84be28151fe3ed5a918b7d43233ee58a6cb9d7c2713219571f9555d6e5b577184673827b1e7b109299ab5104cb4abefbd997cd754
|
7
|
+
data.tar.gz: ee811221aa1764c746afead9aa85365b035dcc1eecf5f768a27dd503f9acc9c200bccded11c7d7c168a7e482d5aaedbf15ee74ea16273151622565a51b46ae78
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
AllCops:
|
2
|
-
TargetRubyVersion: 2.
|
2
|
+
TargetRubyVersion: 2.2
|
3
3
|
|
4
4
|
Metrics/LineLength:
|
5
5
|
Max: 120
|
@@ -9,3 +9,12 @@ Style/Documentation:
|
|
9
9
|
|
10
10
|
Style/FrozenStringLiteralComment:
|
11
11
|
Enabled: false
|
12
|
+
|
13
|
+
Metrics/BlockLength:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Style/IfUnlessModifier:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Style/RescueModifier:
|
20
|
+
Enabled: false
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,21 +1,11 @@
|
|
1
1
|
# Esplanade
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/funbox/esplanade)
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
An example of an error body
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
{
|
13
|
-
"error": [
|
14
|
-
"The property '#/' did not contain a required property of 'login' in schema deee53ed-f917-5f2f-bccf-0b8af3b749c7",
|
15
|
-
"The property '#/' did not contain a required property of 'password' in schema deee53ed-f917-5f2f-bccf-0b8af3b749c7"
|
16
|
-
]
|
17
|
-
}
|
18
|
-
```
|
5
|
+
This gem will help you validation and sinhronize your API in strict accordance to the documentation in
|
6
|
+
[API Blueprint](https://apiblueprint.org/) format.
|
7
|
+
To do this it automatically searches received requestes and responses in the documentation and run validates
|
8
|
+
json-schemas.
|
19
9
|
|
20
10
|
## Installation
|
21
11
|
|
@@ -35,48 +25,93 @@ Or install it yourself as:
|
|
35
25
|
|
36
26
|
## Usage
|
37
27
|
|
38
|
-
|
28
|
+
Example:
|
39
29
|
|
40
|
-
|
30
|
+
`middlewares/your_middleware.rb`
|
41
31
|
|
42
32
|
```ruby
|
43
|
-
|
44
|
-
|
33
|
+
class YourMiddleware < Esplanade::Middleware
|
34
|
+
def call(env)
|
35
|
+
request = Esplanade::Request.new(@documentation, env)
|
36
|
+
request.validation.valid!
|
37
|
+
|
38
|
+
status, headers, body = @app.call(env)
|
39
|
+
|
40
|
+
response = Esplanade::Response.new(request, status, body)
|
41
|
+
response.validation.valid!
|
45
42
|
|
46
|
-
|
43
|
+
[status, headers, body]
|
44
|
+
rescue Esplanade::Error => e
|
45
|
+
your_render_error(e)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
47
49
|
|
48
|
-
config/application.rb
|
50
|
+
`config/application.rb`
|
49
51
|
|
50
52
|
```ruby
|
51
53
|
require 'esplanade'
|
52
|
-
|
54
|
+
Esplanade.configure do |config|
|
55
|
+
config.apib_path = 'doc/backend.apib'
|
56
|
+
end
|
57
|
+
|
58
|
+
require_relative '../middlewares/your_middleware'
|
59
|
+
config.middleware.use YourMiddleware
|
53
60
|
```
|
54
61
|
|
55
|
-
##
|
62
|
+
## Esplanade::Error
|
56
63
|
|
57
|
-
|
64
|
+
From him the `Esplanade::Request::Error` and `Esplanade::Response::Error` are inherited.
|
58
65
|
|
59
|
-
|
66
|
+
### Esplanade::Request::Error
|
60
67
|
|
61
|
-
|
68
|
+
From him the `Esplanade::Request::NotDocumented`, `Esplanade::Request::BodyIsNotJson` and `Esplanade::Request::Invalid` are inherited.
|
62
69
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
70
|
+
#### Esplanade::Request::NotDocumented
|
71
|
+
|
72
|
+
Error message: `{:method=>"method", :path=>"path"}`.
|
73
|
+
|
74
|
+
#### Esplanade::Request::BodyIsNotJson
|
75
|
+
|
76
|
+
Only if the documentation for this request indicates that `Content-Type: application/json`.
|
77
|
+
|
78
|
+
Error message: `{:method=>"method", :path=>"path", :body=>"{\"state\": 1"}`.
|
79
|
+
|
80
|
+
#### Esplanade::Request::Invalid
|
81
|
+
|
82
|
+
Error message: `{:method=>"method", :path=>"path", :body=>"body", :error=>["error"]}`.
|
83
|
+
|
84
|
+
### Esplanade::Response::Error
|
85
|
+
|
86
|
+
From him the `Esplanade::Response::NotDocumented`, `Esplanade::Response::BodyIsNotJson` and `Esplanade::Response::Invalid` are inherited.
|
87
|
+
|
88
|
+
#### Esplanade::Response::NotDocumented
|
89
|
+
|
90
|
+
Error message: `{:request=>{:method=>"method", :path=>"path"}, :status=>"status"}`.
|
91
|
+
|
92
|
+
#### Esplanade::Response::BodyIsNotJson
|
93
|
+
|
94
|
+
Only if the documentation for all the responses of one request indicates that `Content-Type: application/json`.
|
95
|
+
|
96
|
+
Error message: `{:request=>{:method=>"method", :path=>"path"}, :status=>"status", :body=>"body"}`.
|
97
|
+
|
98
|
+
#### Esplanade::Response::Invalid
|
99
|
+
|
100
|
+
Error message: `{:request=>{:method=>"method", :path=>"path"}, :status=>"status", :body=>"body", :error=>["error"]}`.
|
101
|
+
|
102
|
+
## Config
|
68
103
|
|
69
|
-
###
|
104
|
+
### apib_path
|
70
105
|
|
71
|
-
|
106
|
+
Path to API Blueprint documentation. There must be an installed [drafter](https://github.com/apiaryio/drafter) to parse it.
|
72
107
|
|
73
|
-
###
|
108
|
+
### drafter_yaml_path
|
74
109
|
|
75
|
-
|
110
|
+
Path to API Blueprint documentation pre-parsed with `drafter` and saved to a YAML file.
|
76
111
|
|
77
|
-
###
|
112
|
+
### prefix
|
78
113
|
|
79
|
-
|
114
|
+
Prefix of API requests. Example: `'/api'`. The prefix is added to the requests in the documentation.
|
80
115
|
|
81
116
|
## License
|
82
117
|
|
data/esplanade.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'esplanade/version'
|
@@ -19,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
19
20
|
|
20
21
|
spec.add_runtime_dependency 'multi_json', '~> 1.11', '>= 1.11.1'
|
21
22
|
spec.add_runtime_dependency 'json-schema', '~> 2.6', '>= 2.6.2'
|
22
|
-
spec.add_runtime_dependency '
|
23
|
+
spec.add_runtime_dependency 'tomograph', '~> 1.1', '>= 1.1.0'
|
23
24
|
spec.add_development_dependency 'bundler', '~> 1.12'
|
24
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
25
26
|
spec.add_development_dependency 'byebug', '~> 8.2', '>= 8.2.1'
|
data/lib/esplanade.rb
CHANGED
@@ -1,13 +1,5 @@
|
|
1
1
|
require 'esplanade/middleware'
|
2
2
|
require 'esplanade/configuration'
|
3
|
-
require 'esplanade/request'
|
4
|
-
require 'esplanade/request/body'
|
5
|
-
require 'esplanade/request/error'
|
6
|
-
require 'esplanade/response'
|
7
|
-
require 'esplanade/response/body'
|
8
|
-
require 'esplanade/response/error'
|
9
|
-
require 'esplanade/railtie' if defined?(Rails)
|
10
|
-
require 'tomogram_routing'
|
11
3
|
|
12
4
|
module Esplanade
|
13
5
|
class << self
|
@@ -1,14 +1,11 @@
|
|
1
1
|
module Esplanade
|
2
2
|
class Configuration
|
3
|
-
attr_accessor :
|
4
|
-
:
|
5
|
-
:
|
6
|
-
:validation_response
|
3
|
+
attr_accessor :apib_path,
|
4
|
+
:drafter_yaml_path,
|
5
|
+
:prefix
|
7
6
|
|
8
7
|
def initialize
|
9
|
-
@
|
10
|
-
@validation_requests = true
|
11
|
-
@validation_response = true
|
8
|
+
@prefix = ''
|
12
9
|
end
|
13
10
|
end
|
14
11
|
end
|
data/lib/esplanade/middleware.rb
CHANGED
@@ -1,33 +1,20 @@
|
|
1
|
-
require '
|
1
|
+
require 'tomograph'
|
2
|
+
require 'esplanade/request'
|
3
|
+
require 'esplanade/response'
|
2
4
|
|
3
5
|
module Esplanade
|
4
6
|
class Middleware
|
5
|
-
class Exit < RuntimeError; end
|
6
|
-
class ResponseNotDoc < RuntimeError; end
|
7
|
-
|
8
7
|
def initialize(app)
|
9
8
|
@app = app
|
10
|
-
@
|
9
|
+
@documentation = Tomograph::Tomogram.new(
|
10
|
+
prefix: Esplanade.configuration.prefix,
|
11
|
+
apib_path: Esplanade.configuration.apib_path,
|
12
|
+
drafter_yaml_path: Esplanade.configuration.drafter_yaml_path
|
13
|
+
)
|
11
14
|
end
|
12
15
|
|
13
16
|
def call(env)
|
14
|
-
valid!(env)
|
15
|
-
rescue Request::NotDocumented
|
16
|
-
Request::Error.not_documented
|
17
|
-
rescue Response::NotDocumented
|
18
|
-
Response::Error.not_documented
|
19
|
-
rescue Request::Unsuitable => e
|
20
|
-
Request::Error.unsuitable(e.message)
|
21
|
-
rescue Response::Unsuitable => e
|
22
|
-
Response::Error.unsuitable(e.message)
|
23
|
-
end
|
24
|
-
|
25
|
-
def valid!(env)
|
26
|
-
request = Request.new(env, @tomogram)
|
27
|
-
request.valid! if request.validate?
|
28
17
|
status, headers, body = @app.call(env)
|
29
|
-
response = Response.new(status, body, request.schema)
|
30
|
-
response.valid! if response.validate?
|
31
18
|
[status, headers, body]
|
32
19
|
end
|
33
20
|
end
|
data/lib/esplanade/request.rb
CHANGED
@@ -1,31 +1,29 @@
|
|
1
|
-
require '
|
1
|
+
require 'esplanade/request/doc'
|
2
|
+
require 'esplanade/request/raw'
|
3
|
+
require 'esplanade/request/validation'
|
2
4
|
|
3
5
|
module Esplanade
|
4
6
|
class Request
|
5
|
-
|
7
|
+
class Error < Esplanade::Error; end
|
8
|
+
class NotDocumented < Error; end
|
9
|
+
class BodyIsNotJson < Error; end
|
10
|
+
class Invalid < Error; end
|
6
11
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(env, tomogram)
|
11
|
-
@method = env['REQUEST_METHOD']
|
12
|
-
@path = env['PATH_INFO']
|
13
|
-
@body = Body.craft(env)
|
14
|
-
@schema = tomogram.find_request(method: @method, path: @path)
|
15
|
-
raise NotDocumented unless schema || Esplanade.configuration.skip_not_documented
|
16
|
-
self
|
12
|
+
def initialize(documentation, env)
|
13
|
+
@documentation = documentation
|
14
|
+
@env = env
|
17
15
|
end
|
18
16
|
|
19
|
-
def
|
20
|
-
|
17
|
+
def doc
|
18
|
+
@doc ||= Doc.new(@documentation, raw)
|
21
19
|
end
|
22
20
|
|
23
|
-
def
|
24
|
-
|
21
|
+
def raw
|
22
|
+
@raw ||= Raw.new(@env)
|
25
23
|
end
|
26
24
|
|
27
|
-
def
|
28
|
-
@
|
25
|
+
def validation
|
26
|
+
@validation || Validation.new(doc, raw)
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Esplanade
|
2
|
+
class Request
|
3
|
+
class Doc
|
4
|
+
def initialize(main_documentation, raw)
|
5
|
+
@main_documentation = main_documentation
|
6
|
+
@raw = raw
|
7
|
+
end
|
8
|
+
|
9
|
+
def tomogram
|
10
|
+
@tomogram ||= @main_documentation.find_request(method: @raw.method, path: @raw.path)
|
11
|
+
raise NotDocumented, message if @tomogram.nil?
|
12
|
+
@tomogram
|
13
|
+
end
|
14
|
+
|
15
|
+
def json_schema
|
16
|
+
@json_schema ||= tomogram.request
|
17
|
+
end
|
18
|
+
|
19
|
+
def method
|
20
|
+
@method ||= tomogram.method
|
21
|
+
end
|
22
|
+
|
23
|
+
def path
|
24
|
+
@path ||= tomogram.path.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def responses
|
28
|
+
@responses ||= tomogram.responses
|
29
|
+
rescue NotDocumented
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def message
|
36
|
+
{
|
37
|
+
method: @raw.method,
|
38
|
+
path: @raw.path
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'esplanade/request/raw/body'
|
2
|
+
|
3
|
+
module Esplanade
|
4
|
+
class Request
|
5
|
+
class Raw
|
6
|
+
def initialize(env)
|
7
|
+
@env = env
|
8
|
+
end
|
9
|
+
|
10
|
+
def method
|
11
|
+
@method ||= @env['REQUEST_METHOD']
|
12
|
+
end
|
13
|
+
|
14
|
+
def path
|
15
|
+
@path ||= @env['PATH_INFO']
|
16
|
+
end
|
17
|
+
|
18
|
+
def body
|
19
|
+
@body ||= Body.new(self, @env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module Esplanade
|
4
|
+
class Request
|
5
|
+
class Raw
|
6
|
+
class Body
|
7
|
+
def initialize(raw_request, env)
|
8
|
+
@raw_request = raw_request
|
9
|
+
@env = env
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_string
|
13
|
+
@string ||= @env['rack.request.form_vars']
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_hash
|
17
|
+
@hash ||= MultiJson.load(to_string)
|
18
|
+
rescue MultiJson::ParseError
|
19
|
+
raise BodyIsNotJson, message
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def message
|
25
|
+
{
|
26
|
+
method: @raw_request.method,
|
27
|
+
path: @raw_request.path,
|
28
|
+
body: to_string
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'json-schema'
|
2
|
+
require 'esplanade/error'
|
3
|
+
|
4
|
+
module Esplanade
|
5
|
+
class Request
|
6
|
+
class Validation
|
7
|
+
def initialize(doc, raw)
|
8
|
+
@doc = doc
|
9
|
+
@raw = raw
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid!
|
13
|
+
@error ||= JSON::Validator.fully_validate(@doc.json_schema, @raw.body.to_hash)
|
14
|
+
raise Invalid, message if @error != []
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def message
|
20
|
+
{
|
21
|
+
method: @raw.method,
|
22
|
+
path: @raw.path,
|
23
|
+
body: @raw.body.to_string,
|
24
|
+
error: @error
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/esplanade/response.rb
CHANGED
@@ -1,38 +1,32 @@
|
|
1
|
-
require '
|
1
|
+
require 'esplanade/response/doc'
|
2
|
+
require 'esplanade/response/raw'
|
3
|
+
require 'esplanade/response/validation'
|
2
4
|
|
3
5
|
module Esplanade
|
4
6
|
class Response
|
5
|
-
|
7
|
+
class Error < Esplanade::Error; end
|
8
|
+
class NotDocumented < Error; end
|
9
|
+
class BodyIsNotJson < Error; end
|
10
|
+
class Invalid < Error; end
|
6
11
|
|
7
|
-
|
8
|
-
class NotDocumented < RuntimeError; end
|
12
|
+
attr_reader :request
|
9
13
|
|
10
|
-
def initialize(
|
11
|
-
|
14
|
+
def initialize(request, status, raw_body)
|
15
|
+
@request = request
|
12
16
|
@status = status
|
13
|
-
@
|
14
|
-
@schemas = expect_request.find_responses(status: @status)
|
15
|
-
raise NotDocumented unless (@schemas&.first) || Esplanade.configuration.skip_not_documented
|
16
|
-
self
|
17
|
+
@raw_body = raw_body
|
17
18
|
end
|
18
19
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
@schemas.each do |action|
|
23
|
-
res = JSON::Validator.fully_validate(action['body'], @body)
|
24
|
-
return res if res == []
|
25
|
-
end
|
26
|
-
|
27
|
-
['invalid']
|
20
|
+
def doc
|
21
|
+
@doc ||= Doc.new(@request, raw)
|
28
22
|
end
|
29
23
|
|
30
|
-
def
|
31
|
-
|
24
|
+
def raw
|
25
|
+
@raw ||= Raw.new(@request, @status, @raw_body)
|
32
26
|
end
|
33
27
|
|
34
|
-
def
|
35
|
-
@
|
28
|
+
def validation
|
29
|
+
@validation ||= Validation.new(@request, doc, raw)
|
36
30
|
end
|
37
31
|
end
|
38
32
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Esplanade
|
2
|
+
class Response
|
3
|
+
class Doc
|
4
|
+
def initialize(request, raw)
|
5
|
+
@request = request
|
6
|
+
@raw = raw
|
7
|
+
end
|
8
|
+
|
9
|
+
def tomogram
|
10
|
+
@tomogram ||= @request.doc.responses.find_all { |response| response['status'] == @raw.status }
|
11
|
+
raise NotDocumented, message if @tomogram == []
|
12
|
+
@tomogram
|
13
|
+
end
|
14
|
+
|
15
|
+
def json_schemas
|
16
|
+
@json_schemas ||= tomogram.map { |action| action['body'] }
|
17
|
+
end
|
18
|
+
|
19
|
+
def status
|
20
|
+
@status ||= tomogram['status']
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def message
|
26
|
+
{
|
27
|
+
request: {
|
28
|
+
method: @request.raw.method,
|
29
|
+
path: @request.raw.path
|
30
|
+
},
|
31
|
+
status: @raw.status
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'esplanade/response/raw/body'
|
2
|
+
|
3
|
+
module Esplanade
|
4
|
+
class Response
|
5
|
+
class Raw
|
6
|
+
def initialize(request, raw_status, raw_body)
|
7
|
+
@request = request
|
8
|
+
@raw_status = raw_status
|
9
|
+
@raw_body = raw_body
|
10
|
+
end
|
11
|
+
|
12
|
+
def status
|
13
|
+
@status ||= @raw_status.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def body
|
17
|
+
@body ||= Body.new(@request, self, @raw_body)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
|
3
|
+
module Esplanade
|
4
|
+
class Response
|
5
|
+
class Raw
|
6
|
+
class Body
|
7
|
+
def initialize(request, raw_response, raw_body)
|
8
|
+
@request = request
|
9
|
+
@raw_response = raw_response
|
10
|
+
@raw_body = raw_body
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_string
|
14
|
+
@string ||= @raw_body.body rescue nil
|
15
|
+
@string ||= @raw_body.first rescue nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_hash
|
19
|
+
@hash ||= MultiJson.load(to_string)
|
20
|
+
rescue MultiJson::ParseError
|
21
|
+
raise BodyIsNotJson, message
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def message
|
27
|
+
{
|
28
|
+
request: {
|
29
|
+
method: @request.raw.method,
|
30
|
+
path: @request.raw.path
|
31
|
+
},
|
32
|
+
status: @raw_response.status,
|
33
|
+
body: @raw_response.body.to_string
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'json-schema'
|
2
|
+
|
3
|
+
module Esplanade
|
4
|
+
class Response
|
5
|
+
class Validation
|
6
|
+
def initialize(request, doc, raw)
|
7
|
+
@request = request
|
8
|
+
@doc = doc
|
9
|
+
@raw = raw
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid!
|
13
|
+
@error ||= if @doc.json_schemas.size == 1
|
14
|
+
one_json_schema
|
15
|
+
else
|
16
|
+
more_than_one_json_schema
|
17
|
+
end
|
18
|
+
raise Invalid, message if @error != []
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def one_json_schema
|
24
|
+
JSON::Validator.fully_validate(@doc.json_schemas.first, @raw.body.to_hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
def more_than_one_json_schema
|
28
|
+
main_res = @doc.json_schemas.each do |json_schema|
|
29
|
+
res = JSON::Validator.fully_validate(json_schema, @raw.body.to_hash)
|
30
|
+
break res if res == []
|
31
|
+
end
|
32
|
+
if main_res != []
|
33
|
+
['invalid']
|
34
|
+
else
|
35
|
+
[]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def message
|
40
|
+
{
|
41
|
+
request: {
|
42
|
+
method: @request.raw.method,
|
43
|
+
path: @request.raw.path
|
44
|
+
},
|
45
|
+
status: @raw.status,
|
46
|
+
body: @raw.body.to_string,
|
47
|
+
error: @error
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/esplanade/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: esplanade
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- d.efimov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
@@ -51,25 +51,25 @@ dependencies:
|
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: 2.6.2
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
54
|
+
name: tomograph
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - "~>"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
59
|
+
version: '1.1'
|
60
60
|
- - ">="
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
62
|
+
version: 1.1.0
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - "~>"
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: '
|
69
|
+
version: '1.1'
|
70
70
|
- - ">="
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version:
|
72
|
+
version: 1.1.0
|
73
73
|
- !ruby/object:Gem::Dependency
|
74
74
|
name: bundler
|
75
75
|
requirement: !ruby/object:Gem::Requirement
|
@@ -188,6 +188,7 @@ files:
|
|
188
188
|
- ".gitignore"
|
189
189
|
- ".rubocop.yml"
|
190
190
|
- ".ruby-version"
|
191
|
+
- ".travis.yml"
|
191
192
|
- CODE_OF_CONDUCT.md
|
192
193
|
- Gemfile
|
193
194
|
- LICENSE.txt
|
@@ -198,14 +199,18 @@ files:
|
|
198
199
|
- esplanade.gemspec
|
199
200
|
- lib/esplanade.rb
|
200
201
|
- lib/esplanade/configuration.rb
|
202
|
+
- lib/esplanade/error.rb
|
201
203
|
- lib/esplanade/middleware.rb
|
202
|
-
- lib/esplanade/railtie.rb
|
203
204
|
- lib/esplanade/request.rb
|
204
|
-
- lib/esplanade/request/
|
205
|
-
- lib/esplanade/request/
|
205
|
+
- lib/esplanade/request/doc.rb
|
206
|
+
- lib/esplanade/request/raw.rb
|
207
|
+
- lib/esplanade/request/raw/body.rb
|
208
|
+
- lib/esplanade/request/validation.rb
|
206
209
|
- lib/esplanade/response.rb
|
207
|
-
- lib/esplanade/response/
|
208
|
-
- lib/esplanade/response/
|
210
|
+
- lib/esplanade/response/doc.rb
|
211
|
+
- lib/esplanade/response/raw.rb
|
212
|
+
- lib/esplanade/response/raw/body.rb
|
213
|
+
- lib/esplanade/response/validation.rb
|
209
214
|
- lib/esplanade/version.rb
|
210
215
|
homepage:
|
211
216
|
licenses:
|
@@ -227,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
227
232
|
version: '0'
|
228
233
|
requirements: []
|
229
234
|
rubyforge_project:
|
230
|
-
rubygems_version: 2.5
|
235
|
+
rubygems_version: 2.4.5
|
231
236
|
signing_key:
|
232
237
|
specification_version: 4
|
233
238
|
summary: Validate requests and responses against API Blueprint specifications
|
data/lib/esplanade/railtie.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
module Esplanade
|
2
|
-
class Request
|
3
|
-
class Body < Hash
|
4
|
-
private_class_method :new
|
5
|
-
|
6
|
-
def self.craft(env)
|
7
|
-
params_string = env['rack.input'].read
|
8
|
-
params_hash = {}
|
9
|
-
params_hash = MultiJson.load(params_string) unless params_string == ''
|
10
|
-
new.merge(params_hash)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Esplanade
|
2
|
-
class Request
|
3
|
-
class Error
|
4
|
-
def self.unsuitable(message)
|
5
|
-
status = '400'
|
6
|
-
headers = { 'Content-Type' => 'application/json; charset=utf-8' }
|
7
|
-
body = [MultiJson.dump(error: [message[2..-3]])]
|
8
|
-
[status, headers, body]
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.not_documented
|
12
|
-
status = '400'
|
13
|
-
headers = { 'Content-Type' => 'application/json; charset=utf-8' }
|
14
|
-
body = [MultiJson.dump(error: ['Not documented'])]
|
15
|
-
[status, headers, body]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
module Esplanade
|
2
|
-
class Response
|
3
|
-
class Body < Hash
|
4
|
-
class << self
|
5
|
-
private_class_method :new
|
6
|
-
|
7
|
-
def craft(body)
|
8
|
-
# According to specification Rack http://rack.github.io
|
9
|
-
# body can only answer each
|
10
|
-
lines = []
|
11
|
-
body.each { |line| lines.push(line) }
|
12
|
-
lines_to_json(lines)
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def lines_to_json(lines)
|
18
|
-
if lines.join.empty?
|
19
|
-
{}
|
20
|
-
else
|
21
|
-
res = lines.join('\n')
|
22
|
-
begin
|
23
|
-
MultiJson.load(res)
|
24
|
-
rescue MultiJson::ParseError
|
25
|
-
res
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Esplanade
|
2
|
-
class Response
|
3
|
-
class Error
|
4
|
-
def self.unsuitable(message)
|
5
|
-
status = '500'
|
6
|
-
headers = { 'Content-Type' => 'application/json; charset=utf-8' }
|
7
|
-
body = [MultiJson.dump(error: [message[2..-3]])]
|
8
|
-
[status, headers, body]
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.not_documented
|
12
|
-
status = '500'
|
13
|
-
headers = { 'Content-Type' => 'application/json; charset=utf-8' }
|
14
|
-
body = [MultiJson.dump(error: ['Not documented'])]
|
15
|
-
[status, headers, body]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|