sinatra-swagger 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +21 -0
- data/.travis.yml +12 -0
- data/Gemfile +3 -0
- data/README.md +3 -0
- data/Rakefile +23 -0
- data/VERSION +1 -0
- data/lib/sinatra/swagger/spec_enforcer.rb +68 -0
- data/lib/sinatra/swagger/version.rb +5 -0
- data/lib/sinatra/swagger.rb +2 -0
- data/lib/swagger/base.rb +33 -0
- data/lib/swagger/rack.rb +42 -0
- data/schema/README.md +5 -0
- data/schema/draft4-schema.json +150 -0
- data/schema/swagger-schema.json +1483 -0
- data/sinatra-swagger.gemspec +27 -0
- metadata +116 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MmJlMmY4Y2NiMTI5NTljNDAyNGIxODBkY2ZlZmI3OTFiYWU2ODNjMQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MTFiY2Q2NDRhYTJmMmU5ODAyYzgxMDg4OGJjYzRlNDg3NzZiMjgyZg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YjZmMDE4YTZiMDIzMTUyODAwZDhlYjM4YThkZjFkNjNhYTNjNjc5ZWQwM2M5
|
10
|
+
MTc3MzY4NjlmM2E3ZGMyYzExNWY3MjA0OGNiMDI1N2I2NjlkYjBlMTA2YmEx
|
11
|
+
NmVkMGUwZGJkZWU4ZDBhYjhiOWFmMjA0NmY1NTNjOWY0ZGVjZGI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YWQ0NjIzOGJmZTA4NzU2YjIyNGMyN2IwZWJkYWQxMjU4Nzg0ZjA4OTY5ZTRj
|
14
|
+
ZmE2OTdlNzIyNzNiNzNhYTQ4YzIwMTdkMDkxNzgxZmZlMzJiOTg4MjZlY2I1
|
15
|
+
ZjdmYjZjMjhiNjJkYmQzNzg4OWQxYTI1NGZjZWNhOTc3ZGNiZjQ=
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.0.0
|
4
|
+
- 2.1.2
|
5
|
+
deploy:
|
6
|
+
provider: rubygems
|
7
|
+
api_key:
|
8
|
+
secure: f3c7U1z9hulitL8sjYztJmEEgGsCC/X/q/VybFYOC2wsp5D2WggYJ4CTaWcNjCSAWXgVl5L0kOkjKYyItdxVJoyhBa4Mc3MfZo8Dp2UCYpdA+FGd7UbOqbITkr9ZyD22oWk7PgfKeNKoLyffdG9HH/OYJ5ezSa7PB2XMN+Ae2W8=
|
9
|
+
gem: sinatra-swagger
|
10
|
+
on:
|
11
|
+
tags: true
|
12
|
+
repo: jphastings/sinatra-swagger
|
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "rake"
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
|
5
|
+
task :default => :build
|
6
|
+
task :build => :test
|
7
|
+
|
8
|
+
desc "Runs all tests"
|
9
|
+
task :test => :spec
|
10
|
+
|
11
|
+
desc "Run all rspec tests"
|
12
|
+
begin
|
13
|
+
require "rspec/core/rake_task"
|
14
|
+
|
15
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
16
|
+
t.pattern = "spec/**/*_spec.rb"
|
17
|
+
end
|
18
|
+
rescue LoadError => e
|
19
|
+
raise e
|
20
|
+
task :spec do
|
21
|
+
$stderr.puts "Please install rspec: `gem install rspec`"
|
22
|
+
end
|
23
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "swagger/rack"
|
3
|
+
|
4
|
+
module Sinatra
|
5
|
+
module Swagger
|
6
|
+
module SpecEnforcer
|
7
|
+
def swagger(filepath)
|
8
|
+
set :swagger, ::Swagger::Base.new(filepath)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.registered(app)
|
12
|
+
app.helpers Helpers
|
13
|
+
|
14
|
+
app.before do
|
15
|
+
spec = swagger_spec
|
16
|
+
unless spec.nil?
|
17
|
+
_, captures, spec = spec.values
|
18
|
+
|
19
|
+
invalidities = Hash[(spec['parameters'] || []).map { |details|
|
20
|
+
param_name = details['name']
|
21
|
+
|
22
|
+
parameter = case details['in']
|
23
|
+
when "query"
|
24
|
+
params[param_name]
|
25
|
+
when "path"
|
26
|
+
captures[param_name]
|
27
|
+
else
|
28
|
+
raise NotImplementedError, "Can't cope with #{details['in']} parameters right now"
|
29
|
+
end
|
30
|
+
|
31
|
+
if !parameter
|
32
|
+
next [param_name, :missing] if details['required'] && details['required'] != "false"
|
33
|
+
next unless details['default']
|
34
|
+
parameter = details['default']
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
parameter = ::Swagger.cast(parameter.to_s, details['type']) if parameter
|
39
|
+
rescue ArgumentError
|
40
|
+
next [param_name, :incorrect_type]
|
41
|
+
end
|
42
|
+
|
43
|
+
if %w{integer number}.include? details['type']
|
44
|
+
too_large = details['maximum'] && parameter > details['maximum']
|
45
|
+
too_small = details['minimum'] && parameter < details['minimum']
|
46
|
+
next [param_name, :out_of_range] if too_large || too_small
|
47
|
+
end
|
48
|
+
|
49
|
+
params[param_name] = parameter
|
50
|
+
|
51
|
+
nil
|
52
|
+
}.compact]
|
53
|
+
|
54
|
+
halt 400, invalidities.to_json if invalidities.any?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module Helpers
|
60
|
+
def swagger_spec
|
61
|
+
settings.swagger.request_spec(env: env)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
register Swagger::SpecEnforcer
|
68
|
+
end
|
data/lib/swagger/base.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require "json-schema"
|
2
|
+
|
3
|
+
module Swagger
|
4
|
+
# Casts a string value to the ruby datatype as definied in the swagger spec
|
5
|
+
def self.cast(value, type = "string")
|
6
|
+
raise ArgumentError, "#{value} is not a string" unless value.is_a?(String)
|
7
|
+
case type
|
8
|
+
when "string"
|
9
|
+
value
|
10
|
+
when "integer"
|
11
|
+
raise ArgumentError, "#{value} is not an integer" unless value =~ /^-?\d+$/
|
12
|
+
value.to_i
|
13
|
+
when "number"
|
14
|
+
raise ArgumentError, "#{value} is not a float" unless value =~ /^-?\d+(?:\.\d+)?$/
|
15
|
+
value.to_f
|
16
|
+
else
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Base
|
22
|
+
# Pre-load JSON-schema files used for validation
|
23
|
+
Dir.glob(File.join(__dir__, "../../schema/*-schema.json")) do |schema|
|
24
|
+
data = JSON.parse(File.read(schema))
|
25
|
+
JSON::Validator.add_schema(JSON::Schema.new(data, data['id']))
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(filepath)
|
29
|
+
@spec = YAML.load(open(filepath))
|
30
|
+
JSON::Validator.validate!({ "$ref" => "http://swagger.io/v2/schema.json#" }, @spec)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/swagger/rack.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "swagger/base"
|
2
|
+
|
3
|
+
module Swagger
|
4
|
+
module RackHelpers
|
5
|
+
def request_spec(env: nil)
|
6
|
+
path = env['REQUEST_PATH']
|
7
|
+
verb = env['REQUEST_METHOD'].downcase
|
8
|
+
matching_paths = (@spec['paths'] || {}).map { |spec_path, spec|
|
9
|
+
next unless spec[verb]
|
10
|
+
matches = match_string(spec_path, path)
|
11
|
+
next if matches.nil?
|
12
|
+
{
|
13
|
+
path: spec_path,
|
14
|
+
captures: matches,
|
15
|
+
spec: spec[verb]
|
16
|
+
}
|
17
|
+
}.compact
|
18
|
+
|
19
|
+
case matching_paths.size
|
20
|
+
when 0
|
21
|
+
return nil
|
22
|
+
when 1
|
23
|
+
return matching_paths.first
|
24
|
+
else
|
25
|
+
spec_paths = matching_paths.map { |p| p[:path] }
|
26
|
+
raise "Your API documentation is non-deterministic for the path: #{path} (#{spec_paths.join(', ')})"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def match_string(bracketed_string, match_string)
|
33
|
+
re = bracketed_string.gsub(/\{([^}]+)\}/, "(?<\\1>.+?)")
|
34
|
+
matches = Regexp.new("^#{re}$").match(match_string)
|
35
|
+
return nil if matches.nil?
|
36
|
+
captures = matches.captures
|
37
|
+
Hash[matches.names.zip(captures)]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Base.include(RackHelpers)
|
42
|
+
end
|
data/schema/README.md
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
# Schema
|
2
|
+
|
3
|
+
These schema are the ones needed to validate Swagger 2.0 documentation without requiring access to the internet.
|
4
|
+
|
5
|
+
If the Swagger 2.x schema updates then the files here should also be updated. Any file matching `*-schema.json` will be loaded as a JSON Schema file (and will cause errors if it does not validate itself)
|
@@ -0,0 +1,150 @@
|
|
1
|
+
{
|
2
|
+
"id": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
4
|
+
"description": "Core schema meta-schema",
|
5
|
+
"definitions": {
|
6
|
+
"schemaArray": {
|
7
|
+
"type": "array",
|
8
|
+
"minItems": 1,
|
9
|
+
"items": { "$ref": "#" }
|
10
|
+
},
|
11
|
+
"positiveInteger": {
|
12
|
+
"type": "integer",
|
13
|
+
"minimum": 0
|
14
|
+
},
|
15
|
+
"positiveIntegerDefault0": {
|
16
|
+
"allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
|
17
|
+
},
|
18
|
+
"simpleTypes": {
|
19
|
+
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
|
20
|
+
},
|
21
|
+
"stringArray": {
|
22
|
+
"type": "array",
|
23
|
+
"items": { "type": "string" },
|
24
|
+
"minItems": 1,
|
25
|
+
"uniqueItems": true
|
26
|
+
}
|
27
|
+
},
|
28
|
+
"type": "object",
|
29
|
+
"properties": {
|
30
|
+
"id": {
|
31
|
+
"type": "string",
|
32
|
+
"format": "uri"
|
33
|
+
},
|
34
|
+
"$schema": {
|
35
|
+
"type": "string",
|
36
|
+
"format": "uri"
|
37
|
+
},
|
38
|
+
"title": {
|
39
|
+
"type": "string"
|
40
|
+
},
|
41
|
+
"description": {
|
42
|
+
"type": "string"
|
43
|
+
},
|
44
|
+
"default": {},
|
45
|
+
"multipleOf": {
|
46
|
+
"type": "number",
|
47
|
+
"minimum": 0,
|
48
|
+
"exclusiveMinimum": true
|
49
|
+
},
|
50
|
+
"maximum": {
|
51
|
+
"type": "number"
|
52
|
+
},
|
53
|
+
"exclusiveMaximum": {
|
54
|
+
"type": "boolean",
|
55
|
+
"default": false
|
56
|
+
},
|
57
|
+
"minimum": {
|
58
|
+
"type": "number"
|
59
|
+
},
|
60
|
+
"exclusiveMinimum": {
|
61
|
+
"type": "boolean",
|
62
|
+
"default": false
|
63
|
+
},
|
64
|
+
"maxLength": { "$ref": "#/definitions/positiveInteger" },
|
65
|
+
"minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
66
|
+
"pattern": {
|
67
|
+
"type": "string",
|
68
|
+
"format": "regex"
|
69
|
+
},
|
70
|
+
"additionalItems": {
|
71
|
+
"anyOf": [
|
72
|
+
{ "type": "boolean" },
|
73
|
+
{ "$ref": "#" }
|
74
|
+
],
|
75
|
+
"default": {}
|
76
|
+
},
|
77
|
+
"items": {
|
78
|
+
"anyOf": [
|
79
|
+
{ "$ref": "#" },
|
80
|
+
{ "$ref": "#/definitions/schemaArray" }
|
81
|
+
],
|
82
|
+
"default": {}
|
83
|
+
},
|
84
|
+
"maxItems": { "$ref": "#/definitions/positiveInteger" },
|
85
|
+
"minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
86
|
+
"uniqueItems": {
|
87
|
+
"type": "boolean",
|
88
|
+
"default": false
|
89
|
+
},
|
90
|
+
"maxProperties": { "$ref": "#/definitions/positiveInteger" },
|
91
|
+
"minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
|
92
|
+
"required": { "$ref": "#/definitions/stringArray" },
|
93
|
+
"additionalProperties": {
|
94
|
+
"anyOf": [
|
95
|
+
{ "type": "boolean" },
|
96
|
+
{ "$ref": "#" }
|
97
|
+
],
|
98
|
+
"default": {}
|
99
|
+
},
|
100
|
+
"definitions": {
|
101
|
+
"type": "object",
|
102
|
+
"additionalProperties": { "$ref": "#" },
|
103
|
+
"default": {}
|
104
|
+
},
|
105
|
+
"properties": {
|
106
|
+
"type": "object",
|
107
|
+
"additionalProperties": { "$ref": "#" },
|
108
|
+
"default": {}
|
109
|
+
},
|
110
|
+
"patternProperties": {
|
111
|
+
"type": "object",
|
112
|
+
"additionalProperties": { "$ref": "#" },
|
113
|
+
"default": {}
|
114
|
+
},
|
115
|
+
"dependencies": {
|
116
|
+
"type": "object",
|
117
|
+
"additionalProperties": {
|
118
|
+
"anyOf": [
|
119
|
+
{ "$ref": "#" },
|
120
|
+
{ "$ref": "#/definitions/stringArray" }
|
121
|
+
]
|
122
|
+
}
|
123
|
+
},
|
124
|
+
"enum": {
|
125
|
+
"type": "array",
|
126
|
+
"minItems": 1,
|
127
|
+
"uniqueItems": true
|
128
|
+
},
|
129
|
+
"type": {
|
130
|
+
"anyOf": [
|
131
|
+
{ "$ref": "#/definitions/simpleTypes" },
|
132
|
+
{
|
133
|
+
"type": "array",
|
134
|
+
"items": { "$ref": "#/definitions/simpleTypes" },
|
135
|
+
"minItems": 1,
|
136
|
+
"uniqueItems": true
|
137
|
+
}
|
138
|
+
]
|
139
|
+
},
|
140
|
+
"allOf": { "$ref": "#/definitions/schemaArray" },
|
141
|
+
"anyOf": { "$ref": "#/definitions/schemaArray" },
|
142
|
+
"oneOf": { "$ref": "#/definitions/schemaArray" },
|
143
|
+
"not": { "$ref": "#" }
|
144
|
+
},
|
145
|
+
"dependencies": {
|
146
|
+
"exclusiveMaximum": [ "maximum" ],
|
147
|
+
"exclusiveMinimum": [ "minimum" ]
|
148
|
+
},
|
149
|
+
"default": {}
|
150
|
+
}
|