deimos-temp-fork 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +83 -0
  3. data/.gitignore +41 -0
  4. data/.gitmodules +0 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +333 -0
  7. data/.ruby-gemset +1 -0
  8. data/.ruby-version +1 -0
  9. data/CHANGELOG.md +349 -0
  10. data/CODE_OF_CONDUCT.md +77 -0
  11. data/Dockerfile +23 -0
  12. data/Gemfile +6 -0
  13. data/Gemfile.lock +286 -0
  14. data/Guardfile +22 -0
  15. data/LICENSE.md +195 -0
  16. data/README.md +1099 -0
  17. data/Rakefile +13 -0
  18. data/bin/deimos +4 -0
  19. data/deimos-ruby.gemspec +44 -0
  20. data/docker-compose.yml +71 -0
  21. data/docs/ARCHITECTURE.md +140 -0
  22. data/docs/CONFIGURATION.md +236 -0
  23. data/docs/DATABASE_BACKEND.md +147 -0
  24. data/docs/INTEGRATION_TESTS.md +52 -0
  25. data/docs/PULL_REQUEST_TEMPLATE.md +35 -0
  26. data/docs/UPGRADING.md +128 -0
  27. data/lib/deimos-temp-fork.rb +95 -0
  28. data/lib/deimos/active_record_consume/batch_consumption.rb +164 -0
  29. data/lib/deimos/active_record_consume/batch_slicer.rb +27 -0
  30. data/lib/deimos/active_record_consume/message_consumption.rb +79 -0
  31. data/lib/deimos/active_record_consume/schema_model_converter.rb +52 -0
  32. data/lib/deimos/active_record_consumer.rb +67 -0
  33. data/lib/deimos/active_record_producer.rb +87 -0
  34. data/lib/deimos/backends/base.rb +32 -0
  35. data/lib/deimos/backends/db.rb +41 -0
  36. data/lib/deimos/backends/kafka.rb +33 -0
  37. data/lib/deimos/backends/kafka_async.rb +33 -0
  38. data/lib/deimos/backends/test.rb +20 -0
  39. data/lib/deimos/batch_consumer.rb +7 -0
  40. data/lib/deimos/config/configuration.rb +381 -0
  41. data/lib/deimos/config/phobos_config.rb +137 -0
  42. data/lib/deimos/consume/batch_consumption.rb +150 -0
  43. data/lib/deimos/consume/message_consumption.rb +94 -0
  44. data/lib/deimos/consumer.rb +104 -0
  45. data/lib/deimos/instrumentation.rb +76 -0
  46. data/lib/deimos/kafka_message.rb +60 -0
  47. data/lib/deimos/kafka_source.rb +128 -0
  48. data/lib/deimos/kafka_topic_info.rb +102 -0
  49. data/lib/deimos/message.rb +79 -0
  50. data/lib/deimos/metrics/datadog.rb +47 -0
  51. data/lib/deimos/metrics/mock.rb +39 -0
  52. data/lib/deimos/metrics/provider.rb +36 -0
  53. data/lib/deimos/monkey_patches/phobos_cli.rb +35 -0
  54. data/lib/deimos/monkey_patches/phobos_producer.rb +51 -0
  55. data/lib/deimos/poll_info.rb +9 -0
  56. data/lib/deimos/producer.rb +224 -0
  57. data/lib/deimos/railtie.rb +8 -0
  58. data/lib/deimos/schema_backends/avro_base.rb +140 -0
  59. data/lib/deimos/schema_backends/avro_local.rb +30 -0
  60. data/lib/deimos/schema_backends/avro_schema_coercer.rb +119 -0
  61. data/lib/deimos/schema_backends/avro_schema_registry.rb +34 -0
  62. data/lib/deimos/schema_backends/avro_validation.rb +21 -0
  63. data/lib/deimos/schema_backends/base.rb +150 -0
  64. data/lib/deimos/schema_backends/mock.rb +42 -0
  65. data/lib/deimos/shared_config.rb +63 -0
  66. data/lib/deimos/test_helpers.rb +360 -0
  67. data/lib/deimos/tracing/datadog.rb +35 -0
  68. data/lib/deimos/tracing/mock.rb +40 -0
  69. data/lib/deimos/tracing/provider.rb +29 -0
  70. data/lib/deimos/utils/db_poller.rb +150 -0
  71. data/lib/deimos/utils/db_producer.rb +243 -0
  72. data/lib/deimos/utils/deadlock_retry.rb +68 -0
  73. data/lib/deimos/utils/inline_consumer.rb +150 -0
  74. data/lib/deimos/utils/lag_reporter.rb +175 -0
  75. data/lib/deimos/utils/schema_controller_mixin.rb +115 -0
  76. data/lib/deimos/version.rb +5 -0
  77. data/lib/generators/deimos/active_record/templates/migration.rb.tt +28 -0
  78. data/lib/generators/deimos/active_record/templates/model.rb.tt +5 -0
  79. data/lib/generators/deimos/active_record_generator.rb +79 -0
  80. data/lib/generators/deimos/db_backend/templates/migration +25 -0
  81. data/lib/generators/deimos/db_backend/templates/rails3_migration +31 -0
  82. data/lib/generators/deimos/db_backend_generator.rb +48 -0
  83. data/lib/generators/deimos/db_poller/templates/migration +11 -0
  84. data/lib/generators/deimos/db_poller/templates/rails3_migration +16 -0
  85. data/lib/generators/deimos/db_poller_generator.rb +48 -0
  86. data/lib/tasks/deimos.rake +34 -0
  87. data/spec/active_record_batch_consumer_spec.rb +481 -0
  88. data/spec/active_record_consume/batch_slicer_spec.rb +42 -0
  89. data/spec/active_record_consume/schema_model_converter_spec.rb +105 -0
  90. data/spec/active_record_consumer_spec.rb +154 -0
  91. data/spec/active_record_producer_spec.rb +85 -0
  92. data/spec/backends/base_spec.rb +10 -0
  93. data/spec/backends/db_spec.rb +54 -0
  94. data/spec/backends/kafka_async_spec.rb +11 -0
  95. data/spec/backends/kafka_spec.rb +11 -0
  96. data/spec/batch_consumer_spec.rb +256 -0
  97. data/spec/config/configuration_spec.rb +248 -0
  98. data/spec/consumer_spec.rb +209 -0
  99. data/spec/deimos_spec.rb +169 -0
  100. data/spec/generators/active_record_generator_spec.rb +56 -0
  101. data/spec/handlers/my_batch_consumer.rb +10 -0
  102. data/spec/handlers/my_consumer.rb +10 -0
  103. data/spec/kafka_listener_spec.rb +55 -0
  104. data/spec/kafka_source_spec.rb +381 -0
  105. data/spec/kafka_topic_info_spec.rb +111 -0
  106. data/spec/message_spec.rb +19 -0
  107. data/spec/phobos.bad_db.yml +73 -0
  108. data/spec/phobos.yml +77 -0
  109. data/spec/producer_spec.rb +498 -0
  110. data/spec/rake_spec.rb +19 -0
  111. data/spec/schema_backends/avro_base_shared.rb +199 -0
  112. data/spec/schema_backends/avro_local_spec.rb +32 -0
  113. data/spec/schema_backends/avro_schema_registry_spec.rb +32 -0
  114. data/spec/schema_backends/avro_validation_spec.rb +24 -0
  115. data/spec/schema_backends/base_spec.rb +33 -0
  116. data/spec/schemas/com/my-namespace/Generated.avsc +71 -0
  117. data/spec/schemas/com/my-namespace/MyNestedSchema.avsc +62 -0
  118. data/spec/schemas/com/my-namespace/MySchema-key.avsc +13 -0
  119. data/spec/schemas/com/my-namespace/MySchema.avsc +18 -0
  120. data/spec/schemas/com/my-namespace/MySchemaCompound-key.avsc +18 -0
  121. data/spec/schemas/com/my-namespace/MySchemaWithBooleans.avsc +18 -0
  122. data/spec/schemas/com/my-namespace/MySchemaWithDateTimes.avsc +33 -0
  123. data/spec/schemas/com/my-namespace/MySchemaWithId.avsc +28 -0
  124. data/spec/schemas/com/my-namespace/MySchemaWithUniqueId.avsc +32 -0
  125. data/spec/schemas/com/my-namespace/Wibble.avsc +43 -0
  126. data/spec/schemas/com/my-namespace/Widget.avsc +27 -0
  127. data/spec/schemas/com/my-namespace/WidgetTheSecond.avsc +27 -0
  128. data/spec/schemas/com/my-namespace/request/CreateTopic.avsc +11 -0
  129. data/spec/schemas/com/my-namespace/request/Index.avsc +11 -0
  130. data/spec/schemas/com/my-namespace/request/UpdateRequest.avsc +11 -0
  131. data/spec/schemas/com/my-namespace/response/CreateTopic.avsc +11 -0
  132. data/spec/schemas/com/my-namespace/response/Index.avsc +11 -0
  133. data/spec/schemas/com/my-namespace/response/UpdateResponse.avsc +11 -0
  134. data/spec/spec_helper.rb +267 -0
  135. data/spec/utils/db_poller_spec.rb +320 -0
  136. data/spec/utils/db_producer_spec.rb +514 -0
  137. data/spec/utils/deadlock_retry_spec.rb +74 -0
  138. data/spec/utils/inline_consumer_spec.rb +31 -0
  139. data/spec/utils/lag_reporter_spec.rb +76 -0
  140. data/spec/utils/platform_schema_validation_spec.rb +0 -0
  141. data/spec/utils/schema_controller_mixin_spec.rb +84 -0
  142. data/support/deimos-solo.png +0 -0
  143. data/support/deimos-with-name-next.png +0 -0
  144. data/support/deimos-with-name.png +0 -0
  145. data/support/flipp-logo.png +0 -0
  146. metadata +551 -0
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add rake task to Rails.
4
+ class Deimos::Railtie < Rails::Railtie
5
+ rake_tasks do
6
+ load 'tasks/deimos.rake'
7
+ end
8
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require 'avro'
5
+ require 'avro_turf'
6
+ require 'avro_turf/mutable_schema_store'
7
+ require_relative 'avro_schema_coercer'
8
+
9
+ module Deimos
10
+ module SchemaBackends
11
+ # Encode / decode using Avro, either locally or via schema registry.
12
+ class AvroBase < Base
13
+ attr_accessor :schema_store
14
+
15
+ # @override
16
+ def initialize(schema:, namespace:)
17
+ super(schema: schema, namespace: namespace)
18
+ @schema_store = AvroTurf::MutableSchemaStore.new(path: Deimos.config.schema.path)
19
+ end
20
+
21
+ # @override
22
+ def encode_key(key_id, key, topic: nil)
23
+ @key_schema ||= _generate_key_schema(key_id)
24
+ field_name = _field_name_from_schema(@key_schema)
25
+ payload = { field_name => key }
26
+ encode(payload, schema: @key_schema['name'], topic: topic)
27
+ end
28
+
29
+ # @override
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
+
36
+ # :nodoc:
37
+ def sql_type(field)
38
+ type = field.type.type
39
+ return type if %w(array map record).include?(type)
40
+
41
+ if type == :union
42
+ non_null = field.type.schemas.reject { |f| f.type == :null }
43
+ if non_null.size > 1
44
+ warn("WARNING: #{field.name} has more than one non-null type. Picking the first for the SQL type.")
45
+ end
46
+ return non_null.first.type
47
+ end
48
+ return type.to_sym if %w(float boolean).include?(type)
49
+ return :integer if type == 'int'
50
+ return :bigint if type == 'long'
51
+
52
+ if type == 'double'
53
+ warn('Avro `double` type turns into SQL `float` type. Please ensure you have the correct `limit` set.')
54
+ return :float
55
+ end
56
+
57
+ :string
58
+ end
59
+
60
+ # @override
61
+ def coerce_field(field, value)
62
+ AvroSchemaCoercer.new(avro_schema).coerce_type(field.type, value)
63
+ end
64
+
65
+ # @override
66
+ def schema_fields
67
+ avro_schema.fields.map do |field|
68
+ enum_values = field.type.type == 'enum' ? field.type.symbols : []
69
+ SchemaField.new(field.name, field.type, enum_values)
70
+ end
71
+ end
72
+
73
+ # @override
74
+ def validate(payload, schema:)
75
+ Avro::SchemaValidator.validate!(avro_schema(schema), payload,
76
+ recursive: true,
77
+ fail_on_extra_fields: true)
78
+ end
79
+
80
+ # @override
81
+ def self.mock_backend
82
+ :avro_validation
83
+ end
84
+
85
+ # @override
86
+ def self.content_type
87
+ 'avro/binary'
88
+ end
89
+
90
+ private
91
+
92
+ # @param schema [String]
93
+ # @return [Avro::Schema]
94
+ def avro_schema(schema=nil)
95
+ schema ||= @schema
96
+ @schema_store.find(schema, @namespace)
97
+ end
98
+
99
+ # Generate a key schema from the given value schema and key ID. This
100
+ # is used when encoding or decoding keys from an existing value schema.
101
+ # @param key_id [Symbol]
102
+ # @return [Hash]
103
+ def _generate_key_schema(key_id)
104
+ key_field = avro_schema.fields.find { |f| f.name == key_id.to_s }
105
+ name = _key_schema_name(@schema)
106
+ key_schema = {
107
+ 'type' => 'record',
108
+ 'name' => name,
109
+ 'namespace' => @namespace,
110
+ 'doc' => "Key for #{@namespace}.#{@schema} - autogenerated by Deimos",
111
+ 'fields' => [
112
+ {
113
+ 'name' => key_id,
114
+ 'type' => key_field.type.type_sym.to_s
115
+ }
116
+ ]
117
+ }
118
+ @schema_store.add_schema(key_schema)
119
+ key_schema
120
+ end
121
+
122
+ # @param value_schema [Hash]
123
+ # @return [String]
124
+ def _field_name_from_schema(value_schema)
125
+ raise "Schema #{@schema} not found!" if value_schema.nil?
126
+ if value_schema['fields'].nil? || value_schema['fields'].empty?
127
+ raise "Schema #{@schema} has no fields!"
128
+ end
129
+
130
+ value_schema['fields'][0]['name']
131
+ end
132
+
133
+ # @param schema [String]
134
+ # @return [String]
135
+ def _key_schema_name(schema)
136
+ "#{schema}_key"
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'avro_base'
4
+
5
+ module Deimos
6
+ module SchemaBackends
7
+ # Encode / decode using local Avro encoding.
8
+ class AvroLocal < AvroBase
9
+ # @override
10
+ def decode_payload(payload, schema:)
11
+ avro_turf.decode(payload, schema_name: schema, namespace: @namespace)
12
+ end
13
+
14
+ # @override
15
+ def encode_payload(payload, schema: nil, topic: nil)
16
+ avro_turf.encode(payload, schema_name: schema, namespace: @namespace)
17
+ end
18
+
19
+ private
20
+
21
+ # @return [AvroTurf]
22
+ def avro_turf
23
+ @avro_turf ||= AvroTurf.new(
24
+ schemas_path: Deimos.config.schema.path,
25
+ schema_store: @schema_store
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/time'
4
+
5
+ module Deimos
6
+ # Class to coerce values in a payload to match a schema.
7
+ class AvroSchemaCoercer
8
+ # @param schema [Avro::Schema]
9
+ def initialize(schema)
10
+ @schema = schema
11
+ end
12
+
13
+ # Coerce sub-records in a payload to match the schema.
14
+ # @param type [Avro::Schema::UnionSchema]
15
+ # @param val [Object]
16
+ # @return [Object]
17
+ def coerce_union(type, val)
18
+ union_types = type.schemas.map { |s| s.type.to_sym }
19
+ return nil if val.nil? && union_types.include?(:null)
20
+
21
+ schema_type = type.schemas.find { |s| s.type.to_sym != :null }
22
+ coerce_type(schema_type, val)
23
+ end
24
+
25
+ # Coerce sub-records in a payload to match the schema.
26
+ # @param type [Avro::Schema::RecordSchema]
27
+ # @param val [Object]
28
+ # @return [Object]
29
+ def coerce_record(type, val)
30
+ record = val.map do |name, value|
31
+ field = type.fields.find { |f| f.name == name }
32
+ coerce_type(field.type, value)
33
+ end
34
+ val.keys.zip(record).to_h
35
+ end
36
+
37
+ # Coerce values in a payload to match the schema.
38
+ # @param type [Avro::Schema]
39
+ # @param val [Object]
40
+ # @return [Object]
41
+ def coerce_type(type, val)
42
+ int_classes = [Time, ActiveSupport::TimeWithZone]
43
+ field_type = type.type.to_sym
44
+
45
+ case field_type
46
+ when :int, :long
47
+ if %w(timestamp-millis timestamp-micros).include?(type.logical_type)
48
+ val
49
+ elsif val.is_a?(Integer) ||
50
+ _is_integer_string?(val) ||
51
+ int_classes.any? { |klass| val.is_a?(klass) }
52
+ val.to_i
53
+ else
54
+ val # this will fail
55
+ end
56
+ when :float, :double
57
+ if val.is_a?(Numeric) || _is_float_string?(val)
58
+ val.to_f
59
+ else
60
+ val # this will fail
61
+ end
62
+ when :string
63
+ if val.respond_to?(:to_str)
64
+ val.to_s
65
+ elsif _is_to_s_defined?(val)
66
+ val.to_s
67
+ else
68
+ val # this will fail
69
+ end
70
+ when :boolean
71
+ if val.nil? || val == false
72
+ false
73
+ else
74
+ true
75
+ end
76
+ when :union
77
+ coerce_union(type, val)
78
+ when :record
79
+ coerce_record(type, val)
80
+ else
81
+ val
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ # @param val [String]
88
+ # @return [Boolean]
89
+ def _is_integer_string?(val)
90
+ return false unless val.is_a?(String)
91
+
92
+ begin
93
+ true if Integer(val)
94
+ rescue StandardError
95
+ false
96
+ end
97
+ end
98
+
99
+ # @param val [String]
100
+ # @return [Boolean]
101
+ def _is_float_string?(val)
102
+ return false unless val.is_a?(String)
103
+
104
+ begin
105
+ true if Float(val)
106
+ rescue StandardError
107
+ false
108
+ end
109
+ end
110
+
111
+ # @param val [Object]
112
+ # @return [Boolean]
113
+ def _is_to_s_defined?(val)
114
+ return false if val.nil?
115
+
116
+ Object.instance_method(:to_s).bind(val).call != val.to_s
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'avro_base'
4
+ require_relative 'avro_validation'
5
+ require 'avro_turf/messaging'
6
+
7
+ module Deimos
8
+ module SchemaBackends
9
+ # Encode / decode using the Avro schema registry.
10
+ class AvroSchemaRegistry < AvroBase
11
+ # @override
12
+ def decode_payload(payload, schema:)
13
+ avro_turf_messaging.decode(payload, schema_name: schema)
14
+ end
15
+
16
+ # @override
17
+ def encode_payload(payload, schema: nil, topic: nil)
18
+ avro_turf_messaging.encode(payload, schema_name: schema, subject: topic || schema)
19
+ end
20
+
21
+ private
22
+
23
+ # @return [AvroTurf::Messaging]
24
+ def avro_turf_messaging
25
+ @avro_turf_messaging ||= AvroTurf::Messaging.new(
26
+ schema_store: @schema_store,
27
+ registry_url: Deimos.config.schema.registry_url,
28
+ schemas_path: Deimos.config.schema.path,
29
+ namespace: @namespace
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'avro_base'
4
+
5
+ module Deimos
6
+ module SchemaBackends
7
+ # Leave Ruby hashes as is but validate them against the schema.
8
+ # Useful for unit tests.
9
+ class AvroValidation < AvroBase
10
+ # @override
11
+ def decode_payload(payload, schema: nil)
12
+ payload.with_indifferent_access
13
+ end
14
+
15
+ # @override
16
+ def encode_payload(payload, schema: nil, topic: nil)
17
+ payload.with_indifferent_access
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deimos
4
+ # Represents a field in the schema.
5
+ class SchemaField
6
+ attr_accessor :name, :type, :enum_values
7
+
8
+ # @param name [String]
9
+ # @param type [Object]
10
+ # @param enum_values [Array<String>]
11
+ def initialize(name, type, enum_values=[])
12
+ @name = name
13
+ @type = type
14
+ @enum_values = enum_values
15
+ end
16
+ end
17
+
18
+ module SchemaBackends
19
+ # Base class for encoding / decoding.
20
+ class Base
21
+ attr_accessor :schema, :namespace, :key_schema
22
+
23
+ # @param schema [String|Symbol]
24
+ # @param namespace [String]
25
+ def initialize(schema:, namespace: nil)
26
+ @schema = schema
27
+ @namespace = namespace
28
+ end
29
+
30
+ # Encode a payload with a schema. Public method.
31
+ # @param payload [Hash]
32
+ # @param schema [Symbol|String]
33
+ # @param topic [String]
34
+ # @return [String]
35
+ def encode(payload, schema: nil, topic: nil)
36
+ validate(payload, schema: schema || @schema)
37
+ encode_payload(payload, schema: schema || @schema, topic: topic)
38
+ end
39
+
40
+ # Decode a payload with a schema. Public method.
41
+ # @param payload [String]
42
+ # @param schema [Symbol|String]
43
+ # @return [Hash,nil]
44
+ def decode(payload, schema: nil)
45
+ return nil if payload.nil?
46
+ decode_payload(payload, schema: schema || @schema)
47
+ end
48
+
49
+ # Given a hash, coerce its types to our schema. To be defined by subclass.
50
+ # @param payload [Hash]
51
+ # @return [Hash]
52
+ def coerce(payload)
53
+ result = {}
54
+ self.schema_fields.each do |field|
55
+ name = field.name
56
+ next unless payload.key?(name)
57
+
58
+ val = payload[name]
59
+ result[name] = coerce_field(field, val)
60
+ end
61
+ result.with_indifferent_access
62
+ end
63
+
64
+ # Indicate a class which should act as a mocked version of this backend.
65
+ # This class should perform all validations but not actually do any
66
+ # encoding.
67
+ # Note that the "mock" version (e.g. avro_validation) should return
68
+ # its own symbol when this is called, since it may be called multiple
69
+ # times depending on the order of RSpec helpers.
70
+ # @return [Symbol]
71
+ def self.mock_backend
72
+ :mock
73
+ end
74
+
75
+ # The content type to use when encoding / decoding requests over HTTP via ActionController.
76
+ # @return [String]
77
+ def self.content_type
78
+ raise NotImplementedError
79
+ end
80
+
81
+ # Encode a payload. To be defined by subclass.
82
+ # @param payload [Hash]
83
+ # @param schema [Symbol|String]
84
+ # @param topic [String]
85
+ # @return [String]
86
+ def encode_payload(_payload, schema:, topic: nil)
87
+ raise NotImplementedError
88
+ end
89
+
90
+ # Decode a payload. To be defined by subclass.
91
+ # @param payload [String]
92
+ # @param schema [String|Symbol]
93
+ # @return [Hash]
94
+ def decode_payload(_payload, schema:)
95
+ raise NotImplementedError
96
+ end
97
+
98
+ # Validate that a payload matches the schema. To be defined by subclass.
99
+ # @param payload [Hash]
100
+ # @param schema [String|Symbol]
101
+ def validate(_payload, schema:)
102
+ raise NotImplementedError
103
+ end
104
+
105
+ # List of field names belonging to the schema. To be defined by subclass.
106
+ # @return [Array<SchemaField>]
107
+ def schema_fields
108
+ raise NotImplementedError
109
+ end
110
+
111
+ # Given a value and a field definition (as defined by whatever the
112
+ # underlying schema library is), coerce the given value to
113
+ # the given field type.
114
+ # @param field [SchemaField]
115
+ # @param value [Object]
116
+ # @return [Object]
117
+ def coerce_field(_field, _value)
118
+ raise NotImplementedError
119
+ end
120
+
121
+ # Given a field definition, return the SQL type that might be used in
122
+ # ActiveRecord table creation - e.g. for Avro, a `long` type would
123
+ # return `:bigint`. There are also special values that need to be returned:
124
+ # `:array`, `:map` and `:record`, for types representing those structures.
125
+ # `:enum` is also recognized.
126
+ # @param field [SchemaField]
127
+ # @return [Symbol]
128
+ def sql_type(field)
129
+ raise NotImplementedError
130
+ end
131
+
132
+ # Encode a message key. To be defined by subclass.
133
+ # @param key [String|Hash] the value to use as the key.
134
+ # @param key_id [Symbol|String] the field name of the key.
135
+ # @param topic [String]
136
+ # @return [String]
137
+ def encode_key(_key, _key_id, topic: nil)
138
+ raise NotImplementedError
139
+ end
140
+
141
+ # Decode a message key. To be defined by subclass.
142
+ # @param payload [Hash] the message itself.
143
+ # @param key_id [Symbol|String] the field in the message to decode.
144
+ # @return [String]
145
+ def decode_key(_payload, _key_id)
146
+ raise NotImplementedError
147
+ end
148
+ end
149
+ end
150
+ end