deimos-ruby 2.6.0.pre.beta1 → 2.6.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 +4 -4
- data/CHANGELOG.md +8 -2
- data/README.md +45 -20
- data/deimos-ruby.gemspec +0 -1
- data/docs/CONFIGURATION.md +5 -11
- data/lib/deimos/active_record_consume/batch_consumption.rb +128 -31
- data/lib/deimos/active_record_consume/batch_record.rb +3 -0
- data/lib/deimos/active_record_consume/message_consumption.rb +2 -2
- data/lib/deimos/active_record_consumer.rb +6 -1
- data/lib/deimos/active_record_producer.rb +1 -1
- data/lib/deimos/config/configuration.rb +11 -34
- data/lib/deimos/exceptions.rb +17 -0
- data/lib/deimos/ext/consumer_route.rb +3 -1
- data/lib/deimos/ext/producer_middleware.rb +2 -2
- data/lib/deimos/logging.rb +0 -9
- data/lib/deimos/producer.rb +5 -5
- data/lib/deimos/schema_backends/avro_base.rb +36 -7
- data/lib/deimos/schema_class/base.rb +62 -0
- data/lib/deimos/schema_class/enum.rb +49 -0
- data/lib/deimos/schema_class/record.rb +100 -0
- data/lib/deimos/transcoder.rb +6 -6
- data/lib/deimos/utils/deadlock_retry.rb +16 -2
- data/lib/deimos/utils/schema_class.rb +47 -14
- data/lib/deimos/version.rb +1 -1
- data/lib/deimos.rb +4 -3
- data/lib/generators/deimos/schema_class/templates/schema_class.rb.tt +8 -0
- data/lib/generators/deimos/schema_class/templates/schema_enum.rb.tt +13 -0
- data/lib/generators/deimos/schema_class/templates/schema_record.rb.tt +102 -0
- data/lib/generators/deimos/schema_class_generator.rb +369 -0
- data/lib/tasks/deimos.rake +4 -16
- data/regenerate_test_schema_classes.rb +3 -12
- data/spec/active_record_batch_consumer_association_spec.rb +1 -1
- data/spec/active_record_batch_consumer_spec.rb +286 -1
- data/spec/active_record_consume/batch_consumption_spec.rb +1 -1
- data/spec/active_record_consumer_spec.rb +3 -3
- data/spec/active_record_producer_spec.rb +1 -1
- data/spec/batch_consumer_spec.rb +2 -2
- data/spec/consumer_spec.rb +5 -5
- data/spec/generators/schema_class/my_schema_spec.rb +16 -0
- data/spec/generators/schema_class/my_schema_with_circular_reference_spec.rb +98 -0
- data/spec/generators/schema_class/my_schema_with_complex_types_spec.rb +237 -0
- data/spec/generators/schema_class_generator_spec.rb +283 -0
- data/spec/schemas/my_namespace/generated.rb +4 -4
- data/spec/schemas/my_namespace/my_long_namespace_schema.rb +2 -2
- data/spec/schemas/my_namespace/my_nested_schema.rb +3 -3
- data/spec/schemas/my_namespace/my_schema.rb +2 -2
- data/spec/schemas/my_namespace/my_schema_compound_key.rb +2 -2
- data/spec/schemas/my_namespace/my_schema_id_key.rb +2 -2
- data/spec/schemas/my_namespace/my_schema_key.rb +2 -2
- data/spec/schemas/my_namespace/my_schema_with_boolean.rb +2 -2
- data/spec/schemas/my_namespace/my_schema_with_circular_reference.rb +3 -3
- data/spec/schemas/my_namespace/my_schema_with_complex_type.rb +6 -6
- data/spec/schemas/my_namespace/my_schema_with_date_time.rb +2 -2
- data/spec/schemas/my_namespace/my_schema_with_id.rb +2 -2
- data/spec/schemas/my_namespace/my_schema_with_title.rb +2 -2
- data/spec/schemas/my_namespace/my_schema_with_union_type.rb +6 -6
- data/spec/schemas/my_namespace/my_schema_with_unique_id.rb +2 -2
- data/spec/schemas/my_namespace/my_updated_schema.rb +1 -1
- data/spec/schemas/my_namespace/request/create_topic.rb +2 -2
- data/spec/schemas/my_namespace/request/index.rb +2 -2
- data/spec/schemas/my_namespace/request/update_request.rb +2 -2
- data/spec/schemas/my_namespace/response/create_topic.rb +2 -2
- data/spec/schemas/my_namespace/response/index.rb +2 -2
- data/spec/schemas/my_namespace/response/update_response.rb +2 -2
- data/spec/schemas/my_namespace/wibble.rb +2 -2
- data/spec/schemas/my_namespace/widget.rb +2 -2
- data/spec/schemas/my_namespace/widget_the_second.rb +2 -2
- data/spec/schemas/my_namespace/widget_the_third.rb +2 -2
- data/spec/snapshots/consumers-no-nest.snap +1740 -0
- data/spec/snapshots/consumers.snap +1720 -0
- data/spec/snapshots/consumers_and_producers-no-nest.snap +1740 -0
- data/spec/snapshots/consumers_and_producers.snap +1720 -0
- data/spec/snapshots/consumers_circular-no-nest.snap +1740 -0
- data/spec/snapshots/consumers_circular.snap +1720 -0
- data/spec/snapshots/consumers_complex_types-no-nest.snap +1740 -0
- data/spec/snapshots/consumers_complex_types.snap +1720 -0
- data/spec/snapshots/consumers_nested-no-nest.snap +1740 -0
- data/spec/snapshots/consumers_nested.snap +1720 -0
- data/spec/snapshots/namespace_folders.snap +1800 -0
- data/spec/snapshots/namespace_map.snap +1800 -0
- data/spec/snapshots/producers_with_key-no-nest.snap +1740 -0
- data/spec/snapshots/producers_with_key.snap +1720 -0
- data/spec/spec_helper.rb +2 -2
- data/spec/utils/db_poller_spec.rb +1 -1
- data/spec/utils/deadlock_retry_spec.rb +57 -0
- metadata +27 -18
- data/lib/deimos/schema_class.rb +0 -72
- data/spec/schema_class_spec.rb +0 -65
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
require 'deimos'
|
|
5
|
+
require 'deimos/schema_backends/avro_base'
|
|
6
|
+
|
|
7
|
+
# Generates new schema classes.
|
|
8
|
+
module Deimos
|
|
9
|
+
module Generators
|
|
10
|
+
# Generator for Schema Classes used for the IDE and consumer/producer interfaces
|
|
11
|
+
class SchemaClassGenerator < Rails::Generators::Base
|
|
12
|
+
|
|
13
|
+
# @return [Array<Symbol>]
|
|
14
|
+
SPECIAL_TYPES = %i(record enum).freeze
|
|
15
|
+
# @return [String]
|
|
16
|
+
INITIALIZE_WHITESPACE = "\n#{' ' * 19}".freeze
|
|
17
|
+
# @return [Array<String>]
|
|
18
|
+
IGNORE_DEFAULTS = %w(message_id timestamp).freeze
|
|
19
|
+
# @return [String]
|
|
20
|
+
SCHEMA_CLASS_FILE = 'schema_class.rb'
|
|
21
|
+
# @return [String]
|
|
22
|
+
SCHEMA_RECORD_PATH = File.expand_path('schema_class/templates/schema_record.rb.tt', __dir__).freeze
|
|
23
|
+
# @return [String]
|
|
24
|
+
SCHEMA_ENUM_PATH = File.expand_path('schema_class/templates/schema_enum.rb.tt', __dir__).freeze
|
|
25
|
+
|
|
26
|
+
source_root File.expand_path('schema_class/templates', __dir__)
|
|
27
|
+
|
|
28
|
+
no_commands do
|
|
29
|
+
# Retrieve the fields from this Avro Schema
|
|
30
|
+
# @param schema [Avro::Schema::NamedSchema]
|
|
31
|
+
# @return [Array<SchemaField>]
|
|
32
|
+
def fields(schema)
|
|
33
|
+
schema.fields.map do |field|
|
|
34
|
+
Deimos::SchemaField.new(field.name, field.type, [], field.default)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Converts Deimos::SchemaField's to String form for generated YARD docs
|
|
39
|
+
# @param schema_field [Deimos::SchemaField]
|
|
40
|
+
# @return [String] A string representation of the Type of this SchemaField
|
|
41
|
+
def deimos_field_type(schema_field)
|
|
42
|
+
_field_type(schema_field.type)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Generate a Schema Model Class and all of its Nested Records from a
|
|
46
|
+
# Deimos Consumer or Producer Configuration object
|
|
47
|
+
# @param schema_name [String]
|
|
48
|
+
# @param namespace [String]
|
|
49
|
+
# @param key_config [Hash,nil]
|
|
50
|
+
# @return [void]
|
|
51
|
+
def generate_classes(schema_name, namespace, key_config, backend: nil)
|
|
52
|
+
schema_base = Deimos.schema_backend(schema: schema_name,
|
|
53
|
+
namespace: namespace,
|
|
54
|
+
backend: backend)
|
|
55
|
+
return unless schema_base.supports_class_generation?
|
|
56
|
+
|
|
57
|
+
schema_base.load_schema
|
|
58
|
+
if key_config&.dig(:schema)
|
|
59
|
+
key_schema_base = Deimos.schema_backend(schema: key_config[:schema],
|
|
60
|
+
namespace: namespace,
|
|
61
|
+
backend: backend)
|
|
62
|
+
key_schema_base.load_schema
|
|
63
|
+
generate_class_from_schema_base(key_schema_base, key_config: nil)
|
|
64
|
+
end
|
|
65
|
+
generate_class_from_schema_base(schema_base, key_config: key_config)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @param schema [Avro::Schema::NamedSchema]
|
|
69
|
+
# @return [Array<Avro::Schema::NamedSchema]
|
|
70
|
+
def child_schemas(schema)
|
|
71
|
+
if schema.respond_to?(:fields)
|
|
72
|
+
schema.fields.map(&:type)
|
|
73
|
+
elsif schema.respond_to?(:values)
|
|
74
|
+
[schema.values]
|
|
75
|
+
elsif schema.respond_to?(:items)
|
|
76
|
+
[schema.items]
|
|
77
|
+
elsif schema.respond_to?(:schemas)
|
|
78
|
+
schema.schemas.reject { |s| s.instance_of?(Avro::Schema::PrimitiveSchema) }
|
|
79
|
+
else
|
|
80
|
+
[]
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @param schemas [Array<Avro::Schema::NamedSchema>]
|
|
85
|
+
# @return [Array<Avro::Schema::NamedSchema>]
|
|
86
|
+
def collect_all_schemas(schemas)
|
|
87
|
+
schemas.dup.each do |schema|
|
|
88
|
+
next if @discovered_schemas.include?(schema)
|
|
89
|
+
|
|
90
|
+
@discovered_schemas << schema
|
|
91
|
+
schemas.concat(collect_all_schemas(child_schemas(schema)))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
schemas.select { |s| s.respond_to?(:name) }.uniq
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @param schema_base [Deimos::SchemaBackends::Base]
|
|
98
|
+
# @param key_config [Hash,nil]
|
|
99
|
+
# @return [void]
|
|
100
|
+
def generate_class_from_schema_base(schema_base, key_config: nil)
|
|
101
|
+
@discovered_schemas = Set.new
|
|
102
|
+
@sub_schema_templates = []
|
|
103
|
+
schemas = collect_all_schemas(schema_base.schema_store.schemas.values)
|
|
104
|
+
|
|
105
|
+
main_schema = schemas.find { |s| s.name == schema_base.schema }
|
|
106
|
+
sub_schemas = schemas.reject { |s| s.name == schema_base.schema }.sort_by(&:name)
|
|
107
|
+
if Deimos.config.schema.nest_child_schemas
|
|
108
|
+
@sub_schema_templates = sub_schemas.map do |schema|
|
|
109
|
+
_generate_class_template_from_schema(schema, nil)
|
|
110
|
+
end
|
|
111
|
+
write_file(main_schema, key_config)
|
|
112
|
+
else
|
|
113
|
+
write_file(main_schema, key_config)
|
|
114
|
+
sub_schemas.each do |schema|
|
|
115
|
+
write_file(schema, nil)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @param schema [Avro::Schema::NamedSchema]
|
|
121
|
+
# @param key_config [Hash,nil]
|
|
122
|
+
# @return [void]
|
|
123
|
+
def write_file(schema, key_config)
|
|
124
|
+
class_template = _generate_class_template_from_schema(schema, key_config)
|
|
125
|
+
@modules = Utils::SchemaClass.modules_for(schema.namespace)
|
|
126
|
+
@main_class_definition = class_template
|
|
127
|
+
|
|
128
|
+
file_prefix = schema.name.underscore.singularize
|
|
129
|
+
if Deimos.config.schema.use_full_namespace
|
|
130
|
+
# Use entire namespace for folders
|
|
131
|
+
# but don't add directories that are already in the path
|
|
132
|
+
directories = @modules.map(&:underscore).select do |m|
|
|
133
|
+
Deimos.config.schema.generated_class_path.exclude?(m)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
file_prefix = "#{directories.join('/')}/#{file_prefix}"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
filename = "#{Deimos.config.schema.generated_class_path}/#{file_prefix}.rb"
|
|
140
|
+
template(SCHEMA_CLASS_FILE, filename, force: true)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Format a given field into its appropriate to_h representation.
|
|
144
|
+
# @param field[Deimos::SchemaField]
|
|
145
|
+
# @return [String]
|
|
146
|
+
def field_as_json(field)
|
|
147
|
+
res = "'#{field.name}' => @#{field.name}"
|
|
148
|
+
field_base_type = _schema_base_class(field.type).type_sym
|
|
149
|
+
|
|
150
|
+
if %i(record enum).include?(field_base_type)
|
|
151
|
+
res += case field.type.type_sym
|
|
152
|
+
when :array
|
|
153
|
+
'.map { |v| v&.as_json }'
|
|
154
|
+
when :map
|
|
155
|
+
'.transform_values { |v| v&.as_json }'
|
|
156
|
+
else
|
|
157
|
+
'&.as_json'
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
res + (field.name == @fields.last.name ? '' : ',')
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
desc 'Generate a class based on configured consumer and producers.'
|
|
167
|
+
# @return [void]
|
|
168
|
+
def generate
|
|
169
|
+
_validate
|
|
170
|
+
Rails.logger.info("Generating schemas from Deimos.config to #{Deimos.config.schema.generated_class_path}")
|
|
171
|
+
found_schemas = {}
|
|
172
|
+
Deimos.karafka_configs.each do |config|
|
|
173
|
+
schema_name = config.schema
|
|
174
|
+
next if schema_name.nil?
|
|
175
|
+
|
|
176
|
+
namespace = config.namespace || Deimos.config.producers.schema_namespace
|
|
177
|
+
key_schema_name = config.key_config[:schema]
|
|
178
|
+
|
|
179
|
+
# don't regenerate if the schema was already found and had a payload key
|
|
180
|
+
next if found_schemas["#{namespace}.#{schema_name}"].present?
|
|
181
|
+
|
|
182
|
+
found_schemas["#{namespace}.#{schema_name}"] = key_schema_name
|
|
183
|
+
found_schemas["#{namespace}.#{key_schema_name}"] = nil
|
|
184
|
+
generate_classes(schema_name, namespace, config.key_config, backend: config.schema_backend)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
generate_from_schema_files(found_schemas.keys)
|
|
188
|
+
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
private
|
|
192
|
+
|
|
193
|
+
def generate_from_schema_files(found_schemas)
|
|
194
|
+
path = Deimos.config.schema.path || Deimos.config.schema.paths[:avro].first
|
|
195
|
+
schema_store = SchemaRegistry::AvroSchemaStore.new(path: path)
|
|
196
|
+
schema_store.load_schemas!
|
|
197
|
+
schema_store.schemas.values.sort_by { |s| "#{s.namespace}#{s.name}" }.each do |schema|
|
|
198
|
+
name = "#{schema.namespace}.#{schema.name}"
|
|
199
|
+
next if found_schemas.include?(name)
|
|
200
|
+
|
|
201
|
+
generate_classes(schema.name, schema.namespace, nil)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Determines if Schema Class Generation can be run.
|
|
206
|
+
# @raise if Schema Backend is not of a Avro-based class
|
|
207
|
+
def _validate
|
|
208
|
+
backend = Deimos.config.schema.backend.to_s
|
|
209
|
+
raise 'Schema Class Generation requires an Avro-based Schema Backend' if backend !~ /^avro/
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# @param schema[Avro::Schema::NamedSchema]
|
|
213
|
+
# @param key_config[Hash,nil]
|
|
214
|
+
# @return [String]
|
|
215
|
+
def _generate_class_template_from_schema(schema, key_config)
|
|
216
|
+
_set_instance_variables(schema, key_config)
|
|
217
|
+
|
|
218
|
+
temp = schema.is_a?(Avro::Schema::RecordSchema) ? _record_class_template : _enum_class_template
|
|
219
|
+
res = ERB.new(temp, trim_mode: '-')
|
|
220
|
+
res.result(binding)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# @param schema[Avro::Schema::NamedSchema]
|
|
224
|
+
# @param key_config [Hash,nil]
|
|
225
|
+
def _set_instance_variables(schema, key_config)
|
|
226
|
+
schema_is_record = schema.is_a?(Avro::Schema::RecordSchema)
|
|
227
|
+
@current_schema = schema
|
|
228
|
+
return unless schema_is_record
|
|
229
|
+
|
|
230
|
+
@fields = fields(schema)
|
|
231
|
+
key_schema = nil
|
|
232
|
+
if key_config&.dig(:schema)
|
|
233
|
+
key_schema_base = Deimos.schema_backend(schema: key_config[:schema], namespace: schema.namespace)
|
|
234
|
+
key_schema_base.load_schema
|
|
235
|
+
key_schema = key_schema_base.schema_store.schemas.values.first
|
|
236
|
+
@fields << Deimos::SchemaField.new('payload_key', key_schema, [], nil)
|
|
237
|
+
end
|
|
238
|
+
@initialization_definition = _initialization_definition
|
|
239
|
+
@field_assignments = _field_assignments
|
|
240
|
+
@tombstone_assignment = _tombstone_assignment(key_config, key_schema)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def _tombstone_assignment(key_config, key_schema)
|
|
244
|
+
return nil unless key_config
|
|
245
|
+
|
|
246
|
+
if key_config[:plain]
|
|
247
|
+
"record.tombstone_key = key"
|
|
248
|
+
elsif key_config[:field]
|
|
249
|
+
"record.tombstone_key = key\n record.#{key_config[:field]} = key"
|
|
250
|
+
elsif key_schema
|
|
251
|
+
field_base_type = _field_type(key_schema)
|
|
252
|
+
"record.tombstone_key = #{field_base_type}.initialize_from_value(key)\n record.payload_key = key"
|
|
253
|
+
else
|
|
254
|
+
''
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Defines the initialization method for Schema Records with one keyword argument per line
|
|
259
|
+
# @return [String] A string which defines the method signature for the initialize method
|
|
260
|
+
def _initialization_definition
|
|
261
|
+
arguments = @fields.map do |schema_field|
|
|
262
|
+
arg = "#{schema_field.name}:"
|
|
263
|
+
arg += _field_default(schema_field)
|
|
264
|
+
arg.strip
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
result = "def initialize(_from_message: false, #{arguments.first}"
|
|
268
|
+
arguments[1..].each_with_index do |arg, _i|
|
|
269
|
+
result += ",#{INITIALIZE_WHITESPACE}#{arg}"
|
|
270
|
+
end
|
|
271
|
+
"#{result})"
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# @param field [SchemaField]
|
|
275
|
+
# @return [String]
|
|
276
|
+
def _field_default(field)
|
|
277
|
+
default = field.default
|
|
278
|
+
return ' nil' if default == :no_default || default.nil? || IGNORE_DEFAULTS.include?(field.name)
|
|
279
|
+
|
|
280
|
+
type_sym = field.type.type_sym
|
|
281
|
+
if type_sym == :union
|
|
282
|
+
type_sym = field.type.schemas.find { |s| s.type_sym != :null }&.type_sym
|
|
283
|
+
end
|
|
284
|
+
case type_sym
|
|
285
|
+
when :string, :enum
|
|
286
|
+
" \"#{default}\""
|
|
287
|
+
when :record
|
|
288
|
+
schema_name = Deimos::SchemaBackends::AvroBase.schema_classname(field.type)
|
|
289
|
+
class_instance = Utils::SchemaClass.instance(field.default, schema_name)
|
|
290
|
+
" #{class_instance.to_h}"
|
|
291
|
+
else
|
|
292
|
+
" #{default}"
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Overrides default attr accessor methods
|
|
297
|
+
# @return [Array<String>]
|
|
298
|
+
def _field_assignments
|
|
299
|
+
result = []
|
|
300
|
+
@fields.each do |field|
|
|
301
|
+
field_type = field.type.type_sym # Record, Union, Enum, Array or Map
|
|
302
|
+
schema_base_type = _schema_base_class(field.type)
|
|
303
|
+
field_base_type = _field_type(schema_base_type)
|
|
304
|
+
method_argument = %i(array map).include?(field_type) ? 'values' : 'value'
|
|
305
|
+
is_schema_class = %i(record enum).include?(schema_base_type.type_sym)
|
|
306
|
+
|
|
307
|
+
field_initialization = method_argument
|
|
308
|
+
|
|
309
|
+
if _is_complex_union?(field)
|
|
310
|
+
field_initialization = "initialize_#{field.name}_type(value, from_message: self._from_message)"
|
|
311
|
+
elsif is_schema_class
|
|
312
|
+
field_initialization = "#{field_base_type}.initialize_from_value(value, from_message: self._from_message)"
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
result << {
|
|
316
|
+
field: field,
|
|
317
|
+
field_type: field_type,
|
|
318
|
+
is_schema_class: is_schema_class,
|
|
319
|
+
method_argument: method_argument,
|
|
320
|
+
deimos_type: deimos_field_type(field),
|
|
321
|
+
field_initialization: field_initialization,
|
|
322
|
+
is_complex_union: _is_complex_union?(field)
|
|
323
|
+
}
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
result
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Helper method to detect if a field is a complex union type with multiple record schemas
|
|
330
|
+
# @param field [Deimos::SchemaField]
|
|
331
|
+
# @return [Boolean]
|
|
332
|
+
def _is_complex_union?(field)
|
|
333
|
+
return false unless field.type.type_sym == :union
|
|
334
|
+
|
|
335
|
+
non_null_schemas = field.type.schemas.reject { |s| s.type_sym == :null }
|
|
336
|
+
|
|
337
|
+
record_schemas = non_null_schemas.select { |s| s.type_sym == :record }
|
|
338
|
+
record_schemas.length > 1
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Converts Avro::Schema::NamedSchema's to String form for generated YARD docs.
|
|
342
|
+
# Recursively handles the typing for Arrays, Maps and Unions.
|
|
343
|
+
# @param avro_schema [Avro::Schema::NamedSchema]
|
|
344
|
+
# @return [String] A string representation of the Type of this SchemaField
|
|
345
|
+
def _field_type(avro_schema)
|
|
346
|
+
Deimos::SchemaBackends::AvroBase.field_type(avro_schema)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# Returns the base class for this schema. Decodes Arrays, Maps and Unions
|
|
350
|
+
# @param avro_schema [Avro::Schema::NamedSchema]
|
|
351
|
+
# @return [Avro::Schema::NamedSchema]
|
|
352
|
+
def _schema_base_class(avro_schema)
|
|
353
|
+
Deimos::SchemaBackends::AvroBase.schema_base_class(avro_schema)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# An ERB template for schema record classes
|
|
357
|
+
# @return [String]
|
|
358
|
+
def _record_class_template
|
|
359
|
+
File.read(SCHEMA_RECORD_PATH).strip
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# An ERB template for schema enum classes
|
|
363
|
+
# @return [String]
|
|
364
|
+
def _enum_class_template
|
|
365
|
+
File.read(SCHEMA_ENUM_PATH).strip
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
data/lib/tasks/deimos.rake
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'generators/deimos/schema_class_generator'
|
|
4
4
|
require 'optparse'
|
|
5
5
|
require 'deimos/schema_backends/proto_schema_registry'
|
|
6
6
|
|
|
@@ -32,22 +32,10 @@ namespace :deimos do
|
|
|
32
32
|
Deimos::Utils::DbPoller.start!
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
desc 'Run Schema Model Generator
|
|
35
|
+
desc 'Run Schema Model Generator'
|
|
36
36
|
task generate_schema_classes: :environment do
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
40
|
-
Rails.logger.info('Running deimos:generate_schema_classes')
|
|
41
|
-
# Derive AvroGen configs from the configured Kafka topics so keyed records
|
|
42
|
-
# still get their tombstone/payload_key helpers, then delegate to AvroGen.
|
|
43
|
-
configs = Deimos.karafka_configs.filter_map do |config|
|
|
44
|
-
next if config.schema.nil?
|
|
45
|
-
|
|
46
|
-
{ schema: config.schema,
|
|
47
|
-
namespace: config.namespace,
|
|
48
|
-
key_config: config.key_config }
|
|
49
|
-
end
|
|
50
|
-
AvroGen::Generator.new.generate_from_configs(configs)
|
|
37
|
+
Rails.logger.info("Running deimos:generate_schema_classes")
|
|
38
|
+
Deimos::Generators::SchemaClassGenerator.start
|
|
51
39
|
end
|
|
52
40
|
|
|
53
41
|
desc 'Output Protobuf key schemas'
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
#! /usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
2
|
require 'action_controller/railtie'
|
|
5
3
|
require 'deimos'
|
|
6
4
|
require 'deimos/metrics/mock'
|
|
7
5
|
require 'deimos/tracing/mock'
|
|
8
|
-
require
|
|
6
|
+
# not sure why "require deimos/utils/schema_class" doesn't work
|
|
7
|
+
require_relative 'lib/deimos/utils/schema_class'
|
|
9
8
|
|
|
10
9
|
class DeimosApp < Rails::Application
|
|
11
10
|
end
|
|
12
11
|
DeimosApp.initialize!
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
# from the configured Kafka topics (so keyed records keep their tombstone /
|
|
16
|
-
# payload_key helpers) and delegate to AvroGen.
|
|
17
|
-
configs = Deimos.karafka_configs.filter_map do |config|
|
|
18
|
-
next if config.schema.nil?
|
|
19
|
-
|
|
20
|
-
{ schema: config.schema, namespace: config.namespace, key_config: config.key_config }
|
|
21
|
-
end
|
|
22
|
-
AvroGen::Generator.new.generate_from_configs(configs)
|
|
13
|
+
Deimos::Generators::SchemaClassGenerator.new.generate
|