avro_turf 1.7.0 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3eea429db02f7f6eba5f13f46b2bf3e82602371ef9306e374c1102561f508675
4
- data.tar.gz: d798ef10b0e674f48ffdcc6bd3b87e59d9447c985d539f2ba01164e689fe4f8a
3
+ metadata.gz: 3cb36f9b1395a1a6210d1178ca0823636f30ad194caf57663e8fb5cc13273d68
4
+ data.tar.gz: 2fb489fe330b39b88ed585d6b622936e573b525c1f4c5c4fe50704b16b11fd39
5
5
  SHA512:
6
- metadata.gz: e7cacb60d4e7890a700ebaba9a0cf1d46606fb01157e0746a0d6a6c900895e9ca1268f0a04dd4d03f5d1a9cec9bf6795d9c5d70acb6884965353c72bb1de4095
7
- data.tar.gz: c4dbd604b66916355a6cba44ec4e7225eb439c2d9bb0682e62414b6ffa3018bb1d2a84b78d943983c99d7af3897a67658db9cb2205f7d92d6c13661ae44ba602
6
+ metadata.gz: be796c4b42daf30a79dd699d23842e61c61e8f8c70b1645f269ec25e4a4f4fb1e87148b0ffd69a68b24382e9abc01048cb3ba082bd3a3dd2162be714ecdcd57b
7
+ data.tar.gz: 332486d45cd611da3906247f4fb55386c7e1617043070dd44fff06b05c50c44237af6dad62f1fd087ca9c67580aadd53fc342f76509b69b67375532b60637bf9
@@ -7,17 +7,17 @@ jobs:
7
7
 
8
8
  runs-on: ubuntu-latest
9
9
  strategy:
10
+ fail-fast: false
10
11
  matrix:
11
- ruby: [2.3, 2.4, 2.5, 2.6, 2.7, 3.0]
12
+ ruby: [2.6, 2.7, "3.0", 3.1, 3.2]
12
13
 
13
14
  steps:
14
- - uses: actions/checkout@v2
15
+ - uses: actions/checkout@v3
15
16
  - name: Set up Ruby ${{ matrix.ruby }}
16
17
  uses: ruby/setup-ruby@v1
17
18
  with:
18
19
  ruby-version: ${{ matrix.ruby }}
20
+ bundler-cache: true
19
21
  - name: Build and test with RSpec
20
22
  run: |
21
- gem install bundler
22
- bundle install --jobs 4 --retry 3
23
23
  bundle exec rspec
