deimos-ruby 1.4.0.pre.beta7 → 1.5.0.pre.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +140 -58
- data/README.md +38 -11
- data/Rakefile +2 -2
- data/deimos-ruby.gemspec +3 -2
- data/docs/CONFIGURATION.md +1 -0
- data/docs/DATABASE_BACKEND.md +1 -1
- data/lib/deimos/active_record_consumer.rb +11 -12
- data/lib/deimos/active_record_producer.rb +2 -2
- data/lib/deimos/backends/base.rb +32 -0
- data/lib/deimos/backends/db.rb +6 -1
- data/lib/deimos/backends/kafka.rb +1 -1
- data/lib/deimos/backends/kafka_async.rb +1 -1
- data/lib/deimos/backends/test.rb +20 -0
- data/lib/deimos/base_consumer.rb +7 -7
- data/lib/deimos/batch_consumer.rb +0 -1
- data/lib/deimos/config/configuration.rb +4 -0
- data/lib/deimos/consumer.rb +0 -2
- data/lib/deimos/kafka_source.rb +1 -1
- data/lib/deimos/kafka_topic_info.rb +1 -1
- data/lib/deimos/message.rb +7 -7
- data/lib/deimos/producer.rb +10 -12
- data/lib/deimos/schema_backends/avro_base.rb +108 -0
- data/lib/deimos/schema_backends/avro_local.rb +30 -0
- data/lib/deimos/{schema_coercer.rb → schema_backends/avro_schema_coercer.rb} +39 -51
- data/lib/deimos/schema_backends/avro_schema_registry.rb +34 -0
- data/lib/deimos/schema_backends/avro_validation.rb +21 -0
- data/lib/deimos/schema_backends/base.rb +130 -0
- data/lib/deimos/schema_backends/mock.rb +42 -0
- data/lib/deimos/test_helpers.rb +42 -168
- data/lib/deimos/utils/db_producer.rb +5 -0
- data/lib/deimos/version.rb +1 -1
- data/lib/deimos.rb +22 -6
- data/lib/tasks/deimos.rake +1 -1
- data/spec/active_record_consumer_spec.rb +7 -0
- data/spec/{publish_backend_spec.rb → backends/base_spec.rb} +1 -1
- data/spec/backends/db_spec.rb +5 -0
- data/spec/batch_consumer_spec.rb +0 -8
- data/spec/config/configuration_spec.rb +20 -20
- data/spec/consumer_spec.rb +0 -1
- data/spec/deimos_spec.rb +0 -4
- data/spec/kafka_source_spec.rb +8 -0
- data/spec/producer_spec.rb +23 -37
- data/spec/rake_spec.rb +19 -0
- data/spec/schema_backends/avro_base_shared.rb +174 -0
- data/spec/schema_backends/avro_local_spec.rb +32 -0
- data/spec/schema_backends/avro_schema_registry_spec.rb +32 -0
- data/spec/schema_backends/avro_validation_spec.rb +24 -0
- data/spec/schema_backends/base_spec.rb +29 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/utils/db_producer_spec.rb +10 -0
- metadata +56 -33
- data/lib/deimos/avro_data_coder.rb +0 -89
- data/lib/deimos/avro_data_decoder.rb +0 -36
- data/lib/deimos/avro_data_encoder.rb +0 -51
- data/lib/deimos/monkey_patches/schema_store.rb +0 -19
- data/lib/deimos/publish_backend.rb +0 -30
- data/spec/avro_data_decoder_spec.rb +0 -18
- data/spec/avro_data_encoder_spec.rb +0 -37
- data/spec/updateable_schema_store_spec.rb +0 -36
@@ -1,89 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Deimos
|
4
|
-
# Base class for the encoder / decoder classes.
|
5
|
-
class AvroDataCoder
|
6
|
-
attr_accessor :schema, :namespace, :config, :schema_store
|
7
|
-
|
8
|
-
# @param schema [String]
|
9
|
-
# @param namespace [String]
|
10
|
-
# @param schema_store [AvroTurf::SchemaStore]
|
11
|
-
def initialize(schema:, namespace:, schema_store: nil)
|
12
|
-
@schema = schema
|
13
|
-
@namespace = namespace
|
14
|
-
@schema_store = schema_store ||
|
15
|
-
AvroTurf::SchemaStore.new(path: Deimos.config.schema.path)
|
16
|
-
end
|
17
|
-
|
18
|
-
# @param schema [String]
|
19
|
-
# @return [Avro::Schema]
|
20
|
-
def avro_schema(schema=nil)
|
21
|
-
schema ||= @schema
|
22
|
-
@schema_store.find(schema, @namespace)
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
# @return [AvroTurf]
|
28
|
-
def avro_turf
|
29
|
-
@avro_turf ||= AvroTurf.new(
|
30
|
-
schemas_path: Deimos.config.schema.path,
|
31
|
-
schema_store: @schema_store
|
32
|
-
)
|
33
|
-
@avro_turf
|
34
|
-
end
|
35
|
-
|
36
|
-
# @return [AvroTurf::Messaging]
|
37
|
-
def avro_turf_messaging
|
38
|
-
@avro_turf_messaging ||= AvroTurf::Messaging.new(
|
39
|
-
schema_store: @schema_store,
|
40
|
-
registry_url: Deimos.config.schema.registry_url,
|
41
|
-
schemas_path: Deimos.config.schema.path,
|
42
|
-
namespace: @namespace
|
43
|
-
)
|
44
|
-
end
|
45
|
-
|
46
|
-
# Generate a key schema from the given value schema and key ID. This
|
47
|
-
# is used when encoding or decoding keys from an existing value schema.
|
48
|
-
# @param key_id [Symbol]
|
49
|
-
# @return [Hash]
|
50
|
-
def _generate_key_schema(key_id)
|
51
|
-
return @key_schema if @key_schema
|
52
|
-
|
53
|
-
value_schema = @schema_store.find(@schema, @namespace)
|
54
|
-
key_field = value_schema.fields.find { |f| f.name == key_id.to_s }
|
55
|
-
name = _key_schema_name(@schema)
|
56
|
-
@key_schema = {
|
57
|
-
'type' => 'record',
|
58
|
-
'name' => name,
|
59
|
-
'namespace' => @namespace,
|
60
|
-
'doc' => "Key for #{@namespace}.#{@schema}",
|
61
|
-
'fields' => [
|
62
|
-
{
|
63
|
-
'name' => key_id,
|
64
|
-
'type' => key_field.type.type_sym.to_s
|
65
|
-
}
|
66
|
-
]
|
67
|
-
}
|
68
|
-
@schema_store.add_schema(@key_schema)
|
69
|
-
@key_schema
|
70
|
-
end
|
71
|
-
|
72
|
-
# @param value_schema [Hash]
|
73
|
-
# @return [String]
|
74
|
-
def _field_name_from_schema(value_schema)
|
75
|
-
raise "Schema #{@schema} not found!" if value_schema.nil?
|
76
|
-
if value_schema['fields'].nil? || value_schema['fields'].empty?
|
77
|
-
raise "Schema #{@schema} has no fields!"
|
78
|
-
end
|
79
|
-
|
80
|
-
value_schema['fields'][0]['name']
|
81
|
-
end
|
82
|
-
|
83
|
-
# @param schema [String]
|
84
|
-
# @return [String]
|
85
|
-
def _key_schema_name(schema)
|
86
|
-
"#{schema.gsub('-value', '')}_key"
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'avro_turf/messaging'
|
4
|
-
require 'deimos/avro_data_coder'
|
5
|
-
|
6
|
-
module Deimos
|
7
|
-
# Service Object to decode avro messages
|
8
|
-
class AvroDataDecoder < AvroDataCoder
|
9
|
-
# Decode some data.
|
10
|
-
# @param payload [Hash|String]
|
11
|
-
# @param schema [String]
|
12
|
-
# @return [Hash]
|
13
|
-
def decode(payload, schema: nil)
|
14
|
-
schema ||= @schema
|
15
|
-
avro_turf_messaging.decode(payload, schema_name: schema)
|
16
|
-
end
|
17
|
-
|
18
|
-
# Decode against a local schema.
|
19
|
-
# @param payload [Hash]
|
20
|
-
# @param schema [String]
|
21
|
-
# @return [Hash]
|
22
|
-
def decode_local(payload, schema: nil)
|
23
|
-
schema ||= @schema
|
24
|
-
avro_turf.decode(payload, schema_name: schema, namespace: @namespace)
|
25
|
-
end
|
26
|
-
|
27
|
-
# @param payload [String] the encoded key.
|
28
|
-
# @param key_id [String|Symbol]
|
29
|
-
# @return [Object] the decoded key (int/long/string).
|
30
|
-
def decode_key(payload, key_id)
|
31
|
-
key_schema = _generate_key_schema(key_id)
|
32
|
-
field_name = _field_name_from_schema(key_schema)
|
33
|
-
decode(payload, schema: key_schema['name'])[field_name]
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'avro_turf/messaging'
|
4
|
-
require 'deimos/avro_data_coder'
|
5
|
-
|
6
|
-
module Deimos
|
7
|
-
# Service Object to decode Avro messages.
|
8
|
-
class AvroDataEncoder < AvroDataCoder
|
9
|
-
# @param payload [Hash]
|
10
|
-
# @param schema [String]
|
11
|
-
# @return [String]
|
12
|
-
def encode_local(payload, schema: nil)
|
13
|
-
schema ||= @schema
|
14
|
-
Avro::SchemaValidator.validate!(avro_schema(schema), payload,
|
15
|
-
recursive: true,
|
16
|
-
fail_on_extra_fields: true)
|
17
|
-
avro_turf.encode(payload, schema_name: schema, namespace: @namespace)
|
18
|
-
rescue Avro::IO::AvroTypeError
|
19
|
-
# throw a more detailed error
|
20
|
-
value_schema = @schema_store.find(schema, @namespace)
|
21
|
-
Avro::SchemaValidator.validate!(value_schema, payload)
|
22
|
-
end
|
23
|
-
|
24
|
-
# @param payload [Hash]
|
25
|
-
# @param schema [String]
|
26
|
-
# @param topic [String]
|
27
|
-
# @return [String]
|
28
|
-
def encode(payload, schema: nil, topic: nil)
|
29
|
-
schema ||= @schema
|
30
|
-
Avro::SchemaValidator.validate!(avro_schema(schema), payload,
|
31
|
-
recursive: true,
|
32
|
-
fail_on_extra_fields: true)
|
33
|
-
avro_turf_messaging.encode(payload, schema_name: schema, subject: topic)
|
34
|
-
rescue Avro::IO::AvroTypeError
|
35
|
-
# throw a more detailed error
|
36
|
-
schema = @schema_store.find(@schema, @namespace)
|
37
|
-
Avro::SchemaValidator.validate!(schema, payload)
|
38
|
-
end
|
39
|
-
|
40
|
-
# @param key_id [Symbol|String]
|
41
|
-
# @param key [Object]
|
42
|
-
# @param topic [String]
|
43
|
-
# @return [String] the encoded key.
|
44
|
-
def encode_key(key_id, key, topic=nil)
|
45
|
-
key_schema = _generate_key_schema(key_id)
|
46
|
-
field_name = _field_name_from_schema(key_schema)
|
47
|
-
payload = { field_name => key }
|
48
|
-
encode(payload, schema: key_schema['name'], topic: topic)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'avro_turf/schema_store'
|
4
|
-
|
5
|
-
# Allows us to add in-memory schemas to the schema store in
|
6
|
-
# addition to the ones stored in the file system.
|
7
|
-
class AvroTurf::SchemaStore
|
8
|
-
attr_accessor :schemas
|
9
|
-
|
10
|
-
# @param schema_hash [Hash]
|
11
|
-
def add_schema(schema_hash)
|
12
|
-
name = schema_hash['name']
|
13
|
-
namespace = schema_hash['namespace']
|
14
|
-
full_name = Avro::Name.make_fullname(name, namespace)
|
15
|
-
return if @schemas.key?(full_name)
|
16
|
-
|
17
|
-
Avro::Schema.real_parse(schema_hash, @schemas)
|
18
|
-
end
|
19
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Deimos
|
4
|
-
# Abstract class for all publish backends.
|
5
|
-
class PublishBackend
|
6
|
-
class << self
|
7
|
-
# @param producer_class [Class < Deimos::Producer]
|
8
|
-
# @param messages [Array<Deimos::Message>]
|
9
|
-
def publish(producer_class:, messages:)
|
10
|
-
Deimos.config.logger.info(
|
11
|
-
message: 'Publishing messages',
|
12
|
-
topic: producer_class.topic,
|
13
|
-
payloads: messages.map do |message|
|
14
|
-
{
|
15
|
-
payload: message.payload,
|
16
|
-
key: message.key
|
17
|
-
}
|
18
|
-
end
|
19
|
-
)
|
20
|
-
execute(producer_class: producer_class, messages: messages)
|
21
|
-
end
|
22
|
-
|
23
|
-
# @param producer_class [Class < Deimos::Producer]
|
24
|
-
# @param messages [Array<Deimos::Message>]
|
25
|
-
def execute(producer_class:, messages:)
|
26
|
-
raise NotImplementedError
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe Deimos::AvroDataDecoder do
|
4
|
-
|
5
|
-
let(:decoder) do
|
6
|
-
decoder = described_class.new(schema: 'MySchema',
|
7
|
-
namespace: 'com.my-namespace')
|
8
|
-
allow(decoder).to(receive(:decode)) { |payload| payload }
|
9
|
-
decoder
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'should decode a key' do
|
13
|
-
# reset stub from TestHelpers
|
14
|
-
allow(described_class).to receive(:new).and_call_original
|
15
|
-
expect(decoder.decode_key({ 'test_id' => '123' }, 'test_id')).to eq('123')
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'avro_turf/messaging'
|
4
|
-
|
5
|
-
describe Deimos::AvroDataEncoder do
|
6
|
-
|
7
|
-
let(:encoder) do
|
8
|
-
encoder = described_class.new(schema: 'MySchema',
|
9
|
-
namespace: 'com.my-namespace')
|
10
|
-
allow(encoder).to(receive(:encode)) { |payload| payload }
|
11
|
-
encoder
|
12
|
-
end
|
13
|
-
|
14
|
-
specify 'generate_key_schema' do
|
15
|
-
expect_any_instance_of(AvroTurf::SchemaStore).
|
16
|
-
to receive(:add_schema).with(
|
17
|
-
'type' => 'record',
|
18
|
-
'name' => 'MySchema_key',
|
19
|
-
'namespace' => 'com.my-namespace',
|
20
|
-
'doc' => 'Key for com.my-namespace.MySchema',
|
21
|
-
'fields' => [
|
22
|
-
{
|
23
|
-
'name' => 'test_id',
|
24
|
-
'type' => 'string'
|
25
|
-
}
|
26
|
-
]
|
27
|
-
)
|
28
|
-
encoder.send(:_generate_key_schema, 'test_id')
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'should encode a key' do
|
32
|
-
# reset stub from TestHelpers
|
33
|
-
allow(described_class).to receive(:new).and_call_original
|
34
|
-
expect(encoder.encode_key('test_id', '123')).to eq('test_id' => '123')
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe AvroTurf::SchemaStore do
|
4
|
-
|
5
|
-
it 'should add an in-memory schema' do
|
6
|
-
schema_store = described_class.new(path: Deimos.config.schema.path)
|
7
|
-
schema_store.load_schemas!
|
8
|
-
found_schema = schema_store.find('MySchema', 'com.my-namespace').as_json
|
9
|
-
expect(found_schema['name']).to eq('MySchema')
|
10
|
-
expect(found_schema['namespace']).to eq('com.my-namespace')
|
11
|
-
expect(found_schema['fields'].size).to eq(2)
|
12
|
-
expect(found_schema['fields'][0]['type']['type_sym']).to eq('string')
|
13
|
-
expect(found_schema['fields'][0]['name']).to eq('test_id')
|
14
|
-
new_schema = {
|
15
|
-
'namespace' => 'com.my-namespace',
|
16
|
-
'name' => 'MyNewSchema',
|
17
|
-
'type' => 'record',
|
18
|
-
'doc' => 'Test schema',
|
19
|
-
'fields' => [
|
20
|
-
{
|
21
|
-
'name' => 'my_id',
|
22
|
-
'type' => 'int',
|
23
|
-
'doc' => 'test int'
|
24
|
-
}
|
25
|
-
]
|
26
|
-
}
|
27
|
-
schema_store.add_schema(new_schema)
|
28
|
-
found_schema = schema_store.find('MyNewSchema', 'com.my-namespace').
|
29
|
-
as_json
|
30
|
-
expect(found_schema['name']).to eq('MyNewSchema')
|
31
|
-
expect(found_schema['namespace']).to eq('com.my-namespace')
|
32
|
-
expect(found_schema['fields'].size).to eq(1)
|
33
|
-
expect(found_schema['fields'][0]['type']['type_sym']).to eq('int')
|
34
|
-
expect(found_schema['fields'][0]['name']).to eq('my_id')
|
35
|
-
end
|
36
|
-
end
|