deimos-ruby 1.16.1 → 1.16.4
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 +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +6 -0
- data/README.md +22 -0
- data/lib/deimos/active_record_consume/batch_consumption.rb +7 -2
- data/lib/deimos/active_record_consume/batch_slicer.rb +2 -0
- data/lib/deimos/active_record_consume/message_consumption.rb +8 -4
- data/lib/deimos/active_record_consumer.rb +7 -4
- data/lib/deimos/active_record_producer.rb +10 -0
- data/lib/deimos/backends/base.rb +4 -2
- data/lib/deimos/backends/kafka.rb +1 -0
- data/lib/deimos/backends/kafka_async.rb +1 -0
- data/lib/deimos/config/configuration.rb +4 -0
- data/lib/deimos/config/phobos_config.rb +2 -1
- data/lib/deimos/consume/batch_consumption.rb +8 -1
- data/lib/deimos/consume/message_consumption.rb +4 -1
- data/lib/deimos/instrumentation.rb +11 -4
- data/lib/deimos/kafka_message.rb +1 -0
- data/lib/deimos/kafka_source.rb +5 -0
- data/lib/deimos/kafka_topic_info.rb +4 -0
- data/lib/deimos/message.rb +19 -2
- data/lib/deimos/metrics/datadog.rb +2 -1
- data/lib/deimos/metrics/mock.rb +2 -2
- data/lib/deimos/metrics/provider.rb +6 -0
- data/lib/deimos/monkey_patches/phobos_cli.rb +1 -1
- data/lib/deimos/monkey_patches/phobos_producer.rb +1 -0
- data/lib/deimos/producer.rb +12 -6
- data/lib/deimos/schema_backends/base.rb +31 -17
- data/lib/deimos/schema_backends/mock.rb +2 -2
- data/lib/deimos/schema_class/base.rb +9 -5
- data/lib/deimos/schema_class/enum.rb +4 -2
- data/lib/deimos/schema_class/record.rb +12 -6
- data/lib/deimos/shared_config.rb +6 -2
- data/lib/deimos/test_helpers.rb +21 -4
- data/lib/deimos/tracing/datadog.rb +1 -1
- data/lib/deimos/tracing/mock.rb +4 -3
- data/lib/deimos/tracing/provider.rb +5 -0
- data/lib/deimos/utils/db_poller.rb +9 -1
- data/lib/deimos/utils/db_producer.rb +14 -2
- data/lib/deimos/utils/deadlock_retry.rb +3 -0
- data/lib/deimos/utils/inline_consumer.rb +14 -6
- data/lib/deimos/utils/lag_reporter.rb +11 -0
- data/lib/deimos/utils/schema_controller_mixin.rb +8 -0
- data/lib/deimos/version.rb +1 -1
- data/lib/deimos.rb +3 -2
- data/lib/generators/deimos/active_record_generator.rb +1 -1
- data/lib/generators/deimos/db_backend_generator.rb +1 -0
- data/lib/generators/deimos/db_poller_generator.rb +1 -0
- data/lib/generators/deimos/schema_class/templates/schema_record.rb.tt +13 -5
- data/lib/generators/deimos/schema_class_generator.rb +51 -24
- data/rbs_collection.lock.yaml +176 -0
- data/rbs_collection.yaml +15 -0
- data/regenerate_test_schema_classes.rb +68 -0
- data/sig/avro.rbs +14 -0
- data/sig/defs.rbs +1859 -0
- data/sig/fig_tree.rbs +2 -0
- data/spec/active_record_producer_spec.rb +24 -0
- data/spec/generators/schema_class/my_schema_spec.rb +16 -0
- data/spec/generators/schema_class/my_schema_with_circular_reference_spec.rb +1 -1
- data/spec/generators/schema_class/my_schema_with_complex_types_spec.rb +32 -24
- data/spec/producer_spec.rb +11 -11
- data/spec/schemas/{generated.rb → my_namespace/generated.rb} +28 -32
- data/spec/schemas/{my_nested_schema.rb → my_namespace/my_nested_schema.rb} +23 -16
- data/spec/schemas/{my_schema.rb → my_namespace/my_schema.rb} +12 -5
- data/spec/schemas/my_namespace/my_schema_compound_key.rb +41 -0
- data/spec/schemas/my_namespace/my_schema_id_key.rb +36 -0
- data/spec/schemas/{my_schema_key.rb → my_namespace/my_schema_key.rb} +3 -3
- data/spec/schemas/my_namespace/my_schema_with_boolean.rb +41 -0
- data/spec/schemas/{my_schema_with_circular_reference.rb → my_namespace/my_schema_with_circular_reference.rb} +15 -10
- data/spec/schemas/{my_schema_with_complex_type.rb → my_namespace/my_schema_with_complex_type.rb} +34 -49
- data/spec/schemas/my_namespace/my_schema_with_date_time.rb +56 -0
- data/spec/schemas/my_namespace/my_schema_with_id.rb +51 -0
- data/spec/schemas/my_namespace/my_schema_with_unique_id.rb +56 -0
- data/spec/schemas/my_namespace/wibble.rb +76 -0
- data/spec/schemas/my_namespace/widget.rb +56 -0
- data/spec/schemas/my_namespace/widget_the_second.rb +56 -0
- data/spec/schemas/request/create_topic.rb +36 -0
- data/spec/schemas/request/index.rb +36 -0
- data/spec/schemas/request/update_request.rb +36 -0
- data/spec/schemas/response/create_topic.rb +36 -0
- data/spec/schemas/response/index.rb +36 -0
- data/spec/schemas/response/update_response.rb +36 -0
- data/spec/snapshots/consumers-no-nest.snap +93 -86
- data/spec/snapshots/consumers.snap +94 -87
- data/spec/snapshots/consumers_and_producers-no-nest.snap +108 -87
- data/spec/snapshots/consumers_and_producers.snap +109 -88
- data/spec/snapshots/consumers_circular-no-nest.snap +93 -86
- data/spec/snapshots/consumers_circular.snap +94 -87
- data/spec/snapshots/consumers_complex_types-no-nest.snap +93 -86
- data/spec/snapshots/consumers_complex_types.snap +94 -87
- data/spec/snapshots/consumers_nested-no-nest.snap +93 -86
- data/spec/snapshots/consumers_nested.snap +94 -87
- data/spec/snapshots/namespace_folders.snap +111 -90
- data/spec/snapshots/producers_with_key-no-nest.snap +94 -87
- data/spec/snapshots/producers_with_key.snap +95 -88
- data/spec/spec_helper.rb +2 -1
- metadata +53 -15
@@ -8,8 +8,11 @@ module Deimos
|
|
8
8
|
include Phobos::Producer
|
9
9
|
attr_accessor :id, :current_topic
|
10
10
|
|
11
|
+
# @return [Integer]
|
11
12
|
BATCH_SIZE = 1000
|
13
|
+
# @return [Integer]
|
12
14
|
DELETE_BATCH_SIZE = 10
|
15
|
+
# @return [Integer]
|
13
16
|
MAX_DELETE_ATTEMPTS = 3
|
14
17
|
|
15
18
|
# @param logger [Logger]
|
@@ -19,12 +22,13 @@ module Deimos
|
|
19
22
|
@logger.push_tags("DbProducer #{@id}") if @logger.respond_to?(:push_tags)
|
20
23
|
end
|
21
24
|
|
22
|
-
# @return [
|
25
|
+
# @return [FigTree]
|
23
26
|
def config
|
24
27
|
Deimos.config.db_producer
|
25
28
|
end
|
26
29
|
|
27
30
|
# Start the poll.
|
31
|
+
# @return [void]
|
28
32
|
def start
|
29
33
|
@logger.info('Starting...')
|
30
34
|
@signal_to_stop = false
|
@@ -40,12 +44,14 @@ module Deimos
|
|
40
44
|
end
|
41
45
|
|
42
46
|
# Stop the poll.
|
47
|
+
# @return [void]
|
43
48
|
def stop
|
44
49
|
@logger.info('Received signal to stop')
|
45
50
|
@signal_to_stop = true
|
46
51
|
end
|
47
52
|
|
48
53
|
# Complete one loop of processing all messages in the DB.
|
54
|
+
# @return [void]
|
49
55
|
def process_next_messages
|
50
56
|
topics = retrieve_topics
|
51
57
|
@logger.info("Found topics: #{topics}")
|
@@ -80,6 +86,7 @@ module Deimos
|
|
80
86
|
end
|
81
87
|
|
82
88
|
# Process a single batch in a topic.
|
89
|
+
# @return [void]
|
83
90
|
def process_topic_batch
|
84
91
|
messages = retrieve_messages
|
85
92
|
return false if messages.empty?
|
@@ -111,6 +118,7 @@ module Deimos
|
|
111
118
|
end
|
112
119
|
|
113
120
|
# @param messages [Array<Deimos::KafkaMessage>]
|
121
|
+
# @return [void]
|
114
122
|
def delete_messages(messages)
|
115
123
|
attempts = 1
|
116
124
|
begin
|
@@ -137,6 +145,7 @@ module Deimos
|
|
137
145
|
end
|
138
146
|
|
139
147
|
# @param messages [Array<Deimos::KafkaMessage>]
|
148
|
+
# @return [void]
|
140
149
|
def log_messages(messages)
|
141
150
|
return if config.log_topics != :all && !config.log_topics.include?(@current_topic)
|
142
151
|
|
@@ -146,7 +155,8 @@ module Deimos
|
|
146
155
|
end
|
147
156
|
end
|
148
157
|
|
149
|
-
# Send metrics to
|
158
|
+
# Send metrics related to pending messages.
|
159
|
+
# @return [void]
|
150
160
|
def send_pending_metrics
|
151
161
|
metrics = Deimos.config.metrics
|
152
162
|
return unless metrics
|
@@ -185,6 +195,7 @@ module Deimos
|
|
185
195
|
# Shut down the sync producer if we have to. Phobos will automatically
|
186
196
|
# create a new one. We should call this if the producer can be in a bad
|
187
197
|
# state and e.g. we need to clear the buffer.
|
198
|
+
# @return [void]
|
188
199
|
def shutdown_producer
|
189
200
|
if self.class.producer.respond_to?(:sync_producer_shutdown) # Phobos 1.8.3
|
190
201
|
self.class.producer.sync_producer_shutdown
|
@@ -194,6 +205,7 @@ module Deimos
|
|
194
205
|
# Produce messages in batches, reducing the size 1/10 if the batch is too
|
195
206
|
# large. Does not retry batches of messages that have already been sent.
|
196
207
|
# @param batch [Array<Hash>]
|
208
|
+
# @return [void]
|
197
209
|
def produce_messages(batch)
|
198
210
|
batch_size = batch.size
|
199
211
|
current_index = 0
|
@@ -7,9 +7,11 @@ module Deimos
|
|
7
7
|
class DeadlockRetry
|
8
8
|
class << self
|
9
9
|
# Maximum number of times to retry the block after encountering a deadlock
|
10
|
+
# @return [Integer]
|
10
11
|
RETRY_COUNT = 2
|
11
12
|
|
12
13
|
# Need to match on error messages to support older Rails versions
|
14
|
+
# @return [Array<String>]
|
13
15
|
DEADLOCK_MESSAGES = [
|
14
16
|
# MySQL
|
15
17
|
'Deadlock found when trying to get lock',
|
@@ -28,6 +30,7 @@ module Deimos
|
|
28
30
|
# from retrying at the same time.
|
29
31
|
# @param tags [Array] Tags to attach when logging and reporting metrics.
|
30
32
|
# @yield Yields to the block that may deadlock.
|
33
|
+
# @return [void]
|
31
34
|
def wrap(tags=[])
|
32
35
|
count = RETRY_COUNT
|
33
36
|
|
@@ -6,10 +6,12 @@ module Deimos
|
|
6
6
|
module Utils
|
7
7
|
# Listener that can seek to get the last X messages in a topic.
|
8
8
|
class SeekListener < Phobos::Listener
|
9
|
+
# @return [Integer]
|
9
10
|
MAX_SEEK_RETRIES = 3
|
11
|
+
# @return [Integer]
|
10
12
|
attr_accessor :num_messages
|
11
13
|
|
12
|
-
#
|
14
|
+
# @return [void]
|
13
15
|
def start_listener
|
14
16
|
@num_messages ||= 10
|
15
17
|
@consumer = create_kafka_consumer
|
@@ -45,17 +47,20 @@ module Deimos
|
|
45
47
|
|
46
48
|
cattr_accessor :total_messages
|
47
49
|
|
48
|
-
# @param klass [Class
|
50
|
+
# @param klass [Class<Deimos::Consumer>]
|
51
|
+
# @return [void]
|
49
52
|
def self.config_class=(klass)
|
50
53
|
self.config.merge!(klass.config)
|
51
54
|
end
|
52
55
|
|
53
|
-
#
|
56
|
+
# @param _kafka_client [Kafka::Client]
|
57
|
+
# @return [void]
|
54
58
|
def self.start(_kafka_client)
|
55
59
|
self.total_messages = []
|
56
60
|
end
|
57
61
|
|
58
|
-
#
|
62
|
+
# @param payload [Hash]
|
63
|
+
# @param metadata [Hash]
|
59
64
|
def consume(payload, metadata)
|
60
65
|
self.class.total_messages << {
|
61
66
|
key: metadata[:key],
|
@@ -66,18 +71,20 @@ module Deimos
|
|
66
71
|
|
67
72
|
# Class which can process/consume messages inline.
|
68
73
|
class InlineConsumer
|
74
|
+
# @return [Integer]
|
69
75
|
MAX_MESSAGE_WAIT_TIME = 1.second
|
76
|
+
# @return [Integer]
|
70
77
|
MAX_TOPIC_WAIT_TIME = 10.seconds
|
71
78
|
|
72
79
|
# Get the last X messages from a topic. You can specify a subclass of
|
73
80
|
# Deimos::Consumer or Deimos::Producer, or provide the
|
74
81
|
# schema, namespace and key_config directly.
|
75
82
|
# @param topic [String]
|
76
|
-
# @param config_class [Class
|
83
|
+
# @param config_class [Class<Deimos::Consumer>,Class<Deimos::Producer>]
|
77
84
|
# @param schema [String]
|
78
85
|
# @param namespace [String]
|
79
86
|
# @param key_config [Hash]
|
80
|
-
# @param num_messages [
|
87
|
+
# @param num_messages [Integer]
|
81
88
|
# @return [Array<Hash>]
|
82
89
|
def self.get_messages_for(topic:, schema: nil, namespace: nil, key_config: nil,
|
83
90
|
config_class: nil, num_messages: 10)
|
@@ -106,6 +113,7 @@ module Deimos
|
|
106
113
|
# @param frk_consumer [Class]
|
107
114
|
# @param num_messages [Integer] If this number is >= the number
|
108
115
|
# of messages in the topic, all messages will be consumed.
|
116
|
+
# @return [void]
|
109
117
|
def self.consume(topic:, frk_consumer:, num_messages: 10)
|
110
118
|
listener = SeekListener.new(
|
111
119
|
handler: frk_consumer,
|
@@ -24,6 +24,7 @@ module Deimos
|
|
24
24
|
|
25
25
|
# @param topic [String]
|
26
26
|
# @param partition [Integer]
|
27
|
+
# @return [void]
|
27
28
|
def report_lag(topic, partition)
|
28
29
|
self.topics[topic.to_s] ||= Topic.new(topic, self)
|
29
30
|
self.topics[topic.to_s].report_lag(partition)
|
@@ -32,6 +33,7 @@ module Deimos
|
|
32
33
|
# @param topic [String]
|
33
34
|
# @param partition [Integer]
|
34
35
|
# @param offset [Integer]
|
36
|
+
# @return [void]
|
35
37
|
def assign_current_offset(topic, partition, offset)
|
36
38
|
self.topics[topic.to_s] ||= Topic.new(topic, self)
|
37
39
|
self.topics[topic.to_s].assign_current_offset(partition, offset)
|
@@ -56,11 +58,15 @@ module Deimos
|
|
56
58
|
end
|
57
59
|
|
58
60
|
# @param partition [Integer]
|
61
|
+
# @param offset [Integer]
|
62
|
+
# @return [void]
|
59
63
|
def assign_current_offset(partition, offset)
|
60
64
|
self.partition_current_offsets[partition.to_i] = offset
|
61
65
|
end
|
62
66
|
|
63
67
|
# @param partition [Integer]
|
68
|
+
# @param offset [Integer]
|
69
|
+
# @return [Integer]
|
64
70
|
def compute_lag(partition, offset)
|
65
71
|
begin
|
66
72
|
client = Phobos.create_kafka_client
|
@@ -74,6 +80,7 @@ module Deimos
|
|
74
80
|
end
|
75
81
|
|
76
82
|
# @param partition [Integer]
|
83
|
+
# @return [void]
|
77
84
|
def report_lag(partition)
|
78
85
|
current_offset = self.partition_current_offsets[partition.to_i]
|
79
86
|
return unless current_offset
|
@@ -94,6 +101,7 @@ module Deimos
|
|
94
101
|
|
95
102
|
class << self
|
96
103
|
# Reset all group information.
|
104
|
+
# @return [void]
|
97
105
|
def reset
|
98
106
|
@groups = {}
|
99
107
|
end
|
@@ -103,6 +111,7 @@ module Deimos
|
|
103
111
|
# topic = event.payload.fetch(:topic)
|
104
112
|
# partition = event.payload.fetch(:partition)
|
105
113
|
# @param payload [Hash]
|
114
|
+
# @return [void]
|
106
115
|
def message_processed(payload)
|
107
116
|
offset = payload[:offset] || payload[:last_offset]
|
108
117
|
topic = payload[:topic]
|
@@ -116,6 +125,7 @@ module Deimos
|
|
116
125
|
end
|
117
126
|
|
118
127
|
# @param payload [Hash]
|
128
|
+
# @return [void]
|
119
129
|
def offset_seek(payload)
|
120
130
|
offset = payload[:offset]
|
121
131
|
topic = payload[:topic]
|
@@ -129,6 +139,7 @@ module Deimos
|
|
129
139
|
end
|
130
140
|
|
131
141
|
# @param payload [Hash]
|
142
|
+
# @return [void]
|
132
143
|
def heartbeat(payload)
|
133
144
|
group = payload[:group_id]
|
134
145
|
synchronize do
|
@@ -31,6 +31,7 @@ module Deimos
|
|
31
31
|
# @param kwactions [String]
|
32
32
|
# @param request [String]
|
33
33
|
# @param response [String]
|
34
|
+
# @return [void]
|
34
35
|
def schemas(*actions, request: nil, response: nil, **kwactions)
|
35
36
|
actions.each do |action|
|
36
37
|
request ||= action.to_s.titleize
|
@@ -49,6 +50,7 @@ module Deimos
|
|
49
50
|
|
50
51
|
# Set the namespace for both requests and responses.
|
51
52
|
# @param name [String]
|
53
|
+
# @return [void]
|
52
54
|
def namespace(name)
|
53
55
|
request_namespace(name)
|
54
56
|
response_namespace(name)
|
@@ -56,12 +58,14 @@ module Deimos
|
|
56
58
|
|
57
59
|
# Set the namespace for requests.
|
58
60
|
# @param name [String]
|
61
|
+
# @return [void]
|
59
62
|
def request_namespace(name)
|
60
63
|
namespaces[:request] = name
|
61
64
|
end
|
62
65
|
|
63
66
|
# Set the namespace for repsonses.
|
64
67
|
# @param name [String]
|
68
|
+
# @return [void]
|
65
69
|
def response_namespace(name)
|
66
70
|
namespaces[:response] = name
|
67
71
|
end
|
@@ -94,6 +98,7 @@ module Deimos
|
|
94
98
|
end
|
95
99
|
|
96
100
|
# Decode the payload with the parameters.
|
101
|
+
# @return [void]
|
97
102
|
def decode_schema
|
98
103
|
namespace, schema = parse_namespace(:request)
|
99
104
|
decoder = Deimos.schema_backend(schema: schema, namespace: namespace)
|
@@ -109,6 +114,9 @@ module Deimos
|
|
109
114
|
|
110
115
|
# Render a hash into a payload as specified by the configured schema and namespace.
|
111
116
|
# @param payload [Hash]
|
117
|
+
# @param schema [String]
|
118
|
+
# @param namespace [String]
|
119
|
+
# @return [void]
|
112
120
|
def render_schema(payload, schema: nil, namespace: nil)
|
113
121
|
namespace, schema = parse_namespace(:response) if !schema && !namespace
|
114
122
|
encoder = Deimos.schema_backend(schema: schema, namespace: namespace)
|
data/lib/deimos/version.rb
CHANGED
data/lib/deimos.rb
CHANGED
@@ -45,7 +45,7 @@ require 'erb'
|
|
45
45
|
# Parent module.
|
46
46
|
module Deimos
|
47
47
|
class << self
|
48
|
-
# @return [Class
|
48
|
+
# @return [Class<Deimos::SchemaBackends::Base>]
|
49
49
|
def schema_backend_class
|
50
50
|
backend = Deimos.config.schema.backend.to_s
|
51
51
|
|
@@ -54,7 +54,7 @@ module Deimos
|
|
54
54
|
"Deimos::SchemaBackends::#{backend.classify}".constantize
|
55
55
|
end
|
56
56
|
|
57
|
-
# @param schema [String
|
57
|
+
# @param schema [String, Symbol]
|
58
58
|
# @param namespace [String]
|
59
59
|
# @return [Deimos::SchemaBackends::Base]
|
60
60
|
def schema_backend(schema:, namespace:)
|
@@ -81,6 +81,7 @@ module Deimos
|
|
81
81
|
|
82
82
|
# Start the DB producers to send Kafka messages.
|
83
83
|
# @param thread_count [Integer] the number of threads to start.
|
84
|
+
# @return [void]
|
84
85
|
def start_db_backend!(thread_count: 1)
|
85
86
|
Sigurd.exit_on_signal = true
|
86
87
|
if self.config.producers.backend != :db
|
@@ -69,7 +69,7 @@ module Deimos
|
|
69
69
|
end
|
70
70
|
|
71
71
|
desc 'Generate migration for a table based on an existing schema.'
|
72
|
-
#
|
72
|
+
# @return [void]
|
73
73
|
def generate
|
74
74
|
migration_template('migration.rb', "db/migrate/create_#{table_name.underscore}.rb")
|
75
75
|
template('model.rb', "app/models/#{table_name.underscore.singularize}.rb")
|
@@ -20,7 +20,7 @@
|
|
20
20
|
<%- if @field_assignments.select{ |h| !h[:is_schema_class] }.any? -%>
|
21
21
|
### Attribute Accessors ###
|
22
22
|
<%- @field_assignments.select{ |h| !h[:is_schema_class] }.each do |method_definition| -%>
|
23
|
-
# @
|
23
|
+
# @return [<%= method_definition[:deimos_type] %>]
|
24
24
|
attr_accessor :<%= method_definition[:field].name %>
|
25
25
|
<%- end -%>
|
26
26
|
|
@@ -28,14 +28,14 @@
|
|
28
28
|
<%- if @field_assignments.select{ |h| h[:is_schema_class] }.any? -%>
|
29
29
|
### Attribute Writers ###
|
30
30
|
<%- @field_assignments.select{ |h| h[:is_schema_class] }.each do |method_definition| -%>
|
31
|
-
# @
|
31
|
+
# @return [<%= method_definition[:deimos_type] %>]
|
32
32
|
def <%= method_definition[:field].name %>=(<%= method_definition[:method_argument] %>)
|
33
33
|
<%- if method_definition[:field_type] == :array -%>
|
34
|
-
@<%= method_definition[:field].name %> = values
|
34
|
+
@<%= method_definition[:field].name %> = values&.map do |value|
|
35
35
|
<%= method_definition[:field_initialization] %>
|
36
36
|
end
|
37
37
|
<%- elsif method_definition[:field_type] == :map -%>
|
38
|
-
@<%= method_definition[:field].name %> = values
|
38
|
+
@<%= method_definition[:field].name %> = values&.transform_values do |value|
|
39
39
|
<%= method_definition[:field_initialization] %>
|
40
40
|
end
|
41
41
|
<%- else -%>
|
@@ -62,7 +62,15 @@
|
|
62
62
|
def namespace
|
63
63
|
'<%= @current_schema.namespace %>'
|
64
64
|
end
|
65
|
-
|
65
|
+
<%- if @tombstone_assignment %>
|
66
|
+
def self.tombstone(key)
|
67
|
+
record = self.allocate
|
68
|
+
<%- if @tombstone_assignment.present? -%>
|
69
|
+
<%= @tombstone_assignment %>
|
70
|
+
<%- end -%>
|
71
|
+
record
|
72
|
+
end
|
73
|
+
<%- end %>
|
66
74
|
# @override
|
67
75
|
def as_json(_opts={})
|
68
76
|
{
|
@@ -11,11 +11,17 @@ module Deimos
|
|
11
11
|
# Generator for Schema Classes used for the IDE and consumer/producer interfaces
|
12
12
|
class SchemaClassGenerator < Rails::Generators::Base
|
13
13
|
|
14
|
+
# @return [Array<Symbol>]
|
14
15
|
SPECIAL_TYPES = %i(record enum).freeze
|
16
|
+
# @return [String]
|
15
17
|
INITIALIZE_WHITESPACE = "\n#{' ' * 19}"
|
18
|
+
# @return [Array<String>]
|
16
19
|
IGNORE_DEFAULTS = %w(message_id timestamp).freeze
|
20
|
+
# @return [String]
|
17
21
|
SCHEMA_CLASS_FILE = 'schema_class.rb'
|
22
|
+
# @return [String]
|
18
23
|
SCHEMA_RECORD_PATH = File.expand_path('schema_class/templates/schema_record.rb.tt', __dir__).freeze
|
24
|
+
# @return [String]
|
19
25
|
SCHEMA_ENUM_PATH = File.expand_path('schema_class/templates/schema_enum.rb.tt', __dir__).freeze
|
20
26
|
|
21
27
|
source_root File.expand_path('schema_class/templates', __dir__)
|
@@ -41,16 +47,17 @@ module Deimos
|
|
41
47
|
# Deimos Consumer or Producer Configuration object
|
42
48
|
# @param schema_name [String]
|
43
49
|
# @param namespace [String]
|
44
|
-
# @param
|
45
|
-
|
50
|
+
# @param key_config [Hash,nil]
|
51
|
+
# @return [void]
|
52
|
+
def generate_classes(schema_name, namespace, key_config)
|
46
53
|
schema_base = Deimos.schema_backend(schema: schema_name, namespace: namespace)
|
47
54
|
schema_base.load_schema
|
48
|
-
if
|
49
|
-
key_schema_base = Deimos.schema_backend(schema:
|
55
|
+
if key_config&.dig(:schema)
|
56
|
+
key_schema_base = Deimos.schema_backend(schema: key_config[:schema], namespace: namespace)
|
50
57
|
key_schema_base.load_schema
|
51
|
-
generate_class_from_schema_base(key_schema_base)
|
58
|
+
generate_class_from_schema_base(key_schema_base, key_config: nil)
|
52
59
|
end
|
53
|
-
generate_class_from_schema_base(schema_base,
|
60
|
+
generate_class_from_schema_base(schema_base, key_config: key_config)
|
54
61
|
end
|
55
62
|
|
56
63
|
# @param schema [Avro::Schema::NamedSchema]
|
@@ -83,8 +90,9 @@ module Deimos
|
|
83
90
|
end
|
84
91
|
|
85
92
|
# @param schema_base [Deimos::SchemaBackends::Base]
|
86
|
-
# @param
|
87
|
-
|
93
|
+
# @param key_config [Hash,nil]
|
94
|
+
# @return [void]
|
95
|
+
def generate_class_from_schema_base(schema_base, key_config: nil)
|
88
96
|
@discovered_schemas = Set.new
|
89
97
|
@sub_schema_templates = []
|
90
98
|
schemas = collect_all_schemas(schema_base.schema_store.schemas.values)
|
@@ -93,11 +101,11 @@ module Deimos
|
|
93
101
|
sub_schemas = schemas.reject { |s| s.name == schema_base.schema }.sort_by(&:name)
|
94
102
|
if Deimos.config.schema.nest_child_schemas
|
95
103
|
@sub_schema_templates = sub_schemas.map do |schema|
|
96
|
-
_generate_class_template_from_schema(schema)
|
104
|
+
_generate_class_template_from_schema(schema, nil)
|
97
105
|
end
|
98
|
-
write_file(main_schema,
|
106
|
+
write_file(main_schema, key_config)
|
99
107
|
else
|
100
|
-
write_file(main_schema,
|
108
|
+
write_file(main_schema, key_config)
|
101
109
|
sub_schemas.each do |schema|
|
102
110
|
write_file(schema, nil)
|
103
111
|
end
|
@@ -105,9 +113,10 @@ module Deimos
|
|
105
113
|
end
|
106
114
|
|
107
115
|
# @param schema [Avro::Schema::NamedSchema]
|
108
|
-
# @param
|
109
|
-
|
110
|
-
|
116
|
+
# @param key_config [Hash,nil]
|
117
|
+
# @return [void]
|
118
|
+
def write_file(schema, key_config)
|
119
|
+
class_template = _generate_class_template_from_schema(schema, key_config)
|
111
120
|
@modules = Utils::SchemaClass.modules_for(schema.namespace)
|
112
121
|
@main_class_definition = class_template
|
113
122
|
|
@@ -143,7 +152,7 @@ module Deimos
|
|
143
152
|
end
|
144
153
|
|
145
154
|
desc 'Generate a class based on configured consumer and producers.'
|
146
|
-
#
|
155
|
+
# @return [void]
|
147
156
|
def generate
|
148
157
|
_validate
|
149
158
|
Rails.logger.info("Generating schemas from Deimos.config to #{Deimos.config.schema.generated_class_path}")
|
@@ -154,7 +163,7 @@ module Deimos
|
|
154
163
|
key_schema_name = config.key_config[:schema]
|
155
164
|
found_schemas.add("#{namespace}.#{schema_name}")
|
156
165
|
found_schemas.add("#{namespace}.#{key_schema_name}") if key_schema_name
|
157
|
-
generate_classes(schema_name, namespace,
|
166
|
+
generate_classes(schema_name, namespace, config.key_config)
|
158
167
|
end
|
159
168
|
|
160
169
|
Deimos.config.consumer_objects.each do |config|
|
@@ -163,7 +172,7 @@ module Deimos
|
|
163
172
|
key_schema_name = config.key_config[:schema]
|
164
173
|
found_schemas.add("#{namespace}.#{schema_name}")
|
165
174
|
found_schemas.add("#{namespace}.#{key_schema_name}") if key_schema_name
|
166
|
-
generate_classes(schema_name, namespace,
|
175
|
+
generate_classes(schema_name, namespace, config.key_config)
|
167
176
|
end
|
168
177
|
|
169
178
|
generate_from_schema_files(found_schemas)
|
@@ -191,10 +200,10 @@ module Deimos
|
|
191
200
|
end
|
192
201
|
|
193
202
|
# @param schema[Avro::Schema::NamedSchema]
|
194
|
-
# @param
|
203
|
+
# @param key_config[Hash,nil]
|
195
204
|
# @return [String]
|
196
|
-
def _generate_class_template_from_schema(schema,
|
197
|
-
_set_instance_variables(schema,
|
205
|
+
def _generate_class_template_from_schema(schema, key_config)
|
206
|
+
_set_instance_variables(schema, key_config)
|
198
207
|
|
199
208
|
temp = schema.is_a?(Avro::Schema::RecordSchema) ? _record_class_template : _enum_class_template
|
200
209
|
res = ERB.new(temp, nil, '-')
|
@@ -202,20 +211,38 @@ module Deimos
|
|
202
211
|
end
|
203
212
|
|
204
213
|
# @param schema[Avro::Schema::NamedSchema]
|
205
|
-
# @param
|
206
|
-
def _set_instance_variables(schema,
|
214
|
+
# @param key_config [Hash,nil]
|
215
|
+
def _set_instance_variables(schema, key_config)
|
207
216
|
schema_is_record = schema.is_a?(Avro::Schema::RecordSchema)
|
208
217
|
@current_schema = schema
|
209
218
|
return unless schema_is_record
|
210
219
|
|
211
220
|
@fields = fields(schema)
|
212
|
-
|
221
|
+
key_schema = nil
|
222
|
+
if key_config&.dig(:schema)
|
223
|
+
key_schema_base = Deimos.schema_backend(schema: key_config[:schema], namespace: schema.namespace)
|
213
224
|
key_schema_base.load_schema
|
214
225
|
key_schema = key_schema_base.schema_store.schemas.values.first
|
215
226
|
@fields << Deimos::SchemaField.new('payload_key', key_schema, [], nil)
|
216
227
|
end
|
217
228
|
@initialization_definition = _initialization_definition
|
218
229
|
@field_assignments = _field_assignments
|
230
|
+
@tombstone_assignment = _tombstone_assignment(key_config, key_schema)
|
231
|
+
end
|
232
|
+
|
233
|
+
def _tombstone_assignment(key_config, key_schema)
|
234
|
+
return nil unless key_config
|
235
|
+
|
236
|
+
if key_config[:plain]
|
237
|
+
"record.tombstone_key = key"
|
238
|
+
elsif key_config[:field]
|
239
|
+
"record.tombstone_key = key\n record.#{key_config[:field]} = key"
|
240
|
+
elsif key_schema
|
241
|
+
field_base_type = _field_type(key_schema)
|
242
|
+
"record.tombstone_key = #{field_base_type}.initialize_from_value(key)\n record.payload_key = key"
|
243
|
+
else
|
244
|
+
''
|
245
|
+
end
|
219
246
|
end
|
220
247
|
|
221
248
|
# Defines the initialization method for Schema Records with one keyword argument per line
|
@@ -234,7 +261,7 @@ module Deimos
|
|
234
261
|
"#{result})"
|
235
262
|
end
|
236
263
|
|
237
|
-
# @param [SchemaField]
|
264
|
+
# @param field [SchemaField]
|
238
265
|
# @return [String]
|
239
266
|
def _field_default(field)
|
240
267
|
default = field.default
|