service_contract 0.3.0 → 0.4.0
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 +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:
|