swagger-core 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.rubocop.yml +2 -0
- data/.yardopts +4 -0
- data/Gemfile +1 -0
- data/README.md +87 -4
- data/Rakefile +4 -1
- data/lib/swagger.rb +30 -6
- data/lib/swagger/{api_declaration.rb → api.rb} +4 -3
- data/lib/swagger/attachable.rb +8 -5
- data/lib/swagger/builder.rb +24 -2
- data/lib/swagger/mime_type.rb +20 -0
- data/lib/swagger/parsers.rb +38 -0
- data/lib/swagger/schema.rb +3 -0
- data/lib/swagger/swagger_object.rb +42 -0
- data/lib/swagger/uri.rb +2 -1
- data/lib/swagger/uri_template.rb +3 -2
- data/lib/swagger/v2/api.rb +89 -0
- data/lib/swagger/v2/example.rb +11 -3
- data/lib/swagger/v2/info.rb +24 -14
- data/lib/swagger/v2/operation.rb +52 -0
- data/lib/swagger/v2/parameter.rb +40 -1
- data/lib/swagger/v2/path.rb +7 -6
- data/lib/swagger/v2/response.rb +9 -5
- data/lib/swagger/v2/tag.rb +11 -0
- data/lib/swagger/version.rb +1 -1
- data/resources/schemas/swagger/v2.0/schema.json +2 -2
- data/spec/swagger/{api_declaration_spec.rb → api_spec.rb} +1 -1
- data/spec/swagger/builder_spec.rb +52 -0
- data/spec/swagger/{api_operation_spec.rb → operation_spec.rb} +9 -1
- data/spec/swagger/swagger_spec.rb +50 -1
- data/swagger-core.gemspec +2 -0
- metadata +73 -62
- data/lib/swagger/definition_section.rb +0 -34
- data/lib/swagger/loaders.rb +0 -29
- data/lib/swagger/v2/api_declaration.rb +0 -81
- data/lib/swagger/v2/api_operation.rb +0 -56
data/lib/swagger/uri_template.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Swagger
|
2
|
+
# Class representing a URI Template. Backed by Addressable::Template.
|
3
|
+
# @see http://tools.ietf.org/html/rfc6570
|
2
4
|
class URITemplate < String
|
3
5
|
attr_reader :uri_template
|
4
6
|
def initialize(string)
|
5
|
-
|
6
|
-
@uri_template = Addressable::Template.new string
|
7
|
+
@uri_template = Addressable::Template.new(string)
|
7
8
|
super
|
8
9
|
end
|
9
10
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'swagger/swagger_object'
|
2
|
+
require 'swagger/v2/info'
|
3
|
+
require 'swagger/v2/path'
|
4
|
+
require 'swagger/v2/tag'
|
5
|
+
require 'json-schema'
|
6
|
+
|
7
|
+
module Swagger
|
8
|
+
# Module containing classes that handle version 2 of the Swagger specification.
|
9
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md Swagger Specification 2.0
|
10
|
+
module V2
|
11
|
+
SWAGGER_SCHEMA = File.expand_path 'schemas/swagger/v2.0/schema.json', Swagger::RESOURCES_DIR
|
12
|
+
JSON_SCHEMA = File.expand_path 'schemas/json_schema/draft-04.json', Swagger::RESOURCES_DIR
|
13
|
+
|
14
|
+
# Class representing the top level "Swagger Object"
|
15
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#swagger-object- Swagger Object
|
16
|
+
class API < Swagger::API
|
17
|
+
# @group Swagger Fields
|
18
|
+
# HACK: Swagger-Spec: This is correct for now, but schema will likely be changed to String
|
19
|
+
required_field :swagger, Float
|
20
|
+
required_field :info, Info
|
21
|
+
field :host, Swagger::URITemplate
|
22
|
+
field :basePath, Swagger::URITemplate
|
23
|
+
field :schemes, Array[String]
|
24
|
+
field :consumes, Array[String]
|
25
|
+
field :produces, Array[String]
|
26
|
+
required_field :paths, Hash[String => Path]
|
27
|
+
field :definitions, Hash[String => Schema]
|
28
|
+
field :parameters, Hash[String => Parameter]
|
29
|
+
field :responses, Hash[String => Response]
|
30
|
+
# HACK: Swagger-Spec: Not documented/defined yet. Implement once spec is complete.
|
31
|
+
field :security, Object
|
32
|
+
# TODO: This is actually an array of tag names, not Tag objects, need to handle relation
|
33
|
+
field :tag, Array[String]
|
34
|
+
# TODO: externalDocs - Documentable Module
|
35
|
+
# @endgroup
|
36
|
+
|
37
|
+
alias_method :base_path, :basePath
|
38
|
+
|
39
|
+
# All operations under all paths
|
40
|
+
# @return [Array<Operation>]
|
41
|
+
def operations
|
42
|
+
# Perhaps not the best way...
|
43
|
+
paths.values.map do | path |
|
44
|
+
path.operations.values
|
45
|
+
end.flatten
|
46
|
+
end
|
47
|
+
|
48
|
+
# A complete (including host) URI Template for the basePath.
|
49
|
+
# @return [Swagger::URITemplate]
|
50
|
+
def uri_template
|
51
|
+
Swagger::URITemplate.new("#{host}#{basePath}")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Validates this object against the Swagger specification and returns all detected errors.
|
55
|
+
# Slower than {#validate}.
|
56
|
+
# @return [true] if the object fully complies with the Swagger specification.
|
57
|
+
# @raise [Swagger::InvalidDefinition] if any errors are found.
|
58
|
+
def fully_validate
|
59
|
+
# NOTE: fully_validate is ideal, but very slow with the current schema/validator
|
60
|
+
errors = JSON::Validator.fully_validate(swagger_schema, to_json)
|
61
|
+
fail Swagger::InvalidDefinition, errors unless errors.empty?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
# Validates this object against the Swagger specification and returns the first detected error.
|
66
|
+
# Faster than {#fully_validate}.
|
67
|
+
# @return [true] if the object fully complies with the Swagger specification.
|
68
|
+
# @raise [Swagger::InvalidDefinition] if an error is found.
|
69
|
+
def validate
|
70
|
+
JSON::Validator.validate!(swagger_schema, to_json)
|
71
|
+
rescue JSON::Schema::ValidationError => e
|
72
|
+
raise Swagger::InvalidDefinition, e.message
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def swagger_schema
|
78
|
+
@swagger_schema ||= JSON.parse(File.read(SWAGGER_SCHEMA))
|
79
|
+
|
80
|
+
# FIXME: Swagger should be able to parse offline. Blocked by json-schema.
|
81
|
+
# Offline workaround
|
82
|
+
# @swagger_schema = JSON.parse(File.read(SWAGGER_SCHEMA)
|
83
|
+
# .gsub('http://json-schema.org/draft-04/schema', "file://#{SWAGGER_SCHEMA}"))
|
84
|
+
# @swagger_schema['$schema'] = 'http://json-schema.org/draft-04/schema#'
|
85
|
+
# @swagger_schema
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/swagger/v2/example.rb
CHANGED
@@ -1,19 +1,27 @@
|
|
1
1
|
module Swagger
|
2
2
|
module V2
|
3
|
+
# A class to represent example objects in the Swagger schema.
|
4
|
+
# Usually used to represent example request or responses.
|
5
|
+
# Provides access to both the raw example or a parsed representation.
|
3
6
|
class Example
|
4
7
|
extend Forwardable
|
5
8
|
def_delegator :@raw, :to_s, :inspect
|
6
9
|
|
10
|
+
# The example as it appears in the Swagger document.
|
11
|
+
# @return Object the example
|
7
12
|
attr_reader :raw
|
8
13
|
|
9
14
|
def initialize(sample)
|
10
15
|
@raw = sample
|
11
16
|
end
|
12
17
|
|
13
|
-
|
18
|
+
# The example after it has been parsed to match the +media_type+.
|
19
|
+
# @param media_type [String] the target media_type
|
20
|
+
# @return [Object] an object according to the +media_type+
|
21
|
+
def parse(media_type = 'application/json')
|
14
22
|
return @raw unless @raw.is_a? String
|
15
|
-
|
16
|
-
|
23
|
+
parser = Swagger::MIMEType.parser_for(media_type)
|
24
|
+
parser.parse(@raw)
|
17
25
|
end
|
18
26
|
|
19
27
|
def inspect
|
data/lib/swagger/v2/info.rb
CHANGED
@@ -1,23 +1,33 @@
|
|
1
|
+
require 'swagger/swagger_object'
|
2
|
+
|
1
3
|
module Swagger
|
2
4
|
module V2
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# Class representing a Swagger "Info Object".
|
6
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#infoObject Info Object
|
7
|
+
class Info < SwaggerObject
|
8
|
+
# Class representing a Swagger "Contact Object".
|
9
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#contactObject Contact Object
|
10
|
+
class Contact < SwaggerObject
|
11
|
+
# @group Swagger Fields
|
12
|
+
field :name, String
|
13
|
+
field :url, Swagger::URI
|
14
|
+
field :email, String
|
15
|
+
# @endgroup
|
8
16
|
end
|
9
17
|
|
10
|
-
|
11
|
-
|
12
|
-
|
18
|
+
# Class representing a Swagger "License Object".
|
19
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#licenseObject License Object
|
20
|
+
class License < SwaggerObject
|
21
|
+
required_field :name, String
|
22
|
+
field :url, Swagger::URI
|
13
23
|
end
|
14
24
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
25
|
+
required_field :title, String
|
26
|
+
field :description, String
|
27
|
+
field :termsOfService, String
|
28
|
+
field :contact, Contact
|
29
|
+
field :license, License
|
30
|
+
required_field :version, String
|
21
31
|
end
|
22
32
|
end
|
23
33
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'swagger/swagger_object'
|
2
|
+
require 'swagger/v2/parameter'
|
3
|
+
require 'swagger/v2/response'
|
4
|
+
|
5
|
+
module Swagger
|
6
|
+
module V2
|
7
|
+
# Class representing a Swagger "Operation Object".
|
8
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#operationObject Operation Object
|
9
|
+
class Operation < SwaggerObject
|
10
|
+
extend Forwardable
|
11
|
+
def_delegators :parent, :uri_template, :path, :host
|
12
|
+
|
13
|
+
# required_field :verb, Symbol
|
14
|
+
field :summary, String
|
15
|
+
field :description, String
|
16
|
+
field :operationId, String
|
17
|
+
alias_method :operation_id, :operationId
|
18
|
+
field :produces, Array[String]
|
19
|
+
field :consumes, Array[String]
|
20
|
+
field :tags, Array[String]
|
21
|
+
field :parameters, Array[Parameter]
|
22
|
+
field :responses, Hash[String => Response]
|
23
|
+
field :schemes, Array[String]
|
24
|
+
|
25
|
+
# TODO: Add externalDocs
|
26
|
+
|
27
|
+
def api_title
|
28
|
+
root.info.title
|
29
|
+
end
|
30
|
+
|
31
|
+
def full_name
|
32
|
+
"#{api_title} - #{summary}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def verb
|
36
|
+
parent.operations.key self
|
37
|
+
end
|
38
|
+
|
39
|
+
def signature
|
40
|
+
"#{verb.to_s.upcase} #{parent.uri_template}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def default_response
|
44
|
+
return nil if responses.values.nil?
|
45
|
+
|
46
|
+
# FIXME: Swagger isn't very clear on "normal response codes"
|
47
|
+
# In the examples, default is actually an error
|
48
|
+
responses['200'] || responses['201'] || responses['default'] || responses.values.first
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/swagger/v2/parameter.rb
CHANGED
@@ -1,6 +1,45 @@
|
|
1
|
+
require 'swagger/swagger_object'
|
2
|
+
|
1
3
|
module Swagger
|
2
4
|
module V2
|
3
|
-
|
5
|
+
# Class representing a Swagger "Parameter Object".
|
6
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#parameterObject Parameter Object
|
7
|
+
class Parameter < SwaggerObject
|
8
|
+
# @!group Fixed Fields
|
9
|
+
required_field :name, String
|
10
|
+
# required_field :in, String
|
11
|
+
field :in, String
|
12
|
+
field :description, String
|
13
|
+
field :required, Object # FIXME: Should be a boolean
|
14
|
+
# @!endgroup
|
15
|
+
|
16
|
+
# @!group Body Fields
|
17
|
+
field :schema, Schema
|
18
|
+
# @!endgroup
|
19
|
+
|
20
|
+
# @!group Non-Body Fields
|
21
|
+
field :type, String
|
22
|
+
field :format, String
|
23
|
+
field :items, Hash # TODO: Items Object
|
24
|
+
field :collectionFormat, String
|
25
|
+
field :default, Object
|
26
|
+
# @!endgroup
|
27
|
+
|
28
|
+
# @!group Deterministic JSON Schema
|
29
|
+
field :default, Object
|
30
|
+
field :maximum, Numeric
|
31
|
+
field :exclusiveMaximum, String # FIXME: boolean
|
32
|
+
field :minimum, Numeric
|
33
|
+
field :exclusiveMinimum, String # FIXME: boolean
|
34
|
+
field :maxLength, Integer
|
35
|
+
field :minLength, Integer
|
36
|
+
field :pattern, String
|
37
|
+
field :maxItems, Integer
|
38
|
+
field :minItems, Integer
|
39
|
+
field :uniqueItems, String # FIXME: boolean
|
40
|
+
field :enum, Array[Object]
|
41
|
+
field :multipleOf, Numeric
|
42
|
+
# @!endgroup
|
4
43
|
end
|
5
44
|
end
|
6
45
|
end
|
data/lib/swagger/v2/path.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
|
-
require 'swagger/
|
1
|
+
require 'swagger/swagger_object'
|
2
|
+
require 'swagger/v2/operation'
|
2
3
|
|
3
4
|
module Swagger
|
4
5
|
module V2
|
5
|
-
|
6
|
+
# Class representing a Swagger "Path Item Object".
|
7
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#pathItemObject Path Item Object
|
8
|
+
class Path < SwaggerObject
|
6
9
|
extend Forwardable
|
7
10
|
def_delegator :parent, :host
|
8
11
|
|
9
12
|
VERBS = [:get, :put, :post, :delete, :options, :head, :patch]
|
10
|
-
|
11
|
-
section :parameters, Array[Parameter]
|
12
|
-
|
13
13
|
VERBS.each do | verb |
|
14
|
-
|
14
|
+
field verb, Operation
|
15
15
|
end
|
16
|
+
field :parameters, Array[Parameter]
|
16
17
|
|
17
18
|
def initialize(hash)
|
18
19
|
hash[:parameters] ||= []
|
data/lib/swagger/v2/response.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
|
+
require 'swagger/swagger_object'
|
1
2
|
require 'swagger/v2/example'
|
2
3
|
|
3
4
|
module Swagger
|
4
5
|
module V2
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
# Class representing a Swagger "Response Object".
|
7
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#responseObject Response Object
|
8
|
+
class Response < SwaggerObject
|
9
|
+
field :description, String
|
10
|
+
field :schema, Swagger::Schema
|
11
|
+
field :headers, Array # [String => String] # TODO: Headers
|
12
|
+
field :examples, Hash[Swagger::MimeType => Example]
|
10
13
|
|
11
14
|
def status_code
|
15
|
+
# FIXME: swagger-spec needs a defined way to define codes
|
12
16
|
code = parent.responses.key self
|
13
17
|
code = '200' if code == 'default'
|
14
18
|
code.to_i
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Swagger
|
2
|
+
module V2
|
3
|
+
# Class representing a Swagger "Tag Object".
|
4
|
+
# @see https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md#tagObject Tag Object
|
5
|
+
class Tag < SwaggerObject
|
6
|
+
required_field :name, String
|
7
|
+
field :description, String
|
8
|
+
# TODO: Documentable
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/swagger/version.rb
CHANGED
@@ -62,7 +62,7 @@
|
|
62
62
|
"^x-": {
|
63
63
|
"$ref": "#/definitions/vendorExtension"
|
64
64
|
},
|
65
|
-
"
|
65
|
+
"^\/$|^\/.*[^\/]$": {
|
66
66
|
"$ref": "#/definitions/pathItem"
|
67
67
|
}
|
68
68
|
},
|
@@ -523,7 +523,7 @@
|
|
523
523
|
"^x-": {
|
524
524
|
"$ref": "#/definitions/vendorExtension"
|
525
525
|
},
|
526
|
-
"
|
526
|
+
"^\/$|^\/.*[^\/]$": {
|
527
527
|
"type": "string"
|
528
528
|
}
|
529
529
|
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Swagger
|
4
|
+
describe Builder do
|
5
|
+
subject(:builder) { described_class.builder }
|
6
|
+
describe 'setting fields' do
|
7
|
+
it 'raises an error if the field does not exist' do
|
8
|
+
expect { builder.xxx = 'foo' }.to raise_error(NoMethodError)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'raises if the value cannot be coerced to match the field' do
|
12
|
+
expect { builder.swagger = :foo }.to raise_error(Hashie::CoercionError)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'sets the field' do
|
16
|
+
expect { builder.swagger = 2.0 }.to change { builder.swagger }.from(nil).to(2.0)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'allows complex types to be set via a block' do
|
20
|
+
builder.info do |info|
|
21
|
+
info.title = 'Sample Swagger API'
|
22
|
+
info.version = '1.0'
|
23
|
+
end
|
24
|
+
|
25
|
+
expect(builder.info).to be_an_kind_of(Swagger::Bash)
|
26
|
+
expect(builder.info.title).to eq('Sample Swagger API')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#build' do
|
31
|
+
it 'raises an error if a required field is omitted' do
|
32
|
+
expect { builder.build }.to raise_error(ArgumentError, /property '\w+' is required/)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns the built Swagger API object if all required fields are present' do
|
36
|
+
builder.swagger = 2.0
|
37
|
+
builder.info do |info|
|
38
|
+
info.title = 'Sample Swagger API'
|
39
|
+
info.version = '1.0'
|
40
|
+
end
|
41
|
+
builder.paths = {
|
42
|
+
'/foo' => {}
|
43
|
+
}
|
44
|
+
builder.paths['/foo'].get do |get|
|
45
|
+
get.description = 'Testing...'
|
46
|
+
get.tags = %w(foo bar)
|
47
|
+
end
|
48
|
+
expect(builder.build).to be_an_instance_of(Swagger::V2::API)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Swagger
|
4
4
|
module V2
|
5
|
-
describe
|
5
|
+
describe Operation do
|
6
6
|
context 'Sample petstore API' do
|
7
7
|
let(:swagger_file) { 'spec/fixtures/petstore-full.yaml' }
|
8
8
|
let(:swagger) { Swagger.load swagger_file }
|
@@ -85,6 +85,14 @@ module Swagger
|
|
85
85
|
expect(subject.description).to eq('pet response')
|
86
86
|
end
|
87
87
|
end
|
88
|
+
|
89
|
+
describe '#signature' do
|
90
|
+
subject { swagger.paths['/pets'].get }
|
91
|
+
it 'is a signature similar to what would appear in logs or HTTP dumps' do
|
92
|
+
# FIXME: It's a bit weird to have host but not scheme... but Swagger allows multiple schemes
|
93
|
+
expect(subject.signature).to eq('GET petstore.swagger.wordnik.com/api/pets')
|
94
|
+
end
|
95
|
+
end
|
88
96
|
end
|
89
97
|
end
|
90
98
|
end
|