avro_turf 1.8.0 → 1.10.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: 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