sinatra-swagger 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
17
+ config/
18
+ bin/
19
+ vendor/
20
+ log/
21
+ Gemfile.lock
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
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Sinatra::Swagger
2
+
3
+ Helper methods for accesing your swagger documentation from within your sinatra routes.
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
@@ -0,0 +1,5 @@
1
+ module Sinatra
2
+ module Swagger
3
+ VERSION = File.read(File.join(__dir__, "../../../VERSION")) rescue "0.0.0"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require "sinatra/swagger/version"
2
+ require "sinatra/swagger/spec_enforcer"
@@ -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
@@ -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
+ }