avro_turf 1.3.1 → 1.5.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/.github/workflows/ruby.yml +5 -6
- data/CHANGELOG.md +14 -0
- data/README.md +6 -2
- data/avro_turf.gemspec +3 -1
- data/lib/avro_turf/confluent_schema_registry.rb +6 -7
- data/lib/avro_turf/messaging.rb +3 -0
- data/lib/avro_turf/schema_store.rb +5 -0
- data/lib/avro_turf/version.rb +1 -1
- data/lib/avro_turf.rb +13 -3
- data/spec/avro_turf_spec.rb +175 -28
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e110697cb451e5315414f8086d32aa6e6c43444a462fcdaa678d1311302fcab8
|
4
|
+
data.tar.gz: a54afac8d7e8cb4a599695825ccb3d666d08ad800ccfe63229f14b6b42b26f11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78ee7cc3c12975d15bc621d9b3a96ed014cc8265536650f365ed80a861a06d53f3e9db9f7d698f71c02485df6f9b371944102404e76cc05e13816e12d312d97c
|
7
|
+
data.tar.gz: ac02799c0f25b88f8c9bfbbd0de48a2b3c39a7744ee1b90c6c87439b5a547db9456a2b3f89a9730303c76ea302159fdce9189f417e6424ecbac45da14682dd0d
|
data/.github/workflows/ruby.yml
CHANGED
@@ -8,15 +8,14 @@ jobs:
|
|
8
8
|
runs-on: ubuntu-latest
|
9
9
|
strategy:
|
10
10
|
matrix:
|
11
|
-
ruby
|
12
|
-
2.3.x, 2.4.x, 2.5.x, 2.6.x, 2.7.x, 3.0.x]
|
11
|
+
ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0]
|
13
12
|
|
14
13
|
steps:
|
15
|
-
- uses: actions/checkout@
|
16
|
-
- name: Set up Ruby
|
17
|
-
uses:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
16
|
+
uses: ruby/setup-ruby@v1
|
18
17
|
with:
|
19
|
-
ruby-version:
|
18
|
+
ruby-version: ${{ matrix.ruby }}
|
20
19
|
- name: Build and test with RSpec
|
21
20
|
run: |
|
22
21
|
gem install bundler
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,20 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## v1.5.0
|
6
|
+
|
7
|
+
- Add CA cert file option (#157)
|
8
|
+
- Add compatibility with Avro v1.11.x.
|
9
|
+
|
10
|
+
## v1.4.1
|
11
|
+
|
12
|
+
- Purge sub-schemas from cache before re-parsing schema (#151)
|
13
|
+
|
14
|
+
## v1.4.0
|
15
|
+
|
16
|
+
- Add support for Ruby 3 (#146)
|
17
|
+
- Add ability to validate message before encoding in `AvroTurf#encode` interface
|
18
|
+
|
5
19
|
## v1.3.1
|
6
20
|
|
7
21
|
- Prevent CachedConfluentSchemaRegistry from caching the 'latest' version (#140)
|
data/README.md
CHANGED
@@ -18,7 +18,7 @@ The aliases for the original names will be removed in a future release.
|
|
18
18
|
|
19
19
|
## Note about finding nested schemas
|
20
20
|
|
21
|
-
As of AvroTurf version 0.
|
21
|
+
As of AvroTurf version 1.0.0, only top-level schemas that have their own .avsc file will be loaded and resolvable by the `AvroTurf::SchemaStore#find` method. This change will likely not affect most users. However, if you use `AvroTurf::SchemaStore#load_schemas!` to pre-cache all your schemas and then rely on `AvroTurf::SchemaStore#find` to access nested schemas that are not defined by their own .avsc files, your code may stop working when you upgrade to v1.0.0.
|
22
22
|
|
23
23
|
As an example, if you have a `person` schema (defined in `my/schemas/contacts/person.avsc`) that defines a nested `address` schema like this:
|
24
24
|
|
@@ -44,7 +44,7 @@ As an example, if you have a `person` schema (defined in `my/schemas/contacts/pe
|
|
44
44
|
]
|
45
45
|
}
|
46
46
|
```
|
47
|
-
...this will no longer work in
|
47
|
+
...this will no longer work in v1.0.0:
|
48
48
|
```ruby
|
49
49
|
store = AvroTurf::SchemaStore.new(path: 'my/schemas')
|
50
50
|
store.load_schemas!
|
@@ -88,6 +88,10 @@ avro.decode(encoded_data, schema_name: "person")
|
|
88
88
|
|
89
89
|
# Encode some data using the named schema.
|
90
90
|
avro.encode({ "name" => "Jane", "age" => 28 }, schema_name: "person")
|
91
|
+
|
92
|
+
# Data can be validated before encoding to get a description of problem through
|
93
|
+
# Avro::SchemaValidator::ValidationError exception
|
94
|
+
avro.encode({ "titl" => "hello, world" }, schema_name: "person", validate: true)
|
91
95
|
```
|
92
96
|
|
93
97
|
### Inter-schema references
|
data/avro_turf.gemspec
CHANGED
@@ -12,12 +12,14 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.homepage = "https://github.com/dasch/avro_turf"
|
13
13
|
spec.license = "MIT"
|
14
14
|
|
15
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
16
|
+
|
15
17
|
spec.files = `git ls-files -z`.split("\x0")
|
16
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
20
|
spec.require_paths = ["lib"]
|
19
21
|
|
20
|
-
spec.add_dependency "avro", ">= 1.7.7", "< 1.
|
22
|
+
spec.add_dependency "avro", ">= 1.7.7", "< 1.12"
|
21
23
|
spec.add_dependency "excon", "~> 0.71"
|
22
24
|
|
23
25
|
spec.add_development_dependency "bundler", "~> 2.0"
|
@@ -9,6 +9,7 @@ class AvroTurf::ConfluentSchemaRegistry
|
|
9
9
|
proxy: nil,
|
10
10
|
user: nil,
|
11
11
|
password: nil,
|
12
|
+
ssl_ca_file: nil,
|
12
13
|
client_cert: nil,
|
13
14
|
client_key: nil,
|
14
15
|
client_key_pass: nil,
|
@@ -25,6 +26,7 @@ class AvroTurf::ConfluentSchemaRegistry
|
|
25
26
|
headers: headers,
|
26
27
|
user: user,
|
27
28
|
password: password,
|
29
|
+
ssl_ca_file: ssl_ca_file,
|
28
30
|
client_cert: client_cert,
|
29
31
|
client_key: client_key,
|
30
32
|
client_key_pass: client_key_pass,
|
@@ -40,9 +42,7 @@ class AvroTurf::ConfluentSchemaRegistry
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def register(subject, schema)
|
43
|
-
data = post("/subjects/#{subject}/versions", body: {
|
44
|
-
schema: schema.to_s
|
45
|
-
}.to_json)
|
45
|
+
data = post("/subjects/#{subject}/versions", body: { schema: schema.to_s }.to_json)
|
46
46
|
|
47
47
|
id = data.fetch("id")
|
48
48
|
|
@@ -82,8 +82,7 @@ class AvroTurf::ConfluentSchemaRegistry
|
|
82
82
|
# http://docs.confluent.io/3.1.2/schema-registry/docs/api.html#compatibility
|
83
83
|
def compatible?(subject, schema, version = 'latest')
|
84
84
|
data = post("/compatibility/subjects/#{subject}/versions/#{version}",
|
85
|
-
expects: [200, 404],
|
86
|
-
body: { schema: schema.to_s }.to_json)
|
85
|
+
expects: [200, 404], body: { schema: schema.to_s }.to_json)
|
87
86
|
data.fetch('is_compatible', false) unless data.has_key?('error_code')
|
88
87
|
end
|
89
88
|
|
@@ -94,7 +93,7 @@ class AvroTurf::ConfluentSchemaRegistry
|
|
94
93
|
|
95
94
|
# Update global config
|
96
95
|
def update_global_config(config)
|
97
|
-
put("/config",
|
96
|
+
put("/config", body: config.to_json)
|
98
97
|
end
|
99
98
|
|
100
99
|
# Get config for subject
|
@@ -104,7 +103,7 @@ class AvroTurf::ConfluentSchemaRegistry
|
|
104
103
|
|
105
104
|
# Update config for subject
|
106
105
|
def update_subject_config(subject, config)
|
107
|
-
put("/config/#{subject}",
|
106
|
+
put("/config/#{subject}", body: config.to_json)
|
108
107
|
end
|
109
108
|
|
110
109
|
private
|
data/lib/avro_turf/messaging.rb
CHANGED
@@ -36,6 +36,7 @@ class AvroTurf
|
|
36
36
|
# proxy - Forward the request via proxy (optional).
|
37
37
|
# user - User for basic auth (optional).
|
38
38
|
# password - Password for basic auth (optional).
|
39
|
+
# ssl_ca_file - Name of file containing CA certificate (optional).
|
39
40
|
# client_cert - Name of file containing client certificate (optional).
|
40
41
|
# client_key - Name of file containing client private key to go with client_cert (optional).
|
41
42
|
# client_key_pass - Password to go with client_key (optional).
|
@@ -51,6 +52,7 @@ class AvroTurf
|
|
51
52
|
proxy: nil,
|
52
53
|
user: nil,
|
53
54
|
password: nil,
|
55
|
+
ssl_ca_file: nil,
|
54
56
|
client_cert: nil,
|
55
57
|
client_key: nil,
|
56
58
|
client_key_pass: nil,
|
@@ -67,6 +69,7 @@ class AvroTurf
|
|
67
69
|
proxy: proxy,
|
68
70
|
user: user,
|
69
71
|
password: password,
|
72
|
+
ssl_ca_file: ssl_ca_file,
|
70
73
|
client_cert: client_cert,
|
71
74
|
client_key: client_key,
|
72
75
|
client_key_pass: client_key_pass,
|
@@ -83,6 +83,11 @@ class AvroTurf::SchemaStore
|
|
83
83
|
# has been resolved and use the now-updated local_schemas_cache to
|
84
84
|
# pick up where we left off.
|
85
85
|
local_schemas_cache.delete(fullname)
|
86
|
+
# Ensure all sub-schemas are cleaned up to avoid conflicts when re-parsing
|
87
|
+
# schema.
|
88
|
+
local_schemas_cache.each do |schema_name, schema|
|
89
|
+
local_schemas_cache.delete(schema_name) unless File.exist?(build_schema_path(schema_name))
|
90
|
+
end
|
86
91
|
load_schema!(fullname, local_schemas_cache)
|
87
92
|
else
|
88
93
|
raise
|
data/lib/avro_turf/version.rb
CHANGED
data/lib/avro_turf.rb
CHANGED
@@ -40,12 +40,15 @@ class AvroTurf
|
|
40
40
|
#
|
41
41
|
# data - The data that should be encoded.
|
42
42
|
# schema_name - The name of a schema in the `schemas_path`.
|
43
|
+
# validate - The boolean for performing complete data validation before
|
44
|
+
# encoding it, Avro::SchemaValidator::ValidationError with
|
45
|
+
# a descriptive message will be raised in case of invalid message.
|
43
46
|
#
|
44
47
|
# Returns a String containing the encoded data.
|
45
|
-
def encode(data, schema_name: nil, namespace: @namespace)
|
48
|
+
def encode(data, schema_name: nil, namespace: @namespace, validate: false)
|
46
49
|
stream = StringIO.new
|
47
50
|
|
48
|
-
encode_to_stream(data, stream: stream, schema_name: schema_name, namespace: namespace)
|
51
|
+
encode_to_stream(data, stream: stream, schema_name: schema_name, namespace: namespace, validate: validate)
|
49
52
|
|
50
53
|
stream.string
|
51
54
|
end
|
@@ -56,12 +59,19 @@ class AvroTurf
|
|
56
59
|
# data - The data that should be encoded.
|
57
60
|
# schema_name - The name of a schema in the `schemas_path`.
|
58
61
|
# stream - An IO object that the encoded data should be written to (optional).
|
62
|
+
# validate - The boolean for performing complete data validation before
|
63
|
+
# encoding it, Avro::SchemaValidator::ValidationError with
|
64
|
+
# a descriptive message will be raised in case of invalid message.
|
59
65
|
#
|
60
66
|
# Returns nothing.
|
61
|
-
def encode_to_stream(data, schema_name: nil, stream: nil, namespace: @namespace)
|
67
|
+
def encode_to_stream(data, schema_name: nil, stream: nil, namespace: @namespace, validate: false)
|
62
68
|
schema = @schema_store.find(schema_name, namespace)
|
63
69
|
writer = Avro::IO::DatumWriter.new(schema)
|
64
70
|
|
71
|
+
if validate
|
72
|
+
Avro::SchemaValidator.validate!(schema, data, recursive: true, encoded: false, fail_on_extra_fields: true)
|
73
|
+
end
|
74
|
+
|
65
75
|
dw = Avro::DataFile::Writer.new(stream, writer, schema, @codec)
|
66
76
|
dw << data.as_avro
|
67
77
|
dw.close
|
data/spec/avro_turf_spec.rb
CHANGED
@@ -6,44 +6,130 @@ describe AvroTurf do
|
|
6
6
|
end
|
7
7
|
|
8
8
|
describe "#encode" do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
9
|
+
context "when using plain schema" do
|
10
|
+
before do
|
11
|
+
define_schema "person.avsc", <<-AVSC
|
12
|
+
{
|
13
|
+
"name": "person",
|
14
|
+
"type": "record",
|
15
|
+
"fields": [
|
16
|
+
{
|
17
|
+
"type": "string",
|
18
|
+
"name": "full_name"
|
19
|
+
}
|
20
|
+
]
|
21
|
+
}
|
22
|
+
AVSC
|
23
|
+
end
|
24
|
+
|
25
|
+
it "encodes data with Avro" do
|
26
|
+
data = {
|
27
|
+
"full_name" => "John Doe"
|
20
28
|
}
|
21
|
-
AVSC
|
22
|
-
end
|
23
29
|
|
24
|
-
|
25
|
-
data = {
|
26
|
-
"full_name" => "John Doe"
|
27
|
-
}
|
30
|
+
encoded_data = avro.encode(data, schema_name: "person")
|
28
31
|
|
29
|
-
|
32
|
+
expect(avro.decode(encoded_data)).to eq(data)
|
33
|
+
end
|
30
34
|
|
31
|
-
|
35
|
+
it "allows specifying a codec that should be used to compress messages" do
|
36
|
+
compressed_avro = AvroTurf.new(schemas_path: "spec/schemas/", codec: "deflate")
|
37
|
+
|
38
|
+
data = {
|
39
|
+
"full_name" => "John Doe" * 100
|
40
|
+
}
|
41
|
+
|
42
|
+
uncompressed_data = avro.encode(data, schema_name: "person")
|
43
|
+
compressed_data = compressed_avro.encode(data, schema_name: "person")
|
44
|
+
|
45
|
+
expect(compressed_data.bytesize).to be < uncompressed_data.bytesize
|
46
|
+
expect(compressed_avro.decode(compressed_data)).to eq(data)
|
47
|
+
end
|
32
48
|
end
|
33
49
|
|
34
|
-
|
35
|
-
|
50
|
+
context 'when using nested schemas' do
|
51
|
+
before do
|
52
|
+
define_schema "post.avsc", <<-AVSC
|
53
|
+
{
|
54
|
+
"name": "post",
|
55
|
+
"type": "record",
|
56
|
+
"fields": [
|
57
|
+
{
|
58
|
+
"name": "tag",
|
59
|
+
"type": {
|
60
|
+
"type": "enum",
|
61
|
+
"name": "tag",
|
62
|
+
"symbols": ["foo", "bar"]
|
63
|
+
}
|
64
|
+
},
|
65
|
+
{
|
66
|
+
"name": "messages",
|
67
|
+
"type": {
|
68
|
+
"type": "array",
|
69
|
+
"items": "message"
|
70
|
+
}
|
71
|
+
},
|
72
|
+
{
|
73
|
+
"name": "status",
|
74
|
+
"type": "publishing_status"
|
75
|
+
}
|
76
|
+
]
|
77
|
+
}
|
78
|
+
AVSC
|
79
|
+
|
80
|
+
define_schema "publishing_status.avsc", <<-AVSC
|
81
|
+
{
|
82
|
+
"name": "publishing_status",
|
83
|
+
"type": "enum",
|
84
|
+
"symbols": ["draft", "published", "archived"]
|
85
|
+
}
|
86
|
+
AVSC
|
36
87
|
|
37
|
-
|
38
|
-
|
39
|
-
|
88
|
+
define_schema "message.avsc", <<-AVSC
|
89
|
+
{
|
90
|
+
"name": "message",
|
91
|
+
"type": "record",
|
92
|
+
"fields": [
|
93
|
+
{
|
94
|
+
"type": "string",
|
95
|
+
"name": "content"
|
96
|
+
},
|
97
|
+
{
|
98
|
+
"name": "label",
|
99
|
+
"type": {
|
100
|
+
"type": "enum",
|
101
|
+
"name": "label",
|
102
|
+
"symbols": ["foo", "bar"]
|
103
|
+
}
|
104
|
+
},
|
105
|
+
{
|
106
|
+
"name": "status",
|
107
|
+
"type": "publishing_status"
|
108
|
+
}
|
109
|
+
]
|
110
|
+
}
|
111
|
+
AVSC
|
112
|
+
end
|
113
|
+
|
114
|
+
it "encodes data with Avro" do
|
115
|
+
data = {
|
116
|
+
"tag" => "foo",
|
117
|
+
"messages" => [
|
118
|
+
{
|
119
|
+
"content" => "hello",
|
120
|
+
"label" => "bar",
|
121
|
+
"status" => "draft"
|
122
|
+
}
|
123
|
+
],
|
124
|
+
"status" => "published"
|
125
|
+
}
|
40
126
|
|
41
|
-
|
42
|
-
compressed_data = compressed_avro.encode(data, schema_name: "person")
|
127
|
+
encoded_data = avro.encode(data, schema_name: "post")
|
43
128
|
|
44
|
-
|
45
|
-
|
129
|
+
expect(avro.decode(encoded_data)).to eq(data)
|
130
|
+
end
|
46
131
|
end
|
132
|
+
|
47
133
|
end
|
48
134
|
|
49
135
|
describe "#decode" do
|
@@ -105,6 +191,67 @@ describe AvroTurf do
|
|
105
191
|
|
106
192
|
expect(avro.decode(stream.string)).to eq "hello"
|
107
193
|
end
|
194
|
+
|
195
|
+
context "validating" do
|
196
|
+
subject(:encode_to_stream) do
|
197
|
+
stream = StringIO.new
|
198
|
+
avro.encode_to_stream(message, stream: stream, schema_name: "message", validate: true)
|
199
|
+
end
|
200
|
+
|
201
|
+
context "with a valid message" do
|
202
|
+
let(:message) { { "full_name" => "John Doe" } }
|
203
|
+
|
204
|
+
it "does not raise any error" do
|
205
|
+
define_schema "message.avsc", <<-AVSC
|
206
|
+
{
|
207
|
+
"name": "message",
|
208
|
+
"type": "record",
|
209
|
+
"fields": [
|
210
|
+
{ "name": "full_name", "type": "string" }
|
211
|
+
]
|
212
|
+
}
|
213
|
+
AVSC
|
214
|
+
|
215
|
+
expect { encode_to_stream }.not_to raise_error
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "when message has wrong type" do
|
220
|
+
let(:message) { { "full_name" => 123 } }
|
221
|
+
|
222
|
+
it "raises Avro::SchemaValidator::ValidationError with a message about type mismatch" do
|
223
|
+
define_schema "message.avsc", <<-AVSC
|
224
|
+
{
|
225
|
+
"name": "message",
|
226
|
+
"type": "record",
|
227
|
+
"fields": [
|
228
|
+
{ "name": "full_name", "type": "string" }
|
229
|
+
]
|
230
|
+
}
|
231
|
+
AVSC
|
232
|
+
|
233
|
+
expect { encode_to_stream }.to raise_error(Avro::SchemaValidator::ValidationError, /\.full_name expected type string, got int/)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "when message contains extra fields (typo in key)" do
|
238
|
+
let(:message) { { "fulll_name" => "John Doe" } }
|
239
|
+
|
240
|
+
it "raises Avro::SchemaValidator::ValidationError with a message about extra field" do
|
241
|
+
define_schema "message.avsc", <<-AVSC
|
242
|
+
{
|
243
|
+
"name": "message",
|
244
|
+
"type": "record",
|
245
|
+
"fields": [
|
246
|
+
{ "name": "full_name", "type": "string" }
|
247
|
+
]
|
248
|
+
}
|
249
|
+
AVSC
|
250
|
+
|
251
|
+
expect { encode_to_stream }.to raise_error(Avro::SchemaValidator::ValidationError, /extra field 'fulll_name'/)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
108
255
|
end
|
109
256
|
|
110
257
|
describe "#decode_stream" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: avro_turf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Schierbeck
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: avro
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: 1.7.7
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '1.
|
22
|
+
version: '1.12'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: 1.7.7
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '1.
|
32
|
+
version: '1.12'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: excon
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -225,7 +225,8 @@ files:
|
|
225
225
|
homepage: https://github.com/dasch/avro_turf
|
226
226
|
licenses:
|
227
227
|
- MIT
|
228
|
-
metadata:
|
228
|
+
metadata:
|
229
|
+
rubygems_mfa_required: 'true'
|
229
230
|
post_install_message: |2
|
230
231
|
|
231
232
|
avro_turf v0.8.0 deprecates the names AvroTurf::SchemaRegistry,
|