avro_turf 1.8.0 → 1.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 67ff8ee9b578227cd54a33d76a95fed8bd2c9a396aa360ad9df9f52d01e5c25a
4
- data.tar.gz: 8bca24236ce42f19f6db457ad13d76d33e89b18703dc0eb74a8adca53b2c7b6e
3
+ metadata.gz: c0907531d40226b59cd545baf7341f3e41613565d50de98684b266b6b33a081f
4
+ data.tar.gz: e472794d6e7ecbd4a32b1a08f4cc0eb6b0ae810d849483d86ab16a7da929a9fb
5
5
  SHA512:
6
- metadata.gz: 859e8fa6938679007704d35b1b4a54616da7637c83f41c58cbd10828216e0af198846e755334f465613ceb9086ec0c63a823dc2cbf3ddbdc191a97423d2e3147
7
- data.tar.gz: 7900a85abaf9dd55ac3b89bba2d04935ddbafc850a92fb1941ff8984df864cfaa4bb5527ca4d48a74ef3715915d598044b7c486363909f2705ece83b0ffca50f
6
+ metadata.gz: ac1324149cb3299c3e67ffbd287879da88a09c338b967e2098fed63836c9fba5f775623ee6d6ae2ac0ac5e6f6a4ad28ea4c7bb7ece2d1e5979cee2eb379ac43a
7
+ data.tar.gz: 84709d06f4b3d97a23c1f0a1b94f4757c8b7024d17354ec65c7068be8cca52cdeed832096d7ea43f646d3f52d09ebd0f2286375456503d9da14cf6cb7a37772b
@@ -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.10.0
6
+
7
+ - Add `schema_subject_versions` to `ConfluentSchemaRegistry` to retrieve all subject versions for a schema id. (#189)
8
+ - `FakeConfluentSchemaRegistryServer` now returns same id if identical schema is created for a different subject (#188)
9
+
10
+ ## v1.9.0
11
+
12
+ - Send Accept and User-Agent headers on every request (#184)
13
+
5
14
  ## v1.8.0
6
15
 
7
- - add support for `Date` via appropriate logicalType defintion. This is a backwards incompatible change (#177)
16
+ - Add support for `Date` via appropriate logicalType defintion. This is a backwards incompatible change (#177)
8
17
  - Fixed schema file cache truncation on multiple running instances and parallel access to the cache files.
9
18
 
10
19
  ## v1.7.0
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"
@@ -17,7 +17,7 @@ class AvroTurf::CachedConfluentSchemaRegistry
17
17
  end
18
18
 
19
19
  # Delegate the following methods to the upstream
20
- %i(subjects subject_versions check compatible?
20
+ %i(subjects subject_versions schema_subject_versions check compatible?
21
21
  global_config update_global_config subject_config update_subject_config).each do |name|
22
22
  define_method(name) do |*args|
23
23
  instance_variable_get(:@upstream).send(name, *args)
@@ -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,
@@ -68,6 +68,11 @@ class AvroTurf::ConfluentSchemaRegistry
68
68
  get("/subjects/#{subject}/versions/#{version}")
69
69
  end
70
70
 
71
+ # Get the subject and version for a schema id
72
+ def schema_subject_versions(schema_id)
73
+ get("/schemas/ids/#{schema_id}/versions")
74
+ end
75
+
71
76
  # Check if a schema exists. Returns nil if not found.
72
77
  def check(subject, schema)
73
78
  data = post("/subjects/#{subject}",
@@ -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
 
@@ -22,7 +22,7 @@ class AvroTurf::SchemaStore
22
22
  # Still need to check is the schema already loaded
23
23
  return @schemas[fullname] if @schemas.key?(fullname)
24
24
 
25
- load_schema!(fullname)
25
+ load_schema!(fullname, @schemas.dup)
26
26
  end
27
27
  end
28
28
 
@@ -35,23 +35,35 @@ class FakeConfluentSchemaRegistryServer < Sinatra::Base
35
35
 
36
36
  post "/subjects/:subject/versions" do
37
37
  schema = parse_schema
38
- ids_for_subject = SUBJECTS[params[:subject]]
39
-
40
- schemas_for_subject =
41
- SCHEMAS.select
42
- .with_index { |_, i| ids_for_subject.include?(i) }
43
-
44
- if schemas_for_subject.include?(schema)
45
- schema_id = SCHEMAS.index(schema)
46
- else
38
+ schema_id = SCHEMAS.index(schema)
39
+ if schema_id.nil?
47
40
  SCHEMAS << schema
48
41
  schema_id = SCHEMAS.size - 1
49
- SUBJECTS[params[:subject]] = SUBJECTS[params[:subject]] << schema_id
42
+ end
43
+
44
+ subject = params[:subject]
45
+ unless SUBJECTS[subject].include?(schema_id)
46
+ SUBJECTS[subject] = SUBJECTS[subject] << schema_id
50
47
  end
51
48
 
52
49
  { id: schema_id }.to_json
53
50
  end
54
51
 
52
+ get "/schemas/ids/:schema_id/versions" do
53
+ schema_id = params[:schema_id].to_i
54
+ schema = SCHEMAS.at(schema_id)
55
+ halt(404, SCHEMA_NOT_FOUND) unless schema
56
+
57
+ related_subjects = SUBJECTS.select {|_, vs| vs.include? schema_id }
58
+
59
+ related_subjects.map do |subject, versions|
60
+ {
61
+ subject: subject,
62
+ version: versions.find_index(schema_id) + 1
63
+ }
64
+ end.to_json
65
+ end
66
+
55
67
  get "/schemas/ids/:schema_id" do
56
68
  schema = SCHEMAS.at(params[:schema_id].to_i)
57
69
  halt(404, SCHEMA_NOT_FOUND) unless schema
@@ -1,3 +1,3 @@
1
1
  class AvroTurf
2
- VERSION = "1.8.0"
2
+ VERSION = "1.10.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`.
@@ -360,11 +360,42 @@ describe AvroTurf do
360
360
  }
361
361
  AVSC
362
362
 
363
- datum = {
363
+ datum = {
364
364
  message: "hello",
365
365
  sent_date: Date.new(2022, 9, 11)
366
366
  }
367
367
  expect(avro.valid?(datum, schema_name: "postcard")).to eq true
368
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
369
400
  end
370
401
  end
@@ -109,7 +109,7 @@ describe AvroTurf::Messaging do
109
109
  end
110
110
 
111
111
  it 'encodes and decodes messages' do
112
- data = avro.encode(message, schema_id: 1)
112
+ data = avro.encode(message, schema_id: 0)
113
113
  expect(avro.decode(data)).to eq message
114
114
  end
115
115
 
@@ -118,7 +118,7 @@ describe AvroTurf::Messaging do
118
118
  end
119
119
 
120
120
  it 'caches parsed schemas for decoding' do
121
- data = avro.encode(message, schema_id: 1)
121
+ data = avro.encode(message, schema_id: 0)
122
122
  avro.decode(data)
123
123
  allow(Avro::Schema).to receive(:parse).and_call_original
124
124
  expect(avro.decode(data)).to eq message
@@ -361,6 +361,49 @@ describe AvroTurf::SchemaStore do
361
361
  end
362
362
 
363
363
  describe "#load_schemas!" do
364
+ it "do not try to reload known schemas" do
365
+ define_schema "first.avsc", <<-AVSC
366
+ {
367
+ "name": "first",
368
+ "type": "record",
369
+ "fields": [
370
+ {
371
+ "type": "string",
372
+ "name": "a_value"
373
+ }
374
+ ]
375
+ }
376
+ AVSC
377
+
378
+ define_schema "second.avsc", <<-AVSC
379
+ {
380
+ "name": "second",
381
+ "type": "record",
382
+ "fields": [
383
+ {
384
+ "type": "first",
385
+ "name": "full_name"
386
+ }
387
+ ]
388
+ }
389
+ AVSC
390
+
391
+ define_schema "third.avsc", <<-AVSC
392
+ {
393
+ "name": "third",
394
+ "type": "record",
395
+ "fields": [
396
+ {
397
+ "type": "second",
398
+ "name": "embedded_first"
399
+ }
400
+ ]
401
+ }
402
+ AVSC
403
+
404
+ expect { store.load_schemas! }.not_to raise_error
405
+ end
406
+
364
407
  it "loads schemas defined in the `schemas_path` directory" do
365
408
  define_schema "person.avsc", <<-AVSC
366
409
  {
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'bundler/setup'
2
2
  require 'logger'
3
3
  require 'json_spec'
4
+ require 'pp' # Require pp before fakefs to fix TypeError: superclass mismatch for class File
4
5
  require 'fakefs/spec_helpers'
5
6
  require 'avro_turf'
6
7
 
@@ -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
 
@@ -66,6 +77,33 @@ shared_examples_for "a confluent schema registry client" do
66
77
  end
67
78
  end
68
79
 
80
+ describe "#schema_subject_versions" do
81
+ it "returns subject and version for a schema id" do
82
+ schema_id1 = registry.register(subject_name, { type: :record, name: "r1", fields: [] }.to_json)
83
+ registry.register(subject_name, { type: :record, name: "r2", fields: [] }.to_json)
84
+ schema_id2 = registry.register("other#{subject_name}", { type: :record, name: "r2", fields: [] }.to_json)
85
+ expect(registry.schema_subject_versions(schema_id1)).to eq([
86
+ 'subject' => subject_name,
87
+ 'version' => 1
88
+ ])
89
+ expect(registry.schema_subject_versions(schema_id2)).to include({
90
+ 'subject' => subject_name,
91
+ 'version' => 2
92
+ },{
93
+ 'subject' => "other#{subject_name}",
94
+ 'version' => 1
95
+ } )
96
+ end
97
+
98
+ context "when the schema does not exist" do
99
+ it "raises an error" do
100
+ expect do
101
+ registry.schema_subject_versions(-1)
102
+ end.to raise_error(Excon::Errors::NotFound)
103
+ end
104
+ end
105
+ end
106
+
69
107
  describe "#subject_versions" do
70
108
  it "lists all the versions for the subject" do
71
109
  2.times do |n|
@@ -27,14 +27,75 @@ describe FakeConfluentSchemaRegistryServer do
27
27
  expect(JSON.parse(last_response.body).fetch('id')).to eq expected_id
28
28
  end
29
29
 
30
- it 'returns a different schema ID when invoked with same schema and different subject' do
30
+ it 'returns the same schema ID when invoked with same schema and different subject' do
31
31
  post '/subjects/person/versions', { schema: schema }.to_json, 'CONTENT_TYPE' => 'application/vnd.schemaregistry+json'
32
32
 
33
33
  original_id = JSON.parse(last_response.body).fetch('id')
34
34
 
35
35
  post '/subjects/happy-person/versions', { schema: schema }.to_json, 'CONTENT_TYPE' => 'application/vnd.schemaregistry+json'
36
36
 
37
- expect(JSON.parse(last_response.body).fetch('id')).not_to eq original_id
37
+ expect(JSON.parse(last_response.body).fetch('id')).to eq original_id
38
+ end
39
+
40
+ it 'returns a different schema ID when invoked with a different schema' do
41
+ post '/subjects/person/versions', { schema: schema }.to_json, 'CONTENT_TYPE' => 'application/vnd.schemaregistry+json'
42
+
43
+ original_id = JSON.parse(last_response.body).fetch('id')
44
+
45
+ other_schema = {
46
+ type: "record",
47
+ name: "other",
48
+ fields: [
49
+ { name: "name", type: "string" }
50
+ ]
51
+ }.to_json
52
+
53
+ post '/subjects/person/versions', { schema: other_schema }.to_json, 'CONTENT_TYPE' => 'application/vnd.schemaregistry+json'
54
+
55
+ expect(JSON.parse(last_response.body).fetch('id')).to_not eq original_id
56
+ end
57
+ end
58
+
59
+ describe 'GET /schemas/ids/:id/versions' do
60
+ def schema(name:)
61
+ {
62
+ type: "record",
63
+ name: name,
64
+ fields: [
65
+ { name: "name", type: "string" },
66
+ ]
67
+ }.to_json
68
+ end
69
+
70
+ it "returns array containing subjects and versions for given schema id" do
71
+ schema1 = schema(name: "name1")
72
+ schema2 = schema(name: "name2")
73
+
74
+ post "/subjects/cats/versions", { schema: schema1 }.to_json, 'CONTENT_TYPE' => 'application/vnd.schemaregistry+json'
75
+ schema1_id = JSON.parse(last_response.body).fetch('id') # Original cats schema
76
+
77
+ post "/subjects/dogs/versions", { schema: schema2 }.to_json, 'CONTENT_TYPE' => 'application/vnd.schemaregistry+json'
78
+ post "/subjects/cats/versions", { schema: schema2 }.to_json, 'CONTENT_TYPE' => 'application/vnd.schemaregistry+json'
79
+ schema2_id = JSON.parse(last_response.body).fetch('id') # Changed cats schema == Original Dogs schema
80
+
81
+ get "/schemas/ids/#{schema1_id}/versions"
82
+ result = JSON.parse(last_response.body)
83
+
84
+ expect(result).to eq [{
85
+ 'subject' => 'cats',
86
+ 'version' => 1
87
+ }]
88
+
89
+ get "/schemas/ids/#{schema2_id}/versions"
90
+ result = JSON.parse(last_response.body)
91
+
92
+ expect(result).to include( {
93
+ 'subject' => 'cats',
94
+ 'version' => 2
95
+ }, {
96
+ 'subject' => 'dogs',
97
+ 'version' => 1
98
+ })
38
99
  end
39
100
  end
40
101
  end
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.8.0
4
+ version: 1.10.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-11-14 00:00:00.000000000 Z
11
+ date: 2023-08-25 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