service_contract 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +7 -1
- data/CHANGELOG.md +6 -0
- data/lib/service_contract/abstract_parameter.rb +1 -5
- data/lib/service_contract/abstract_type.rb +9 -5
- data/lib/service_contract/assertions.rb +25 -24
- data/lib/service_contract/avro/endpoint.rb +2 -2
- data/lib/service_contract/avro/parameter.rb +1 -7
- data/lib/service_contract/avro/protocol.rb +2 -2
- data/lib/service_contract/avro/type.rb +115 -45
- data/lib/service_contract/avro/views/protocol.slim +8 -5
- data/lib/service_contract/mock.rb +4 -4
- data/lib/service_contract/version.rb +1 -1
- data/test/assertions_test.rb +30 -1
- data/test/sample/2/compiled/social_login.avpr +28 -0
- data/test/sample/2/source/social_login.avdl +16 -0
- data/test/service_test.rb +16 -3
- metadata +8 -3
- data/lib/service_contract/abstract_field.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cda29b086c0922a685bcf1c3ada63c6e5786dd21
|
4
|
+
data.tar.gz: d2471890764bdb42b78ab77107c748e8af0f1599
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b55b44ca63fe651c4de836ed6b37c5bee7df090374e307bfcf86322a46df76c9e525161cbdab9f7be24417d9bb6676185932ba5b66d72d47b4ba7645cb3e79c4
|
7
|
+
data.tar.gz: 990b883ac9c8da2d8a230cd0c2ce53860b526392fc808ef54e221b7e6f86ddca14bfe44ccb3e2f269fb6b93f69dc7a032eda85929821463afdb1698d942da7b6
|
data/.travis.yml
CHANGED
@@ -1,3 +1,9 @@
|
|
1
1
|
env:
|
2
2
|
global:
|
3
|
-
- CODECLIMATE_REPO_TOKEN=2ca11363f82515a172ab992c3e4bacf032fc088c57a9907bcb2bcb9eaa71e344
|
3
|
+
- CODECLIMATE_REPO_TOKEN=2ca11363f82515a172ab992c3e4bacf032fc088c57a9907bcb2bcb9eaa71e344
|
4
|
+
|
5
|
+
rvm:
|
6
|
+
- 2.1.0
|
7
|
+
- 2.2.0
|
8
|
+
|
9
|
+
before_install: gem install bundler
|
data/CHANGELOG.md
ADDED
@@ -12,16 +12,20 @@ module ServiceContract
|
|
12
12
|
[]
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def valid_value?(value)
|
16
|
+
if valid_values.empty?
|
17
|
+
true
|
18
|
+
else
|
19
|
+
valid_values.include?(value)
|
20
|
+
end
|
17
21
|
end
|
18
22
|
|
19
|
-
def
|
20
|
-
|
23
|
+
def valid_values
|
24
|
+
[]
|
21
25
|
end
|
22
26
|
|
23
27
|
def valid_ruby_types
|
24
28
|
[Object]
|
25
29
|
end
|
26
30
|
end
|
27
|
-
end
|
31
|
+
end
|
@@ -6,36 +6,37 @@ module ServiceContract
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def assert_data_matches_type(data, type, allow_nil = true)
|
9
|
-
if
|
10
|
-
|
9
|
+
# Skip out if object is nil and allowed to be nil
|
10
|
+
return true if data.nil? && allow_nil
|
11
|
+
|
12
|
+
# basic type checking
|
13
|
+
assert type.valid_ruby_types.any?{|type| data.is_a?(type) }, "expected #{type.name} to be one of #{type.valid_ruby_types}"
|
14
|
+
assert type.valid_value?(data), "#{data} is not an allowed value of type: #{type.name}"
|
15
|
+
|
16
|
+
# check subtype
|
17
|
+
if type.subtype
|
11
18
|
data.each do |datum|
|
12
19
|
assert_data_matches_type(datum, type.subtype, allow_nil)
|
13
20
|
end
|
14
|
-
|
15
|
-
# Skip out of the complex object is nil and allowed to be nil
|
16
|
-
return true if data.nil? && allow_nil
|
17
|
-
# type should have fields
|
18
|
-
type.fields.each do |field|
|
19
|
-
|
20
|
-
#Does data contain attributes that the contract doesn't specify?
|
21
|
-
data_extra_attrs = (data.keys.map(&:to_sym) - type.fields.map{|n| n.name.to_sym})
|
22
|
-
assert_equal 0, data_extra_attrs.size, "#{type.name} contains attributes not described in contract: #{data_extra_attrs.join(',')}"
|
23
|
-
|
24
|
-
# ensure the field is present
|
25
|
-
value = data.fetch(field.name) do
|
26
|
-
data.fetch(field.name.to_sym) do
|
27
|
-
assert false, "expected #{type.name} to have attribute: #{field.name}"
|
28
|
-
end
|
29
|
-
end
|
21
|
+
end
|
30
22
|
|
31
|
-
|
32
|
-
|
23
|
+
# check subfields
|
24
|
+
type.fields.each do |field|
|
25
|
+
#Does data contain attributes that the contract doesn't specify?
|
26
|
+
data_extra_attrs = (data.keys.map(&:to_sym) - type.fields.map{|n| n.name.to_sym})
|
27
|
+
assert_equal 0, data_extra_attrs.size, "#{type.name} contains attributes not described in contract: #{data_extra_attrs.join(',')}"
|
28
|
+
|
29
|
+
# ensure the field is present
|
30
|
+
value = data.fetch(field.name) do
|
31
|
+
data.fetch(field.name.to_sym) do
|
32
|
+
assert false, "expected #{type.name} to have attribute: #{field.name}"
|
33
|
+
end
|
33
34
|
end
|
34
|
-
|
35
|
-
#
|
36
|
-
|
35
|
+
|
36
|
+
# check the data type
|
37
|
+
assert_data_matches_type(value, field.type, allow_nil)
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
41
|
end
|
41
|
-
end
|
42
|
+
end
|
@@ -62,8 +62,8 @@ module ServiceContract
|
|
62
62
|
return false if parameters.empty?
|
63
63
|
|
64
64
|
first_param_type = parameters.first.type
|
65
|
-
first_param_type.
|
65
|
+
first_param_type.is_a?(RecordType) && first_param_type.name == protocol.main_type
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
69
|
-
end
|
69
|
+
end
|
@@ -9,12 +9,6 @@ module ServiceContract
|
|
9
9
|
Type.build(definition.type)
|
10
10
|
end
|
11
11
|
|
12
|
-
def subtype
|
13
|
-
definition.array? ?
|
14
|
-
definition.type.items :
|
15
|
-
nil
|
16
|
-
end
|
17
|
-
|
18
12
|
def default
|
19
13
|
definition.default
|
20
14
|
end
|
@@ -24,4 +18,4 @@ module ServiceContract
|
|
24
18
|
end
|
25
19
|
end
|
26
20
|
end
|
27
|
-
end
|
21
|
+
end
|
@@ -1,12 +1,8 @@
|
|
1
1
|
module ServiceContract
|
2
2
|
module Avro
|
3
|
-
class
|
3
|
+
class RecordType < AbstractType
|
4
4
|
def name
|
5
|
-
|
6
|
-
"Array(#{subtype.name})" :
|
7
|
-
complex? ?
|
8
|
-
definition.name :
|
9
|
-
definition.type.to_s
|
5
|
+
definition.name
|
10
6
|
end
|
11
7
|
|
12
8
|
def fields
|
@@ -15,66 +11,140 @@ module ServiceContract
|
|
15
11
|
end
|
16
12
|
end
|
17
13
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
def valid_ruby_types
|
15
|
+
[Hash]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ArrayType < AbstractType
|
20
|
+
def name
|
21
|
+
"Array(#{subtype.name})"
|
22
22
|
end
|
23
23
|
|
24
24
|
def subtype
|
25
|
-
return nil unless definition.respond_to?(:items)
|
26
25
|
Type.build(definition.items)
|
27
26
|
end
|
28
27
|
|
29
|
-
def
|
30
|
-
|
28
|
+
def valid_ruby_types
|
29
|
+
[Array]
|
31
30
|
end
|
31
|
+
end
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
class UnionType < AbstractType
|
34
|
+
def name
|
35
|
+
"Union(#{union_types.map(&:name).join(", ")})"
|
35
36
|
end
|
36
37
|
|
37
|
-
def
|
38
|
-
|
38
|
+
def valid_ruby_types
|
39
|
+
union_types.map(&:valid_ruby_types).flatten
|
39
40
|
end
|
40
41
|
|
41
|
-
|
42
|
-
|
42
|
+
protected
|
43
|
+
|
44
|
+
def union_types
|
45
|
+
definition.schemas.map{|schema| Type.build(schema)}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class EnumType < AbstractType
|
50
|
+
def name
|
51
|
+
"Enum(#{definition.name})"
|
43
52
|
end
|
44
53
|
|
45
54
|
def valid_ruby_types
|
46
|
-
|
47
|
-
when "array"
|
48
|
-
[Array]
|
49
|
-
when "int"
|
50
|
-
[Fixnum]
|
51
|
-
when "string"
|
52
|
-
[String]
|
53
|
-
when "float"
|
54
|
-
[Float]
|
55
|
-
when "boolean"
|
56
|
-
[TrueClass, FalseClass]
|
57
|
-
when "null"
|
58
|
-
[NilClass]
|
59
|
-
when "union"
|
60
|
-
union_types.map(&:valid_ruby_types).flatten
|
61
|
-
else # a complex type
|
62
|
-
[Hash]
|
63
|
-
end
|
55
|
+
[String]
|
64
56
|
end
|
65
57
|
|
66
|
-
|
58
|
+
def valid_values
|
59
|
+
definition.symbols.map{|str| str.to_s.downcase}
|
60
|
+
end
|
61
|
+
end
|
67
62
|
|
68
|
-
|
69
|
-
|
63
|
+
class StringType < AbstractType
|
64
|
+
def name
|
65
|
+
"string"
|
66
|
+
end
|
67
|
+
|
68
|
+
def valid_ruby_types
|
69
|
+
[String]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class IntegerType < AbstractType
|
74
|
+
def name
|
75
|
+
"int"
|
76
|
+
end
|
77
|
+
|
78
|
+
def valid_ruby_types
|
79
|
+
[Fixnum]
|
70
80
|
end
|
81
|
+
end
|
71
82
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
type
|
83
|
+
class FloatType < AbstractType
|
84
|
+
def name
|
85
|
+
"float"
|
76
86
|
end
|
77
87
|
|
88
|
+
def valid_ruby_types
|
89
|
+
[Float]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class BooleanType < AbstractType
|
94
|
+
def name
|
95
|
+
"boolean"
|
96
|
+
end
|
97
|
+
|
98
|
+
def valid_ruby_types
|
99
|
+
[TrueClass, FalseClass]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class NullType < AbstractType
|
104
|
+
def name
|
105
|
+
"null"
|
106
|
+
end
|
107
|
+
alias :to_s :name
|
108
|
+
|
109
|
+
def valid_ruby_types
|
110
|
+
[NilClass]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class Type
|
115
|
+
class << self
|
116
|
+
def build(definition)
|
117
|
+
type = type_string(definition)
|
118
|
+
case type
|
119
|
+
when "array"
|
120
|
+
ArrayType.new(definition)
|
121
|
+
when "record"
|
122
|
+
RecordType.new(definition)
|
123
|
+
when "union"
|
124
|
+
UnionType.new(definition)
|
125
|
+
when "enum"
|
126
|
+
EnumType.new(definition)
|
127
|
+
when "string"
|
128
|
+
StringType.new
|
129
|
+
when "int"
|
130
|
+
IntegerType.new
|
131
|
+
when "float"
|
132
|
+
FloatType.new
|
133
|
+
when "boolean"
|
134
|
+
BooleanType.new
|
135
|
+
when "null"
|
136
|
+
NullType.new
|
137
|
+
else
|
138
|
+
raise "unknown type: #{type}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def type_string(definition)
|
143
|
+
type_string = definition.type
|
144
|
+
type_string = type_string.type_sym.to_s if type_string.respond_to?(:type_sym)
|
145
|
+
type_string
|
146
|
+
end
|
147
|
+
end
|
78
148
|
end
|
79
149
|
end
|
80
|
-
end
|
150
|
+
end
|
@@ -25,12 +25,12 @@ a href=to(version.version)
|
|
25
25
|
ul
|
26
26
|
- endpoint.parameters.each do |parameter|
|
27
27
|
li
|
28
|
-
=> parameter.type
|
28
|
+
=> parameter.type.name
|
29
29
|
= parameter.name
|
30
30
|
|
31
31
|
h4 Response
|
32
32
|
p
|
33
|
-
= endpoint.response_type
|
33
|
+
= endpoint.response_type.name
|
34
34
|
|
35
35
|
.col-md-4
|
36
36
|
h2 Types
|
@@ -44,13 +44,16 @@ a href=to(version.version)
|
|
44
44
|
= type.name
|
45
45
|
.panel-collapse.collapse.in id="collapse#{type.name}"
|
46
46
|
.panel-body
|
47
|
+
- type.valid_values.each do |value|
|
48
|
+
p = value
|
47
49
|
- type.fields.each do |field|
|
48
50
|
p
|
49
|
-
|
51
|
+
- if field.doc
|
52
|
+
a.pull-right data-toggle="collapse" href="#collapseDescription#{type.name}#{field.name}"
|
50
53
|
i.glyphicon.glyphicon-info-sign
|
51
|
-
=> field.type
|
54
|
+
=> field.type.name
|
52
55
|
| -
|
53
56
|
=< field.name
|
54
57
|
- if field.doc
|
55
58
|
p.collapse id="collapseDescription#{type.name}#{field.name}"
|
56
|
-
em = field.doc
|
59
|
+
em = field.doc
|
@@ -33,11 +33,11 @@ module ServiceContract
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def mock_value(field)
|
36
|
-
if field.
|
36
|
+
if field.subtype
|
37
37
|
Array.new(3) do
|
38
38
|
mock_value(field.subtype)
|
39
39
|
end
|
40
|
-
elsif field.
|
40
|
+
elsif !field.fields.empty?
|
41
41
|
# recursively mock values
|
42
42
|
mock_fields(field)
|
43
43
|
else
|
@@ -51,8 +51,8 @@ module ServiceContract
|
|
51
51
|
when "boolean", :boolean
|
52
52
|
[true, false].sample
|
53
53
|
end
|
54
|
-
end
|
54
|
+
end
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
58
|
-
end
|
58
|
+
end
|
data/test/assertions_test.rb
CHANGED
@@ -78,4 +78,33 @@ class AssertionsTest < Minitest::Test
|
|
78
78
|
|
79
79
|
end
|
80
80
|
|
81
|
-
|
81
|
+
def test_enum_values
|
82
|
+
service = SampleService.find(2)
|
83
|
+
assert service, "expect to find a service by version"
|
84
|
+
|
85
|
+
protocol = service.protocol("social_login")
|
86
|
+
endpoint = protocol.endpoint("index")
|
87
|
+
|
88
|
+
data = [
|
89
|
+
{token: "sometoken", provider: "facebook"},
|
90
|
+
{token: "anothertoken", provider: "linkedin"}
|
91
|
+
]
|
92
|
+
|
93
|
+
assert_endpoint_response(data, endpoint)
|
94
|
+
|
95
|
+
# test can be nil
|
96
|
+
bad_data = [
|
97
|
+
{token: "sometoken", provider: "bad provider"}
|
98
|
+
]
|
99
|
+
failure_data = nil
|
100
|
+
begin
|
101
|
+
assert_endpoint_response(bad_data, endpoint)
|
102
|
+
rescue Minitest::Assertion => failure
|
103
|
+
failure_data = failure
|
104
|
+
end
|
105
|
+
|
106
|
+
assert !failure_data.nil?
|
107
|
+
assert failure_data.to_s.include?("is not an allowed value"), failure_data
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
{
|
2
|
+
"protocol" : "SocialLogin",
|
3
|
+
"namespace" : "Gnomon",
|
4
|
+
"types" : [ {
|
5
|
+
"type" : "enum",
|
6
|
+
"name" : "SocialNetwork",
|
7
|
+
"symbols" : [ "FACEBOOK", "GOOGLE", "TWITTER", "LINKEDIN" ]
|
8
|
+
}, {
|
9
|
+
"type" : "record",
|
10
|
+
"name" : "Authorization",
|
11
|
+
"fields" : [ {
|
12
|
+
"name" : "token",
|
13
|
+
"type" : "string"
|
14
|
+
}, {
|
15
|
+
"name" : "provider",
|
16
|
+
"type" : "SocialNetwork"
|
17
|
+
} ]
|
18
|
+
} ],
|
19
|
+
"messages" : {
|
20
|
+
"index" : {
|
21
|
+
"request" : [ ],
|
22
|
+
"response" : {
|
23
|
+
"type" : "array",
|
24
|
+
"items" : "Authorization"
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
data/test/service_test.rb
CHANGED
@@ -68,9 +68,22 @@ class ServiceTest < Minitest::Test
|
|
68
68
|
|
69
69
|
field_type = field.type
|
70
70
|
|
71
|
-
assert_equal('
|
72
|
-
assert_equal('null, int, Array(int)', field_type.to_s)
|
71
|
+
assert_equal('Union(null, int, Array(int))', field_type.name)
|
73
72
|
assert_equal([NilClass, Fixnum, Array], field_type.valid_ruby_types)
|
74
73
|
end
|
75
74
|
|
76
|
-
|
75
|
+
def test_enum_types
|
76
|
+
service = SampleService.find(2)
|
77
|
+
protocol = service.protocol('social_login')
|
78
|
+
type = protocol.type('Authorization')
|
79
|
+
|
80
|
+
field = type.fields.detect{|field| field.name == 'provider'}
|
81
|
+
assert(field, 'expected to find a provider field')
|
82
|
+
|
83
|
+
field_type = field.type
|
84
|
+
|
85
|
+
assert_equal('Enum(SocialNetwork)', field_type.name)
|
86
|
+
assert_equal([String], field_type.valid_ruby_types)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: service_contract
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Ching
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: avro
|
@@ -89,13 +89,13 @@ extra_rdoc_files: []
|
|
89
89
|
files:
|
90
90
|
- ".gitignore"
|
91
91
|
- ".travis.yml"
|
92
|
+
- CHANGELOG.md
|
92
93
|
- Gemfile
|
93
94
|
- LICENSE.txt
|
94
95
|
- README.md
|
95
96
|
- Rakefile
|
96
97
|
- lib/service_contract.rb
|
97
98
|
- lib/service_contract/abstract_endpoint.rb
|
98
|
-
- lib/service_contract/abstract_field.rb
|
99
99
|
- lib/service_contract/abstract_parameter.rb
|
100
100
|
- lib/service_contract/abstract_protocol.rb
|
101
101
|
- lib/service_contract/abstract_service.rb
|
@@ -127,7 +127,9 @@ files:
|
|
127
127
|
- test/sample/1/source/location.avdl
|
128
128
|
- test/sample/1/source/sales_region.avdl
|
129
129
|
- test/sample/2/compiled/search_param.avpr
|
130
|
+
- test/sample/2/compiled/social_login.avpr
|
130
131
|
- test/sample/2/source/search_param.avdl
|
132
|
+
- test/sample/2/source/social_login.avdl
|
131
133
|
- test/service_test.rb
|
132
134
|
- test/test_helper.rb
|
133
135
|
homepage: ''
|
@@ -164,6 +166,9 @@ test_files:
|
|
164
166
|
- test/sample/1/source/location.avdl
|
165
167
|
- test/sample/1/source/sales_region.avdl
|
166
168
|
- test/sample/2/compiled/search_param.avpr
|
169
|
+
- test/sample/2/compiled/social_login.avpr
|
167
170
|
- test/sample/2/source/search_param.avdl
|
171
|
+
- test/sample/2/source/social_login.avdl
|
168
172
|
- test/service_test.rb
|
169
173
|
- test/test_helper.rb
|
174
|
+
has_rdoc:
|