swagger-core 0.2.0 → 0.2.1
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 +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
|