skeleton 0.3.3 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +140 -65
- data/Rakefile +10 -3
- data/lib/skeleton.rb +6 -10
- data/lib/skeleton/contact.rb +1 -13
- data/lib/skeleton/error.rb +4 -0
- data/lib/skeleton/graph.rb +56 -0
- data/lib/skeleton/header.rb +2 -63
- data/lib/skeleton/items.rb +34 -0
- data/lib/skeleton/license.rb +1 -12
- data/lib/skeleton/model.rb +13 -17
- data/lib/skeleton/operation.rb +50 -121
- data/lib/skeleton/parameter.rb +31 -88
- data/lib/skeleton/parameters.rb +40 -0
- data/lib/skeleton/path.rb +42 -80
- data/lib/skeleton/presenter.rb +19 -0
- data/lib/skeleton/property.rb +6 -0
- data/lib/skeleton/response.rb +24 -45
- data/lib/skeleton/schema.rb +80 -63
- data/lib/skeleton/scope.rb +18 -0
- data/lib/skeleton/security_scheme.rb +19 -37
- data/lib/skeleton/serializers/options.rb +215 -0
- data/lib/skeleton/serializers/swagger.rb +197 -0
- data/lib/skeleton/structure.rb +92 -138
- data/lib/skeleton/swagger.rb +9 -0
- data/lib/skeleton/tag.rb +11 -16
- data/lib/skeleton/version.rb +1 -1
- data/skeleton.gemspec +2 -0
- data/test/fixtures/json-schema-draft-04.json +150 -0
- data/test/fixtures/schema.json +1482 -0
- data/test/integrations/validate_complex_schema_spec.rb +42 -0
- data/test/skeleton/graph_test.rb +22 -0
- data/test/skeleton/mapper_test.rb +84 -0
- data/test/skeleton/operation_test.rb +11 -0
- data/test/skeleton/parameter_test.rb +34 -0
- data/test/skeleton/parameters_test.rb +9 -0
- data/test/skeleton/path_test.rb +46 -0
- data/test/skeleton/property_test.rb +8 -0
- data/test/skeleton/serializers/options_test.rb +68 -0
- data/test/skeleton/serializers/swagger_test.rb +30 -0
- data/test/support/factories/structure_factory.rb +86 -0
- data/test/support/fixtures.rb +6 -0
- data/test/support/kissmetrics/core_api.rb +542 -0
- data/{spec/spec_helper.rb → test/test_helper.rb} +7 -1
- metadata +73 -25
- data/lib/skeleton/config.rb +0 -37
- data/lib/skeleton/documentation.rb +0 -17
- data/lib/skeleton/example.rb +0 -31
- data/lib/skeleton/headers.rb +0 -50
- data/lib/skeleton/helpers/controller_helpers.rb +0 -25
- data/lib/skeleton/info.rb +0 -40
- data/lib/skeleton/item.rb +0 -99
- data/lib/skeleton/responses.rb +0 -59
- data/lib/skeleton/scopes.rb +0 -24
- data/lib/skeleton/security_definitions.rb +0 -46
- data/lib/skeleton/security_requirement.rb +0 -29
- data/spec/integrations/use_case_spec.rb +0 -131
- data/spec/skeleton/operation_spec.rb +0 -113
- data/spec/skeleton/serializers/contact_spec.rb +0 -30
- data/spec/skeleton/serializers/documentation_spec.rb +0 -23
- data/spec/skeleton/serializers/header_spec.rb +0 -57
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'multi_json'
|
3
|
+
require 'json-schema'
|
4
|
+
|
5
|
+
require 'skeleton/serializers/swagger'
|
6
|
+
|
7
|
+
describe 'Validate complex schemas' do
|
8
|
+
context 'KISSmetrics Core API' do
|
9
|
+
it 'conforms to the Swagger v2.0 Schema' do
|
10
|
+
# JSON Validator will reach out and grab this draft. This is to avoid
|
11
|
+
# the intermittent errors that occur
|
12
|
+
stub_request(:get, "http://json-schema.org/draft-04/schema")
|
13
|
+
.to_return({
|
14
|
+
status: 200,
|
15
|
+
body: Fixtures.read('json-schema-draft-04.json')
|
16
|
+
})
|
17
|
+
|
18
|
+
structure = KISSmetrics::CoreAPI.structure
|
19
|
+
hash = Skeleton::Serializers::Swagger.new(structure).to_h
|
20
|
+
schema = MultiJson.load(Fixtures.read('schema.json'))
|
21
|
+
|
22
|
+
errors = JSON::Validator.fully_validate(schema, hash, :errors_as_objects => true)
|
23
|
+
|
24
|
+
unless errors.empty?
|
25
|
+
messages = [
|
26
|
+
"JSON Validation Failed",
|
27
|
+
"Errors: #{errors.length}"
|
28
|
+
]
|
29
|
+
|
30
|
+
errors.each do |e|
|
31
|
+
messages << ' '.concat(e[:message])
|
32
|
+
messages << ' => '.concat(e[:fragment])
|
33
|
+
messages << ' => '.concat(e[:failed_attribute])
|
34
|
+
messages << ' => '.concat(e[:schema])
|
35
|
+
messages << ''
|
36
|
+
end
|
37
|
+
|
38
|
+
fail(messages.join("\n"))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'skeleton/graph'
|
4
|
+
|
5
|
+
module Skeleton
|
6
|
+
class GraphTest < Minitest::Test
|
7
|
+
def test_integration
|
8
|
+
graph = Skeleton::Graph.new
|
9
|
+
graph.register('Accounts', ['Meta', 'AccountData', 'Link'])
|
10
|
+
graph.register('Meta', 'Error')
|
11
|
+
graph.register('Error', 'Error')
|
12
|
+
graph.register('Accounts', 'AccountData')
|
13
|
+
graph.register('AccountData', 'Contact')
|
14
|
+
graph.register('Accounts', 'Link')
|
15
|
+
|
16
|
+
expected = Set.new(%w(Accounts Meta AccountData Link Contact Error))
|
17
|
+
actual = Set.new
|
18
|
+
graph.each_dependent_for('Accounts') { |n| actual.add(n) }
|
19
|
+
assert_equal(expected, actual)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'skeleton'
|
4
|
+
|
5
|
+
module Skeleton
|
6
|
+
class MapperTest < Minitest::Test
|
7
|
+
|
8
|
+
def structure
|
9
|
+
@structure ||= Skeleton::Structure.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup
|
13
|
+
structure.configure do
|
14
|
+
define_model('Link') do
|
15
|
+
describe('The link object used for api discovery')
|
16
|
+
required(:href, type: 'string')
|
17
|
+
required(:rel, type: 'string')
|
18
|
+
optional(:templated, type: 'boolean')
|
19
|
+
optional(:name, type: 'string')
|
20
|
+
end
|
21
|
+
|
22
|
+
define_model('Error') do
|
23
|
+
describe('Represents an error')
|
24
|
+
required(:message, type: 'string')
|
25
|
+
optional(:field, type: 'string')
|
26
|
+
optional(:errors, type: 'array', items: { ref: 'Error' })
|
27
|
+
end
|
28
|
+
|
29
|
+
define_model('Meta') do
|
30
|
+
describe('The meta information regarding the request')
|
31
|
+
required(:status, type: 'integer')
|
32
|
+
optional(:message, type: 'string')
|
33
|
+
optional(:errors, type: 'array', items: { ref: 'Error' })
|
34
|
+
end
|
35
|
+
|
36
|
+
define_model('ErrorResponse') do
|
37
|
+
describe('The information when there is an issue with a request')
|
38
|
+
required(:meta, ref: 'Meta')
|
39
|
+
required(:links, type: 'array', items: { ref: 'Link' })
|
40
|
+
end
|
41
|
+
|
42
|
+
define_model('Contact') do
|
43
|
+
optional(:name, type: 'string')
|
44
|
+
optional(:country, type: 'string')
|
45
|
+
optional(:phone, type: 'string')
|
46
|
+
end
|
47
|
+
|
48
|
+
define_model('AccountData') do
|
49
|
+
required(:id, type: 'string', format: 'uuid')
|
50
|
+
required(:name, type: 'string')
|
51
|
+
required(:status, type: 'string', enum: %w(active canceled))
|
52
|
+
required(:created_at, type: 'string', format: 'date-time')
|
53
|
+
required(:updated_at, type: 'string', format: 'date-time')
|
54
|
+
required(:contact, ref: 'Contact')
|
55
|
+
optional(:links, type: 'array', items: { ref: 'Link' })
|
56
|
+
end
|
57
|
+
|
58
|
+
define_model('Account') do
|
59
|
+
required(:meta, ref: 'Meta')
|
60
|
+
required(:data, ref: 'AccountData')
|
61
|
+
required(:links, type: 'array', items: { ref: 'Link' })
|
62
|
+
end
|
63
|
+
|
64
|
+
define_model('Accounts') do
|
65
|
+
required(:meta, ref: 'Meta')
|
66
|
+
required(:data, type: 'array', items: { ref: 'AccountData' })
|
67
|
+
required(:links, type: 'array', items: { ref: 'Link' })
|
68
|
+
end
|
69
|
+
|
70
|
+
define_path('/accounts') do
|
71
|
+
get do
|
72
|
+
response(200) do
|
73
|
+
describe 'get a list of accounts'
|
74
|
+
schema(ref: 'Accounts')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end # end setup
|
80
|
+
|
81
|
+
def test_the_mapping
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'skeleton/parameter'
|
4
|
+
|
5
|
+
module Skeleton
|
6
|
+
class ParameterTests < Minitest::Test
|
7
|
+
def parameter
|
8
|
+
@parameter ||= Skeleton::Parameter.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_body?
|
12
|
+
parameter.location = 'body'
|
13
|
+
assert(parameter.body?)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_query?
|
17
|
+
parameter.location = 'query'
|
18
|
+
refute(parameter.body?)
|
19
|
+
assert(parameter.query?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_path?
|
23
|
+
parameter.location = 'path'
|
24
|
+
refute(parameter.body?)
|
25
|
+
assert(parameter.path?)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_header?
|
29
|
+
parameter.location = 'header'
|
30
|
+
refute(parameter.body?)
|
31
|
+
assert(parameter.header?)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'skeleton/path'
|
4
|
+
|
5
|
+
module Skeleton
|
6
|
+
class PathTest < Minitest::Test
|
7
|
+
def setup
|
8
|
+
@path = Skeleton::Path.new
|
9
|
+
end
|
10
|
+
def test_get
|
11
|
+
@path.get do
|
12
|
+
end
|
13
|
+
assert(@path.operations.key?(:get), 'expected the get operation to be present')
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_put
|
17
|
+
@path.put do
|
18
|
+
end
|
19
|
+
assert(@path.operations.key?(:put), 'expected the put operation to be present')
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_post
|
23
|
+
@path.post do
|
24
|
+
end
|
25
|
+
assert(@path.operations.key?(:post), 'expected the post operation to be present')
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_patch
|
29
|
+
@path.patch do
|
30
|
+
end
|
31
|
+
assert(@path.operations.key?(:patch), 'expected the patch operation to be present')
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_delete
|
35
|
+
@path.delete do
|
36
|
+
end
|
37
|
+
assert(@path.operations.key?(:delete), 'expected the delete operation to be present')
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_options
|
41
|
+
@path.options do
|
42
|
+
end
|
43
|
+
assert(@path.operations.key?(:options), 'expected the options operation to be present')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Skeleton
|
4
|
+
module Serializers
|
5
|
+
class OptionsTest < Minitest::Test
|
6
|
+
def structure
|
7
|
+
@structure ||= Skeleton::Structure.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def serializer
|
11
|
+
@serializer ||= Skeleton::Serializers::Options.new(structure, path: '/accounts')
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_deeply_nested_dependencies
|
15
|
+
Factories::StructureFactory.configure_basic_structure(structure)
|
16
|
+
hash = serializer.to_h
|
17
|
+
|
18
|
+
refute_nil(hash[:definitions], 'Expected definitions to be present')
|
19
|
+
refute_nil(hash[:definitions]['Accounts'])
|
20
|
+
refute_nil(hash[:definitions]['AccountData'])
|
21
|
+
refute_nil(hash[:definitions]['Meta'])
|
22
|
+
refute_nil(hash[:definitions]['Error'])
|
23
|
+
refute_nil(hash[:definitions]['Link'])
|
24
|
+
refute_nil(hash[:definitions]['Contact'])
|
25
|
+
refute_nil(hash[:definitions]['ErrorResponse'])
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_empty_structure
|
29
|
+
assert_raises(Skeleton::Error) { serializer.to_h }
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_reference_non_existant_model
|
33
|
+
structure.define_path('/accounts') do
|
34
|
+
get do
|
35
|
+
response(200) do
|
36
|
+
schema(ref: 'Foo')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
structure.define_model('Foo') do
|
41
|
+
property(:bar, type: 'array', items: { ref: 'Bar' })
|
42
|
+
end
|
43
|
+
|
44
|
+
hash = serializer.to_h
|
45
|
+
|
46
|
+
refute_nil(hash[:definitions], 'Expected definitions to be present')
|
47
|
+
refute_nil(hash[:definitions]['Foo'], 'Expected Foo to be present')
|
48
|
+
assert_empty(hash[:definitions]['Bar'], 'Expected Bar to be empty')
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_path_that_does_not_exist
|
52
|
+
structure.define_model('Foo') do
|
53
|
+
property(:bar, type: 'array', items: { ref: 'Bar' })
|
54
|
+
end
|
55
|
+
|
56
|
+
assert_raises(Skeleton::Error) do
|
57
|
+
serializer.to_h
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_path_not_specified
|
62
|
+
assert_raises(Skeleton::Error) do
|
63
|
+
Skeleton::Serializers::Options.new(structure)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Skeleton
|
4
|
+
module Serializers
|
5
|
+
class SwaggerTest < Minitest::Test
|
6
|
+
def structure
|
7
|
+
@structure ||= Skeleton::Structure.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def serializer
|
11
|
+
@serializer ||= Skeleton::Serializers::Swagger.new(structure)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_empty_structure
|
15
|
+
result = serializer.to_h
|
16
|
+
assert_equal('2.0', result[:swagger])
|
17
|
+
refute_nil(result[:info], 'expected the info hash to be present')
|
18
|
+
|
19
|
+
assert(result.key?(:basePath))
|
20
|
+
assert(result.key?(:host))
|
21
|
+
assert_empty(result[:schemes])
|
22
|
+
assert_empty(result[:consumes])
|
23
|
+
assert_empty(result[:produces])
|
24
|
+
assert_empty(result[:tags])
|
25
|
+
assert_empty(result[:paths])
|
26
|
+
assert_empty(result[:definitions])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'skeleton'
|
2
|
+
|
3
|
+
module Factories
|
4
|
+
class StructureFactory
|
5
|
+
def self.build_basic_structure
|
6
|
+
structure = Skeleton::Structure.new
|
7
|
+
configure_basic_structure(structure)
|
8
|
+
structure
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.configure_basic_structure(structure)
|
12
|
+
structure.configure do
|
13
|
+
define_model('Link') do
|
14
|
+
describe('The link object used for api discovery')
|
15
|
+
required(:href, type: 'string')
|
16
|
+
required(:rel, type: 'string')
|
17
|
+
optional(:templated, type: 'boolean')
|
18
|
+
optional(:name, type: 'string')
|
19
|
+
end
|
20
|
+
|
21
|
+
define_model('Error') do
|
22
|
+
describe('Represents an error')
|
23
|
+
required(:message, type: 'string')
|
24
|
+
optional(:field, type: 'string')
|
25
|
+
optional(:errors, type: 'array', items: { ref: 'Error' })
|
26
|
+
end
|
27
|
+
|
28
|
+
define_model('Meta') do
|
29
|
+
describe('The meta information regarding the request')
|
30
|
+
required(:status, type: 'integer')
|
31
|
+
optional(:message, type: 'string')
|
32
|
+
optional(:errors, type: 'array', items: { ref: 'Error' })
|
33
|
+
end
|
34
|
+
|
35
|
+
define_model('ErrorResponse') do
|
36
|
+
describe('The information when there is an issue with a request')
|
37
|
+
required(:meta, ref: 'Meta')
|
38
|
+
required(:links, type: 'array', items: { ref: 'Link' })
|
39
|
+
end
|
40
|
+
|
41
|
+
define_model('Contact') do
|
42
|
+
optional(:name, type: 'string')
|
43
|
+
optional(:country, type: 'string')
|
44
|
+
optional(:phone, type: 'string')
|
45
|
+
end
|
46
|
+
|
47
|
+
define_model('AccountData') do
|
48
|
+
required(:id, type: 'string', format: 'uuid')
|
49
|
+
required(:name, type: 'string')
|
50
|
+
required(:status, type: 'string', enum: %w(active canceled))
|
51
|
+
required(:created_at, type: 'string', format: 'date-time')
|
52
|
+
required(:updated_at, type: 'string', format: 'date-time')
|
53
|
+
required(:contact, ref: 'Contact')
|
54
|
+
optional(:links, type: 'array', items: { ref: 'Link' })
|
55
|
+
end
|
56
|
+
|
57
|
+
define_model('Account') do
|
58
|
+
required(:meta, ref: 'Meta')
|
59
|
+
required(:data, ref: 'AccountData')
|
60
|
+
required(:links, type: 'array', items: { ref: 'Link' })
|
61
|
+
end
|
62
|
+
|
63
|
+
define_model('Accounts') do
|
64
|
+
required(:meta, ref: 'Meta')
|
65
|
+
required(:data, type: 'array', items: { ref: 'AccountData' })
|
66
|
+
required(:links, type: 'array', items: { ref: 'Link' })
|
67
|
+
end
|
68
|
+
|
69
|
+
define_path('/accounts') do
|
70
|
+
get do
|
71
|
+
response(200) do
|
72
|
+
describe 'get a list of accounts'
|
73
|
+
schema(ref: 'Accounts')
|
74
|
+
end
|
75
|
+
response(403) do
|
76
|
+
describe 'access denied'
|
77
|
+
schema(ref: 'ErrorResponse')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
structure
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|