sinatra-swagger 0.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 +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
|
+
}
|