data/CHANGELOG.md CHANGED
@@ -2,9 +2,18 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v1.9.0
6
+
7
+ - Send Accept and User-Agent headers on every request (#184)
8
+
9
+ ## v1.8.0
10
+
11
+ - Add support for `Date` via appropriate logicalType defintion. This is a backwards incompatible change (#177)
12
+ - Fixed schema file cache truncation on multiple running instances and parallel access to the cache files.
13
+
5
14
  ## v1.7.0
6
15
 
7
- - Added extra params for the validation message schem before encode (#169)
16
+ - Added extra params for the validation message schema before encode (#169)
8
17
  - Fix infinite retry when loading schema with nested primary type in separate file (#165)
9
18
 
10
19
  ## v1.6.0
@@ -60,7 +69,7 @@
60
69
  ## v0.9.0
61
70
 
62
71
  - Compatibility with Avro v1.9.0 (#94)
63
- - Disable the auto registeration of schema (#95)
72
+ - Disable the auto registration of schema (#95)
64
73
  - abstracted caching from CachedConfluentSchemaRegistry (#74)
65
74
  - Load avro-patches if installed to silence deprecation errors (#85)
66
75
  - Make schema store to be thread safe (#92)
data/avro_turf.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "bundler", "~> 2.0"
26
26
  spec.add_development_dependency "rake", "~> 13.0"
27
27
  spec.add_development_dependency "rspec", "~> 3.2"
28
- spec.add_development_dependency "fakefs", "~> 0.20.0"
28
+ spec.add_development_dependency "fakefs", "< 3"
29
29
  spec.add_development_dependency "webmock"
30
30
  spec.add_development_dependency "sinatra"
31
31
  spec.add_development_dependency "json_spec"
@@ -13,7 +13,7 @@ class AvroTurf::CachedConfluentSchemaRegistry
13
13
  # cache - Optional user provided Cache object that responds to all methods in the AvroTurf::InMemoryCache interface.
14
14
  def initialize(upstream, cache: nil)
15
15
  @upstream = upstream
16
- @cache = cache || AvroTurf::InMemoryCache.new()
16
+ @cache = cache || AvroTurf::InMemoryCache.new
17
17
  end
18
18
 
19
19
  # Delegate the following methods to the upstream
@@ -34,7 +34,7 @@ class AvroTurf::CachedConfluentSchemaRegistry
34
34
 
35
35
  def subject_version(subject, version = 'latest')
36
36
  return @upstream.subject_version(subject, version) if version == 'latest'
37
-
37
+
38
38
  @cache.lookup_by_version(subject, version) ||
39
39
  @cache.store_by_version(subject, version, @upstream.subject_version(subject, version))
40
40
  end
@@ -19,9 +19,9 @@ class AvroTurf::ConfluentSchemaRegistry
19
19
  )
20
20
  @path_prefix = path_prefix
21
21
  @logger = logger
22
- headers = {
22
+ headers = Excon.defaults[:headers].merge(
23
23
  "Content-Type" => CONTENT_TYPE
24
- }
24
+ )
25
25
  headers[:proxy] = proxy unless proxy.nil?
26
26
  @connection = Excon.new(
27
27
  url,
@@ -1,5 +1,5 @@
1
1
  class Date
2
2
  def as_avro
3
- iso8601
3
+ self
4
4
  end
5
5
  end
@@ -1,20 +1,18 @@
1
1
  # A cache for the CachedConfluentSchemaRegistry.
2
2
  # Extends the InMemoryCache to provide a write-thru to disk for persistent cache.
3
- class AvroTurf::DiskCache < AvroTurf::InMemoryCache
3
+ class AvroTurf::DiskCache
4
4
 
5
5
  def initialize(disk_path, logger: Logger.new($stdout))
6
- super()
7
-
8
6
  @logger = logger
9
7
 
10
8
  # load the write-thru cache on startup, if it exists
11
9
  @schemas_by_id_path = File.join(disk_path, 'schemas_by_id.json')
12
10
  hash = read_from_disk_cache(@schemas_by_id_path)
13
- @schemas_by_id = hash if hash
11
+ @schemas_by_id = hash || {}
14
12
 
15
13
  @ids_by_schema_path = File.join(disk_path, 'ids_by_schema.json')
16
14
  hash = read_from_disk_cache(@ids_by_schema_path)
17
- @ids_by_schema = hash if hash
15
+ @ids_by_schema = hash || {}
18
16
 
19
17
  @schemas_by_subject_version_path = File.join(disk_path, 'schemas_by_subject_version.json')
20
18
  @schemas_by_subject_version = {}
@@ -24,15 +22,16 @@ class AvroTurf::DiskCache < AvroTurf::InMemoryCache
24
22
  # the write-thru cache (json) does not store keys in numeric format
25
23
  # so, convert id to a string for caching purposes
26
24
  def lookup_by_id(id)
27
- super(id.to_s)
25
+ @schemas_by_id[id.to_s]
28
26
  end
29
27
 
30
28
  # override to include write-thru cache after storing result from upstream
31
29
  def store_by_id(id, schema)
32
30
  # must return the value from storing the result (i.e. do not return result from file write)
33
- value = super(id.to_s, schema)
34
- File.write(@schemas_by_id_path, JSON.pretty_generate(@schemas_by_id))
35
- return value
31
+ @schemas_by_id[id.to_s] = schema
32
+ write_to_disk_cache(@schemas_by_id_path, @schemas_by_id)
33
+
34
+ schema
36
35
  end
37
36
 
38
37
  # override to use a json serializable cache key
@@ -45,7 +44,8 @@ class AvroTurf::DiskCache < AvroTurf::InMemoryCache
45
44
  def store_by_schema(subject, schema, id)
46
45
  key = "#{subject}#{schema}"
47
46
  @ids_by_schema[key] = id
48
- File.write(@ids_by_schema_path, JSON.pretty_generate(@ids_by_schema))
47
+
48
+ write_to_disk_cache(@ids_by_schema_path, @ids_by_schema)
49
49
  id
50
50
  end
51
51
 
@@ -78,7 +78,7 @@ class AvroTurf::DiskCache < AvroTurf::InMemoryCache
78
78
  hash[key] = schema
79
79
  hash
80
80
  else
81
- {key => schema}
81
+ { key => schema }
82
82
  end
83
83
 
84
84
  write_to_disk_cache(@schemas_by_subject_version_path, hash)
@@ -90,17 +90,27 @@ class AvroTurf::DiskCache < AvroTurf::InMemoryCache
90
90
  # Parse the file from disk, if it exists and is not zero length
91
91
  private def read_from_disk_cache(path)
92
92
  if File.exist?(path)
93
- if File.size(path)!=0
94
- return JSON.parse(File.read(path))
93
+ if File.size(path) != 0
94
+ json_data = File.open(path, 'r') do |file|
95
+ file.flock(File::LOCK_SH)
96
+ file.read
97
+ end
98
+
99
+ return JSON.parse(json_data)
95
100
  else
96
101
  # just log a message if skipping zero length file
97
102
  @logger.warn "skipping JSON.parse of zero length file at #{path}"
98
103
  end
99
104
  end
100
- return nil
105
+
106
+ nil
101
107
  end
102
108
 
103
109
  private def write_to_disk_cache(path, hash)
104
- File.write(path, JSON.pretty_generate(hash))
110
+ # don't use "w" because it truncates the file before lock
111
+ File.open(path, File::RDWR | File::CREAT, 0644) do |file|
112
+ file.flock(File::LOCK_EX)
113
+ file.write(JSON.pretty_generate(hash))
114
+ end
105
115
  end
106
116
  end
@@ -17,12 +17,12 @@ class AvroTurf::InMemoryCache
17
17
  end
18
18
 
19
19
  def lookup_by_schema(subject, schema)
20
- key = [subject, schema.to_s]
20
+ key = [subject, schema]
21
21
  @ids_by_schema[key]
22
22
  end
23
23
 
24
24
  def store_by_schema(subject, schema, id)
25
- key = [subject, schema.to_s]
25
+ key = [subject, schema]
26
26
  @ids_by_schema[key] = id
27
27
  end
28
28
 
@@ -21,8 +21,20 @@ class AvroTurf
21
21
  # 1: https://github.com/confluentinc/schema-registry
22
22
  class Messaging
23
23
  MAGIC_BYTE = [0].pack("C").freeze
24
- DecodedMessage = Struct.new(:schema_id, :writer_schema, :reader_schema, :message)
25
- private_constant(:DecodedMessage)
24
+
25
+ class DecodedMessage
26
+ attr_reader :schema_id
27
+ attr_reader :writer_schema
28
+ attr_reader :reader_schema
29
+ attr_reader :message
30
+
31
+ def initialize(schema_id, writer_schema, reader_schema, message)
32
+ @schema_id = schema_id
33
+ @writer_schema = writer_schema
34
+ @reader_schema = reader_schema
35
+ @message = message
36
+ end
37
+ end
26
38
 
27
39
  # Instantiate a new Messaging instance with the given configuration.
28
40
  #
@@ -34,7 +46,7 @@ class AvroTurf
34
46
  # namespace - The String default schema namespace.
35
47
  # registry_path_prefix - The String URL path prefix used to namespace schema registry requests (optional).
36
48
  # logger - The Logger that should be used to log information (optional).
37
- # proxy - Forward the request via proxy (optional).
49
+ # proxy - Forward the request via proxy (optional).
38
50
  # user - User for basic auth (optional).
39
51
  # password - Password for basic auth (optional).
40
52
  # ssl_ca_file - Name of file containing CA certificate (optional).
@@ -130,7 +142,7 @@ class AvroTurf
130
142
  writer.write(message, encoder)
131
143
 
132
144
  stream.string
133
- rescue Excon::Errors::NotFound
145
+ rescue Excon::Error::NotFound
134
146
  if schema_id
135
147
  raise SchemaNotFoundError.new("Schema with id: #{schema_id} is not found on registry")
136
148
  else
@@ -1,3 +1,3 @@
1
1
  class AvroTurf
2
- VERSION = "1.7.0"
2
+ VERSION = "1.9.0"
3
3
  end
data/lib/avro_turf.rb CHANGED
@@ -121,10 +121,9 @@ class AvroTurf
121
121
  # namespace - The namespace of the Avro schema (optional).
122
122
  #
123
123
  # Returns true if the data is valid, false otherwise.
124
- def valid?(data, schema_name: nil, namespace: @namespace)
124
+ def valid?(data, schema_name: nil, namespace: @namespace, validate_options: {})
125
125
  schema = schema_name && @schema_store.find(schema_name, namespace)
126
-
127
- Avro::Schema.validate(schema, data.as_avro)
126
+ Avro::Schema.validate(schema, data.as_avro, **validate_options)
128
127
  end
129
128
 
130
129
  # Loads all schema definition files in the `schemas_dir`.
@@ -16,6 +16,13 @@ describe AvroTurf do
16
16
  {
17
17
  "type": "string",
18
18
  "name": "full_name"
19
+ },
20
+ {
21
+ "name": "birth_date",
22
+ "type": {
23
+ "type": "int",
24
+ "logicalType": "date"
25
+ }
19
26
  }
20
27
  ]
21
28
  }
@@ -24,7 +31,8 @@ describe AvroTurf do
24
31
 
25
32
  it "encodes data with Avro" do
26
33
  data = {
27
- "full_name" => "John Doe"
34
+ "full_name" => "John Doe",
35
+ "birth_date" => Date.new(1934, 1, 2)
28
36
  }
29
37
 
30
38
  encoded_data = avro.encode(data, schema_name: "person")
@@ -36,7 +44,8 @@ describe AvroTurf do
36
44
  compressed_avro = AvroTurf.new(schemas_path: "spec/schemas/", codec: "deflate")
37
45
 
38
46
  data = {
39
- "full_name" => "John Doe" * 100
47
+ "full_name" => "John Doe" * 100,
48
+ "birth_date" => Date.new(1934, 1, 2)
40
49
  }
41
50
 
42
51
  uncompressed_data = avro.encode(data, schema_name: "person")
@@ -329,5 +338,64 @@ describe AvroTurf do
329
338
  datum = { message: "hello" }
330
339
  expect(avro.valid?(datum, schema_name: "postcard")).to eq true
331
340
  end
341
+
342
+ it "handles logicalType of date in schema" do
343
+ define_schema "postcard.avsc", <<-AVSC
344
+ {
345
+ "name": "postcard",
346
+ "type": "record",
347
+ "fields": [
348
+ {
349
+ "name": "message",
350
+ "type": "string"
351
+ },
352
+ {
353
+ "name": "sent_date",
354
+ "type": {
355
+ "type": "int",
356
+ "logicalType": "date"
357
+ }
358
+ }
359
+ ]
360
+ }
361
+ AVSC
362
+
363
+ datum = {
364
+ message: "hello",
365
+ sent_date: Date.new(2022, 9, 11)
366
+ }
367
+ expect(avro.valid?(datum, schema_name: "postcard")).to eq true
368
+ end
369
+
370
+ context "when message contains extra fields (typo in key)" do
371
+ let(:message) { { "fulll_name" => "John Doe" } }
372
+
373
+ before do
374
+ define_schema "message.avsc", <<-AVSC
375
+ {
376
+ "name": "message",
377
+ "type": "record",
378
+ "fields": [
379
+ { "name": "full_name", "type": "string" }
380
+ ]
381
+ }
382
+ AVSC
383
+ end
384
+
385
+ it "is valid" do
386
+ datum = { "full_name" => "John Doe", "extra" => "extra" }
387
+ expect(avro.valid?(datum, schema_name: "message")).to eq true
388
+ end
389
+
390
+ it "is invalid when passing fail_on_extra_fields" do
391
+ datum = { "full_name" => "John Doe", "extra" => "extra" }
392
+ validate_options = {
393
+ recursive: true,
394
+ encoded: false,
395
+ fail_on_extra_fields: true }
396
+ valid = avro.valid?(datum, schema_name: "message", validate_options: validate_options)
397
+ expect(valid).to eq false
398
+ end
399
+ end
332
400
  end
333
401
  end
@@ -1,6 +1,6 @@
1
1
  describe Date, "#as_avro" do
2
- it "returns an ISO8601 string describing the time" do
2
+ it "returns Date object describing the time" do
3
3
  date = Date.today
4
- expect(date.as_avro).to eq(date.iso8601)
4
+ expect(date.as_avro).to eq(date)
5
5
  end
6
6
  end
data/spec/spec_helper.rb CHANGED
@@ -22,6 +22,16 @@ module Helpers
22
22
  end
23
23
  end
24
24
 
25
+ # gem `fakefs` does not support flock for the file, and require patch
26
+ # https://github.com/fakefs/fakefs/issues/433
27
+ module FakeFS
28
+ class File < StringIO
29
+ def flock(*)
30
+ true
31
+ end
32
+ end
33
+ end
34
+
25
35
  RSpec.configure do |config|
26
36
  config.include FakeFS::SpecHelpers
27
37
  config.include Helpers
@@ -13,9 +13,20 @@ shared_examples_for "a confluent schema registry client" do
13
13
  ]
14
14
  }.to_json
15
15
  end
16
+ let(:headers) do
17
+ {
18
+ 'Accept'=>'*/*',
19
+ 'Content-Type'=> AvroTurf::ConfluentSchemaRegistry::CONTENT_TYPE,
20
+ 'Host'=> "#{URI.parse(registry_url).host}:80",
21
+ 'User-Agent'=> "excon/#{Excon::VERSION}"
22
+ }
23
+ end
16
24
 
17
25
  before do
18
- stub_request(:any, /^#{registry_url}/).to_rack(FakeConfluentSchemaRegistryServer)
26
+ stub_request(:any, /^#{registry_url}/)
27
+ .with(headers: headers)
28
+ .to_rack(FakeConfluentSchemaRegistryServer)
29
+
19
30
  FakeConfluentSchemaRegistryServer.clear
20
31
  end
21
32
 
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.7.0
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Schierbeck
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-25 00:00:00.000000000 Z
11
+ date: 2023-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro
@@ -90,16 +90,16 @@ dependencies:
90
90
  name: fakefs
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - "~>"
93
+ - - "<"
94
94
  - !ruby/object:Gem::Version
95
- version: 0.20.0
95
+ version: '3'
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
- - - "~>"
100
+ - - "<"
101
101
  - !ruby/object:Gem::Version
102
- version: 0.20.0
102
+ version: '3'
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: webmock
105
105
  requirement: !ruby/object:Gem::Requirement
@@ -156,7 +156,7 @@ dependencies:
156
156
  - - ">="
157
157
  - !ruby/object:Gem::Version
158
158
  version: '0'
159
- description:
159
+ description:
160
160
  email:
161
161
  - dasch@zendesk.com
162
162
  executables: []
@@ -251,8 +251,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
251
251
  - !ruby/object:Gem::Version
252
252
  version: '0'
253
253
  requirements: []
254
- rubygems_version: 3.3.3
255
- signing_key:
254
+ rubygems_version: 3.0.3.1
255
+ signing_key:
256
256
  specification_version: 4
257
257
  summary: A library that makes it easier to use the Avro serialization format from
258
258
  Ruby