avro_turf 1.1.0 → 1.2.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: a708d217a7af1550daca801dec09f63eee619c882bf099dc456ce300e2414aee
4
- data.tar.gz: e4fe1fa9f4b8af78f36a2eb59caeb6946aa1ef3a1dc0db20d548d122c8ab7802
3
+ metadata.gz: 8fc2d29f1112e649cc2cd5ec96f2126a0ea5fa8f46ebee01d908cdceeac8da0d
4
+ data.tar.gz: e1989abd9d9b0e5db24f360e96e7bce7ce18e5f488cc794d25a2b3fae6102e66
5
5
  SHA512:
6
- metadata.gz: 5476655050a88e2e38ccedaa9cf3228810802507fa8fb0c6b7b345b2da617577a8f6d015d3d5290630ebf07476fe1aa1b2c3eea3c7de7bcf6969ee511039eff6
7
- data.tar.gz: 7a61be786f276dec7a62385413f89be39188670337d739e2eddfcfc2b2ed7cf8fa73c5148d06a4a4f113cfa7d8627885529d1900b58e7f5ba624b68a8137b1b9
6
+ metadata.gz: f562a66ba746d2c0e4bee8b8f3a0e7fcf65900f261eca444986c8ff6c68231e67aa9c4dc659213721d0d09c65fd9273e12e158938c0773baa48cf86598d6bef9
7
+ data.tar.gz: 53e3640900a3b64038fe063f5dadab2049417490be1833bb5f17124307abe628af64ea0c68b5bdc2bd4ef2f6e58bbfac287696eaf5ece9a35ddb7c2ab8e5127e
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v1.2.0
6
+
7
+ - Expose `fetch_schema`, `fetch_schema_by_id` and `register_schema` schema in `Messaging` interface (#117, #119)
8
+ - Add ability to validate message before encoding in `Messaging#encode` interface (#116, #118)
9
+
5
10
  ## v1.1.0
6
11
 
7
12
  - Compatibility with Avro v1.10.x.
data/README.md CHANGED
@@ -178,6 +178,10 @@ data = avro.encode({ "title" => "hello, world" }, subject: 'greeting', version:
178
178
  # of the same schema version will be served by the cache.
179
179
  data = avro.encode({ "title" => "hello, world" }, schema_id: 2)
180
180
 
181
+ # Message can be validated before encoding to get a description of problem through
182
+ # Avro::SchemaValidator::ValidationError exception
183
+ data = avro.encode({ "titl" => "hello, world" }, schema_name: "greeting", validate: true)
184
+
181
185
  # When decoding, the schema will be fetched from the registry and cached. Subsequent
182
186
  # instances of the same schema id will be served by the cache.
183
187
  avro.decode(data) #=> { "title" => "hello, world" }
@@ -189,6 +193,20 @@ result.message #=> { "title" => "hello, world" }
189
193
  result.schema_id #=> 3
190
194
  result.writer_schema #=> #<Avro::Schema: ...>
191
195
  result.reader_schema #=> nil
196
+
197
+ # You can also work with schema through this interface:
198
+ # Fetch latest schema for subject from registry
199
+ schema, schema_id = avro.fetch_schema(subject: 'greeting')
200
+ # Fetch specific version
201
+ schema, schema_id = avro.fetch_schema(subject: 'greeting', version: 1)
202
+ # Fetch schema by id
203
+ schema, schema_id = avro.fetch_schema_by_id(3)
204
+ # Register schema fetched from store by name
205
+ schema, schema_id = avro.register_schema(schema_name: 'greeting')
206
+ # Specify namespace (same as schema_name: 'somewhere.greeting')
207
+ schema, schema_id = avro.register_schema(schema_name: 'greeting', namespace: 'somewhere')
208
+ # Customize subject under which to register schema
209
+ schema, schema_id = avro.register_schema(schema_name: 'greeting', namespace: 'somewhere', subject: 'test')
192
210
  ```
193
211
 
194
212
  ### Confluent Schema Registry Client
@@ -84,19 +84,26 @@ class AvroTurf
84
84
  # the data. Must match the schema used when encoding (optional).
85
85
  # schema_id - The integer id of the schema that should be used to encode
86
86
  # the data.
87
+ # validate - The boolean for performing complete message validation before
88
+ # encoding it, Avro::SchemaValidator::ValidationError with
89
+ # a descriptive message will be raised in case of invalid message.
87
90
  #
88
91
  # Returns the encoded data as a String.
89
- def encode(message, schema_name: nil, namespace: @namespace, subject: nil, version: nil, schema_id: nil)
90
- schema_id, schema = if schema_id
92
+ def encode(message, schema_name: nil, namespace: @namespace, subject: nil, version: nil, schema_id: nil, validate: false)
93
+ schema, schema_id = if schema_id
91
94
  fetch_schema_by_id(schema_id)
92
95
  elsif subject && version
93
- fetch_schema(subject, version)
96
+ fetch_schema(subject: subject, version: version)
94
97
  elsif schema_name
95
- register_schema(subject, schema_name, namespace)
98
+ register_schema(subject: subject, schema_name: schema_name, namespace: namespace)
96
99
  else
97
100
  raise ArgumentError.new('Neither schema_name nor schema_id nor subject + version provided to determine the schema.')
98
101
  end
99
102
 
103
+ if validate
104
+ Avro::SchemaValidator.validate!(schema, message, recursive: true, encoded: false, fail_on_extra_fields: true)
105
+ end
106
+
100
107
  stream = StringIO.new
101
108
  writer = Avro::IO::DatumWriter.new(schema)
102
109
  encoder = Avro::IO::BinaryEncoder.new(stream)
@@ -169,31 +176,29 @@ class AvroTurf
169
176
  raise SchemaNotFoundError.new("Schema with id: #{schema_id} is not found on registry")
170
177
  end
171
178
 
172
- private
173
-
174
179
  # Providing subject and version to determine the schema,
175
180
  # which skips the auto registeration of schema on the schema registry.
176
181
  # Fetch the schema from registry with the provided subject name and version.
177
- def fetch_schema(subject, version)
182
+ def fetch_schema(subject:, version: 'latest')
178
183
  schema_data = @registry.subject_version(subject, version)
179
184
  schema_id = schema_data.fetch('id')
180
185
  schema = Avro::Schema.parse(schema_data.fetch('schema'))
181
- [schema_id, schema]
186
+ [schema, schema_id]
182
187
  end
183
188
 
184
189
  # Fetch the schema from registry with the provided schema_id.
185
190
  def fetch_schema_by_id(schema_id)
186
191
  schema_json = @registry.fetch(schema_id)
187
192
  schema = Avro::Schema.parse(schema_json)
188
- [schema_id, schema]
193
+ [schema, schema_id]
189
194
  end
190
195
 
191
196
  # Schemas are registered under the full name of the top level Avro record
192
197
  # type, or `subject` if it's provided.
193
- def register_schema(subject, schema_name, namespace)
198
+ def register_schema(schema_name:, subject: nil, namespace: nil)
194
199
  schema = @schema_store.find(schema_name, namespace)
195
200
  schema_id = @registry.register(subject || schema.fullname, schema)
196
- [schema_id, schema]
201
+ [schema, schema_id]
197
202
  end
198
203
  end
199
204
  end
@@ -1,3 +1,3 @@
1
1
  class AvroTurf
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -297,4 +297,106 @@ describe AvroTurf::Messaging do
297
297
  end
298
298
  end
299
299
  end
300
+
301
+ context "validating" do
302
+ subject(:encode){ avro.encode(message, schema_name: "person", validate: true) }
303
+
304
+ context "for correct message" do
305
+ it { expect { encode }.not_to raise_error }
306
+ end
307
+
308
+ context "when message has wrong type" do
309
+ let(:message) { { "full_name" => 123 } }
310
+
311
+ it { expect { encode }.to raise_error(Avro::SchemaValidator::ValidationError, /\.full_name expected type string, got int/) }
312
+ end
313
+
314
+ context "when message contains extra fields (typo in key)" do
315
+ let(:message) { { "fulll_name" => "John Doe" } }
316
+
317
+ it { expect { encode }.to raise_error(Avro::SchemaValidator::ValidationError, /extra field 'fulll_name'/) }
318
+ end
319
+ end
320
+
321
+ context 'fetching and registering schema' do
322
+ let(:schema_store) { AvroTurf::SchemaStore.new(path: "spec/schemas") }
323
+
324
+ let(:registry) { AvroTurf::ConfluentSchemaRegistry.new(registry_url, logger: logger) }
325
+
326
+ let(:avro) do
327
+ AvroTurf::Messaging.new(
328
+ registry: registry,
329
+ schema_store: schema_store,
330
+ logger: logger
331
+ )
332
+ end
333
+
334
+ let(:schema_id) { 234 }
335
+
336
+ context 'using fetch_schema' do
337
+ subject { avro.fetch_schema(subject: subj, version: version) }
338
+
339
+ let(:subj) { 'subject' }
340
+
341
+ let(:version) { 'version' }
342
+
343
+ let(:response) { {'id' => schema_id, 'schema' => schema_json} }
344
+
345
+ before do
346
+ allow(registry).to receive(:subject_version).with(subj, version).and_return(response)
347
+ end
348
+
349
+ it 'gets schema from registry' do
350
+ expect(subject).to eq([schema, schema_id])
351
+ end
352
+ end
353
+
354
+ context 'using fetch_schema_by_id' do
355
+ subject { avro.fetch_schema_by_id(schema_id) }
356
+
357
+ before do
358
+ allow(registry).to receive(:fetch).with(schema_id).and_return(schema_json)
359
+ end
360
+
361
+ it 'gets schema from registry' do
362
+ expect(subject).to eq([schema, schema_id])
363
+ end
364
+ end
365
+
366
+ context 'using register_schema' do
367
+ let(:schema_name) { 'schema_name' }
368
+
369
+ let(:namespace) { 'namespace' }
370
+
371
+ before do
372
+ allow(schema_store).to receive(:find).with(schema_name, namespace).and_return(schema)
373
+ end
374
+
375
+ context 'when subject is not set' do
376
+ subject { avro.register_schema(schema_name: schema_name, namespace: namespace) }
377
+
378
+ before do
379
+ allow(registry).to receive(:register).with(schema.fullname, schema).and_return(schema_id)
380
+ end
381
+
382
+ it 'registers schema in registry' do
383
+ expect(subject).to eq([schema, schema_id])
384
+ end
385
+ end
386
+
387
+ context 'when subject is set' do
388
+ subject { avro.register_schema(schema_name: schema_name, namespace: namespace, subject: subj) }
389
+
390
+ let(:subj) { 'subject' }
391
+
392
+ before do
393
+ allow(registry).to receive(:register).with(subj, schema).and_return(schema_id)
394
+ end
395
+
396
+ it 'registers schema in registry' do
397
+ expect(subject).to eq([schema, schema_id])
398
+ end
399
+ end
400
+ end
401
+ end
300
402
  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.1.0
4
+ version: 1.2.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: 2020-06-30 00:00:00.000000000 Z
11
+ date: 2020-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro