nats_wave 1.1.11 → 1.1.13
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/Gemfile.lock +1 -1
- data/lib/nats_wave/adapters/datadog_metrics.rb +1 -1
- data/lib/nats_wave/concerns/mappable.rb +1078 -118
- data/lib/nats_wave/configuration.rb +1 -1
- data/lib/nats_wave/version.rb +1 -1
- metadata +2 -2
@@ -1,3 +1,822 @@
|
|
1
|
+
# # # # frozen_string_literal: true
|
2
|
+
# # #
|
3
|
+
# # # module NatsWave
|
4
|
+
# # # module Concerns
|
5
|
+
# # # module Mappable
|
6
|
+
# # # extend ActiveSupport::Concern
|
7
|
+
# # #
|
8
|
+
# # # included do
|
9
|
+
# # # class_attribute :nats_wave_subscription_config, :nats_wave_publishing_config
|
10
|
+
# # # self.nats_wave_subscription_config = {}
|
11
|
+
# # # self.nats_wave_publishing_config = {}
|
12
|
+
# # # end
|
13
|
+
# # #
|
14
|
+
# # # class_methods do
|
15
|
+
# # # # Configure subscriptions with optional custom handler
|
16
|
+
# # # def nats_wave_subscribes_to(*subjects, **options, &block)
|
17
|
+
# # # Rails.logger.debug "📡 #{self.name}: Setting up subscription to subjects: #{subjects.inspect}" if defined?(Rails)
|
18
|
+
# # #
|
19
|
+
# # # handler = options[:handler] || block
|
20
|
+
# # #
|
21
|
+
# # # subscription_config = {
|
22
|
+
# # # subjects: subjects.flatten,
|
23
|
+
# # # field_mappings: options[:field_mappings] || {},
|
24
|
+
# # # transformations: options[:transformations] || {},
|
25
|
+
# # # skip_fields: options[:skip_fields] || [],
|
26
|
+
# # # unique_fields: options[:unique_fields] || [:id],
|
27
|
+
# # # sync_strategy: options[:sync_strategy] || :upsert,
|
28
|
+
# # # handler: handler,
|
29
|
+
# # # queue_group: options[:queue_group],
|
30
|
+
# # # auto_sync: options[:auto_sync] != false # Default to true, can be disabled
|
31
|
+
# # # }
|
32
|
+
# # #
|
33
|
+
# # # # Store the config
|
34
|
+
# # # config_key = subjects.join(',')
|
35
|
+
# # # self.nats_wave_subscription_config[config_key] = subscription_config
|
36
|
+
# # #
|
37
|
+
# # # # Create the subscription handler
|
38
|
+
# # # processed_handler = create_subscription_handler(subscription_config)
|
39
|
+
# # #
|
40
|
+
# # # # Register the subscription
|
41
|
+
# # # NatsWave::ModelRegistry.register_subscription(
|
42
|
+
# # # subjects: subjects.flatten,
|
43
|
+
# # # model: self.name,
|
44
|
+
# # # handler: processed_handler,
|
45
|
+
# # # queue_group: subscription_config[:queue_group]
|
46
|
+
# # # )
|
47
|
+
# # #
|
48
|
+
# # # Rails.logger.debug "📡 #{self.name}: Registered subscription for subjects: #{subjects}" if defined?(Rails)
|
49
|
+
# # # end
|
50
|
+
# # #
|
51
|
+
# # # # Configure publishing with optional custom handler
|
52
|
+
# # # def nats_wave_publishes_to(*subjects, **options, &block)
|
53
|
+
# # # Rails.logger.debug "📤 #{self.name}: Setting up publishing to subjects: #{subjects.inspect}" if defined?(Rails)
|
54
|
+
# # #
|
55
|
+
# # # handler = options[:handler] || block
|
56
|
+
# # #
|
57
|
+
# # # publishing_config = {
|
58
|
+
# # # subjects: subjects.flatten,
|
59
|
+
# # # field_mappings: options[:field_mappings] || {},
|
60
|
+
# # # transformations: options[:transformations] || {},
|
61
|
+
# # # skip_fields: options[:skip_fields] || [],
|
62
|
+
# # # conditions: options[:conditions] || {},
|
63
|
+
# # # actions: options[:actions] || [:create, :update, :destroy],
|
64
|
+
# # # handler: handler,
|
65
|
+
# # # async: options[:async] != false, # Default to true
|
66
|
+
# # # enabled: options[:enabled] != false # Default to true
|
67
|
+
# # # }
|
68
|
+
# # #
|
69
|
+
# # # # Store the config
|
70
|
+
# # # config_key = subjects.join(',')
|
71
|
+
# # # self.nats_wave_publishing_config[config_key] = publishing_config
|
72
|
+
# # #
|
73
|
+
# # # Rails.logger.debug "📤 #{self.name}: Registered publishing config for subjects: #{subjects}" if defined?(Rails)
|
74
|
+
# # # end
|
75
|
+
# # #
|
76
|
+
# # # # Get all subscription configurations
|
77
|
+
# # # def nats_wave_subscription_configs
|
78
|
+
# # # nats_wave_subscription_config
|
79
|
+
# # # end
|
80
|
+
# # #
|
81
|
+
# # # # Get all publishing configurations
|
82
|
+
# # # def nats_wave_publishing_configs
|
83
|
+
# # # nats_wave_publishing_config
|
84
|
+
# # # end
|
85
|
+
# # #
|
86
|
+
# # # # Get all subjects this model subscribes to
|
87
|
+
# # # def nats_wave_subscribed_subjects
|
88
|
+
# # # nats_wave_subscription_config.values.flat_map { |config| config[:subjects] }.uniq
|
89
|
+
# # # end
|
90
|
+
# # #
|
91
|
+
# # # # Get all subjects this model publishes to
|
92
|
+
# # # def nats_wave_published_subjects
|
93
|
+
# # # nats_wave_publishing_config.values.flat_map { |config| config[:subjects] }.uniq
|
94
|
+
# # # end
|
95
|
+
# # #
|
96
|
+
# # # private
|
97
|
+
# # #
|
98
|
+
# # # # Create a subscription handler that processes data and calls custom handler or auto-sync
|
99
|
+
# # # def create_subscription_handler(config)
|
100
|
+
# # # model_class = self
|
101
|
+
# # #
|
102
|
+
# # # lambda do |message|
|
103
|
+
# # # begin
|
104
|
+
# # # Rails.logger.debug "📨 Processing subscription message for #{model_class.name}" if defined?(Rails)
|
105
|
+
# # #
|
106
|
+
# # # # Extract the raw data
|
107
|
+
# # # raw_data = message['data'] || {}
|
108
|
+
# # # model_name = message['model']
|
109
|
+
# # # action = message['action']
|
110
|
+
# # #
|
111
|
+
# # # # Process the data through mappings and transformations
|
112
|
+
# # # processed_data = model_class.process_data(raw_data, config)
|
113
|
+
# # #
|
114
|
+
# # # # If custom handler is provided, call it with model_name, action, processed_data
|
115
|
+
# # # if config[:handler]
|
116
|
+
# # # Rails.logger.debug "📨 Calling custom subscription handler" if defined?(Rails)
|
117
|
+
# # # config[:handler].call(model_name, action, processed_data, message)
|
118
|
+
# # # elsif config[:auto_sync]
|
119
|
+
# # # Rails.logger.debug "📨 Performing auto-sync" if defined?(Rails)
|
120
|
+
# # # # Default auto-sync behavior
|
121
|
+
# # # model_class.perform_auto_sync(model_name, action, processed_data, config)
|
122
|
+
# # # else
|
123
|
+
# # # Rails.logger.debug "📨 No handler provided and auto_sync disabled" if defined?(Rails)
|
124
|
+
# # # end
|
125
|
+
# # #
|
126
|
+
# # # rescue => e
|
127
|
+
# # # Rails.logger.error "Error in subscription handler for #{model_class.name}: #{e.message}" if defined?(Rails)
|
128
|
+
# # # Rails.logger.error e.backtrace.join("\n") if defined?(Rails)
|
129
|
+
# # # raise
|
130
|
+
# # # end
|
131
|
+
# # # end
|
132
|
+
# # # end
|
133
|
+
# # #
|
134
|
+
# # # # Process data through field mappings and transformations (used by both subscription and publishing)
|
135
|
+
# # # def process_data(raw_data, config)
|
136
|
+
# # # processed_data = {}
|
137
|
+
# # # field_mappings = config[:field_mappings]
|
138
|
+
# # # transformations = config[:transformations]
|
139
|
+
# # # skip_fields = config[:skip_fields]
|
140
|
+
# # #
|
141
|
+
# # # raw_data.each do |field, value|
|
142
|
+
# # # field_str = field.to_s
|
143
|
+
# # # field_sym = field.to_sym
|
144
|
+
# # #
|
145
|
+
# # # # Skip if in skip_fields
|
146
|
+
# # # next if skip_fields.include?(field_str) || skip_fields.include?(field_sym)
|
147
|
+
# # #
|
148
|
+
# # # # Apply field mapping
|
149
|
+
# # # mapped_field = field_mappings[field_str] ||
|
150
|
+
# # # field_mappings[field_sym] ||
|
151
|
+
# # # field
|
152
|
+
# # #
|
153
|
+
# # # # Apply transformation if any
|
154
|
+
# # # transformed_value = apply_transformation(value, mapped_field, transformations, raw_data)
|
155
|
+
# # #
|
156
|
+
# # # processed_data[mapped_field.to_s] = transformed_value
|
157
|
+
# # # end
|
158
|
+
# # #
|
159
|
+
# # # processed_data
|
160
|
+
# # # end
|
161
|
+
# # #
|
162
|
+
# # # # Apply transformations to field values
|
163
|
+
# # # def apply_transformation(value, field, transformations, full_record)
|
164
|
+
# # # transformation = transformations[field] || transformations[field.to_sym]
|
165
|
+
# # #
|
166
|
+
# # # case transformation
|
167
|
+
# # # when Proc
|
168
|
+
# # # if transformation.arity == 2 || transformation.arity < 0
|
169
|
+
# # # transformation.call(value, full_record)
|
170
|
+
# # # else
|
171
|
+
# # # transformation.call(value)
|
172
|
+
# # # end
|
173
|
+
# # # when Symbol
|
174
|
+
# # # if self.respond_to?(transformation, true)
|
175
|
+
# # # self.send(transformation, value)
|
176
|
+
# # # elsif value.respond_to?(transformation)
|
177
|
+
# # # value.send(transformation)
|
178
|
+
# # # else
|
179
|
+
# # # Rails.logger.warn "Transformation method #{transformation} not found" if defined?(Rails)
|
180
|
+
# # # value
|
181
|
+
# # # end
|
182
|
+
# # # else
|
183
|
+
# # # value
|
184
|
+
# # # end
|
185
|
+
# # # end
|
186
|
+
# # #
|
187
|
+
# # # # Default auto-sync behavior
|
188
|
+
# # # def perform_auto_sync(model_name, action, processed_data, config)
|
189
|
+
# # # unique_fields = config[:unique_fields]
|
190
|
+
# # # sync_strategy = config[:sync_strategy]
|
191
|
+
# # #
|
192
|
+
# # # case action.to_s.downcase
|
193
|
+
# # # when 'create', 'created'
|
194
|
+
# # # handle_auto_create(processed_data, unique_fields, sync_strategy)
|
195
|
+
# # # when 'update', 'updated'
|
196
|
+
# # # handle_auto_update(processed_data, unique_fields, sync_strategy)
|
197
|
+
# # # when 'delete', 'deleted', 'destroy', 'destroyed'
|
198
|
+
# # # handle_auto_delete(processed_data, unique_fields)
|
199
|
+
# # # else
|
200
|
+
# # # Rails.logger.warn "Unknown action for auto-sync: #{action}" if defined?(Rails)
|
201
|
+
# # # end
|
202
|
+
# # # end
|
203
|
+
# # #
|
204
|
+
# # # def handle_auto_create(data, unique_fields, sync_strategy)
|
205
|
+
# # # case sync_strategy
|
206
|
+
# # # when :upsert
|
207
|
+
# # # existing = find_by_unique_fields(data, unique_fields)
|
208
|
+
# # # if existing
|
209
|
+
# # # existing.update!(data)
|
210
|
+
# # # Rails.logger.info "✅ Updated existing #{self.name}: #{existing.id}" if defined?(Rails)
|
211
|
+
# # # else
|
212
|
+
# # # record = self.create!(data)
|
213
|
+
# # # Rails.logger.info "✅ Created new #{self.name}: #{record.id}" if defined?(Rails)
|
214
|
+
# # # end
|
215
|
+
# # # when :create_only
|
216
|
+
# # # record = self.create!(data)
|
217
|
+
# # # Rails.logger.info "✅ Created new #{self.name}: #{record.id}" if defined?(Rails)
|
218
|
+
# # # end
|
219
|
+
# # # rescue => e
|
220
|
+
# # # Rails.logger.error "❌ Failed to create #{self.name}: #{e.message}" if defined?(Rails)
|
221
|
+
# # # raise
|
222
|
+
# # # end
|
223
|
+
# # #
|
224
|
+
# # # def handle_auto_update(data, unique_fields, sync_strategy)
|
225
|
+
# # # existing = find_by_unique_fields(data, unique_fields)
|
226
|
+
# # # if existing
|
227
|
+
# # # existing.update!(data)
|
228
|
+
# # # Rails.logger.info "✅ Updated #{self.name}: #{existing.id}" if defined?(Rails)
|
229
|
+
# # # elsif sync_strategy == :upsert
|
230
|
+
# # # record = self.create!(data)
|
231
|
+
# # # Rails.logger.info "✅ Created new #{self.name} during update: #{record.id}" if defined?(Rails)
|
232
|
+
# # # else
|
233
|
+
# # # Rails.logger.warn "⚠️ Record not found for update: #{data.slice(*unique_fields.map(&:to_s))}" if defined?(Rails)
|
234
|
+
# # # end
|
235
|
+
# # # rescue => e
|
236
|
+
# # # Rails.logger.error "❌ Failed to update #{self.name}: #{e.message}" if defined?(Rails)
|
237
|
+
# # # raise
|
238
|
+
# # # end
|
239
|
+
# # #
|
240
|
+
# # # def handle_auto_delete(data, unique_fields)
|
241
|
+
# # # existing = find_by_unique_fields(data, unique_fields)
|
242
|
+
# # # if existing
|
243
|
+
# # # existing.destroy!
|
244
|
+
# # # Rails.logger.info "✅ Deleted #{self.name}: #{existing.id}" if defined?(Rails)
|
245
|
+
# # # else
|
246
|
+
# # # Rails.logger.warn "⚠️ Record not found for deletion: #{data.slice(*unique_fields.map(&:to_s))}" if defined?(Rails)
|
247
|
+
# # # end
|
248
|
+
# # # rescue => e
|
249
|
+
# # # Rails.logger.error "❌ Failed to delete #{self.name}: #{e.message}" if defined?(Rails)
|
250
|
+
# # # raise
|
251
|
+
# # # end
|
252
|
+
# # #
|
253
|
+
# # # def find_by_unique_fields(data, unique_fields)
|
254
|
+
# # # conditions = {}
|
255
|
+
# # # unique_fields.each do |field|
|
256
|
+
# # # field_str = field.to_s
|
257
|
+
# # # if data.key?(field_str)
|
258
|
+
# # # conditions[field] = data[field_str]
|
259
|
+
# # # elsif data.key?(field.to_sym)
|
260
|
+
# # # conditions[field] = data[field.to_sym]
|
261
|
+
# # # end
|
262
|
+
# # # end
|
263
|
+
# # #
|
264
|
+
# # # return nil if conditions.empty?
|
265
|
+
# # # self.find_by(conditions)
|
266
|
+
# # # end
|
267
|
+
# # # end
|
268
|
+
# # #
|
269
|
+
# # # # Instance methods for publishing
|
270
|
+
# # # def nats_wave_publish(action = nil)
|
271
|
+
# # # action ||= determine_action_from_context
|
272
|
+
# # #
|
273
|
+
# # # self.class.nats_wave_publishing_configs.each do |subjects_key, config|
|
274
|
+
# # # next unless config[:enabled]
|
275
|
+
# # # next unless config[:actions].include?(action.to_sym)
|
276
|
+
# # #
|
277
|
+
# # # # Check conditions (only for publishing)
|
278
|
+
# # # if config[:conditions].any? && !evaluate_conditions(config[:conditions])
|
279
|
+
# # # Rails.logger.debug "📤 Skipping publish - conditions not met" if defined?(Rails)
|
280
|
+
# # # next
|
281
|
+
# # # end
|
282
|
+
# # #
|
283
|
+
# # # # Get the raw data (current model attributes)
|
284
|
+
# # # raw_data = get_raw_attributes
|
285
|
+
# # #
|
286
|
+
# # # # Process the data through mappings and transformations (same as subscription)
|
287
|
+
# # # processed_data = self.class.process_data(raw_data, config)
|
288
|
+
# # #
|
289
|
+
# # # # If custom handler is provided, call it
|
290
|
+
# # # if config[:handler]
|
291
|
+
# # # Rails.logger.debug "📤 Calling custom publishing handler" if defined?(Rails)
|
292
|
+
# # # config[:handler].call(self.class.name, action, processed_data, self)
|
293
|
+
# # # else
|
294
|
+
# # # # Default publishing behavior - publish the processed data
|
295
|
+
# # # config[:subjects].each do |subject|
|
296
|
+
# # # NatsWave.client.publish(
|
297
|
+
# # # subject: subject,
|
298
|
+
# # # model: self.class.name,
|
299
|
+
# # # action: action,
|
300
|
+
# # # data: processed_data, # This is the processed data, not raw attributes
|
301
|
+
# # # metadata: build_publishing_metadata
|
302
|
+
# # # )
|
303
|
+
# # #
|
304
|
+
# # # Rails.logger.info "📤 Published #{self.class.name}##{id} to #{subject} (#{action})" if defined?(Rails)
|
305
|
+
# # # end
|
306
|
+
# # # end
|
307
|
+
# # # end
|
308
|
+
# # # rescue StandardError => e
|
309
|
+
# # # Rails.logger.error("Failed to publish: #{e.message}") if defined?(Rails)
|
310
|
+
# # # # Don't re-raise to avoid breaking transactions
|
311
|
+
# # # end
|
312
|
+
# # #
|
313
|
+
# # # private
|
314
|
+
# # #
|
315
|
+
# # # def determine_action_from_context
|
316
|
+
# # # # Try to determine action from ActiveRecord context
|
317
|
+
# # # if defined?(ActiveRecord) && is_a?(ActiveRecord::Base)
|
318
|
+
# # # return 'create' if previously_new_record?
|
319
|
+
# # # return 'update' if saved_changes.any?
|
320
|
+
# # # return 'destroy' if destroyed?
|
321
|
+
# # # end
|
322
|
+
# # #
|
323
|
+
# # # 'update' # Default fallback
|
324
|
+
# # # end
|
325
|
+
# # #
|
326
|
+
# # # def get_raw_attributes
|
327
|
+
# # # # Get attributes (works for both ActiveRecord and other objects)
|
328
|
+
# # # if respond_to?(:attributes)
|
329
|
+
# # # attributes
|
330
|
+
# # # else
|
331
|
+
# # # # For non-ActiveRecord objects, get instance variables
|
332
|
+
# # # instance_variables.map { |v| [v.to_s.delete('@'), instance_variable_get(v)] }.to_h
|
333
|
+
# # # end
|
334
|
+
# # # end
|
335
|
+
# # #
|
336
|
+
# # # def evaluate_conditions(conditions)
|
337
|
+
# # # conditions.all? do |condition, expected_value|
|
338
|
+
# # # case condition
|
339
|
+
# # # when Proc
|
340
|
+
# # # condition.call(self)
|
341
|
+
# # # when Symbol, String
|
342
|
+
# # # if respond_to?(condition, true)
|
343
|
+
# # # result = send(condition)
|
344
|
+
# # # expected_value.nil? ? result : result == expected_value
|
345
|
+
# # # else
|
346
|
+
# # # false
|
347
|
+
# # # end
|
348
|
+
# # # else
|
349
|
+
# # # false
|
350
|
+
# # # end
|
351
|
+
# # # end
|
352
|
+
# # # end
|
353
|
+
# # #
|
354
|
+
# # # def build_publishing_metadata
|
355
|
+
# # # {
|
356
|
+
# # # source_model: self.class.name,
|
357
|
+
# # # source_id: respond_to?(:id) ? id : object_id,
|
358
|
+
# # # published_at: Time.current.iso8601
|
359
|
+
# # # }
|
360
|
+
# # # end
|
361
|
+
# # # end
|
362
|
+
# # # end
|
363
|
+
# # # end
|
364
|
+
# #
|
365
|
+
# # # frozen_string_literal: true
|
366
|
+
# #
|
367
|
+
# # module NatsWave
|
368
|
+
# # module Concerns
|
369
|
+
# # module Mappable
|
370
|
+
# # extend ActiveSupport::Concern
|
371
|
+
# #
|
372
|
+
# # included do
|
373
|
+
# # class_attribute :nats_wave_subscription_config, :nats_wave_publishing_config
|
374
|
+
# # self.nats_wave_subscription_config = {}
|
375
|
+
# # self.nats_wave_publishing_config = {}
|
376
|
+
# # end
|
377
|
+
# #
|
378
|
+
# # class_methods do
|
379
|
+
# # # Configure subscriptions with optional custom handler
|
380
|
+
# # def nats_wave_subscribes_to(*subjects, **options, &block)
|
381
|
+
# # Rails.logger.debug "📡 #{self.name}: Setting up subscription to subjects: #{subjects.inspect}" if defined?(Rails)
|
382
|
+
# #
|
383
|
+
# # handler = options[:handler] || block
|
384
|
+
# #
|
385
|
+
# # subscription_config = {
|
386
|
+
# # subjects: subjects.flatten,
|
387
|
+
# # field_mappings: options[:field_mappings] || {},
|
388
|
+
# # transformations: options[:transformations] || {},
|
389
|
+
# # skip_fields: options[:skip_fields] || [],
|
390
|
+
# # unique_fields: options[:unique_fields] || [:id],
|
391
|
+
# # sync_strategy: options[:sync_strategy] || :upsert,
|
392
|
+
# # handler: handler,
|
393
|
+
# # queue_group: options[:queue_group],
|
394
|
+
# # auto_sync: options[:auto_sync] != false # Default to true, can be disabled
|
395
|
+
# # }
|
396
|
+
# #
|
397
|
+
# # # Store the config
|
398
|
+
# # config_key = subjects.join(',')
|
399
|
+
# # self.nats_wave_subscription_config[config_key] = subscription_config
|
400
|
+
# #
|
401
|
+
# # # Create the subscription handler
|
402
|
+
# # processed_handler = create_subscription_handler(subscription_config)
|
403
|
+
# #
|
404
|
+
# # # Register the subscription
|
405
|
+
# # NatsWave::ModelRegistry.register_subscription(
|
406
|
+
# # subjects: subjects.flatten,
|
407
|
+
# # model: self.name,
|
408
|
+
# # handler: processed_handler,
|
409
|
+
# # queue_group: subscription_config[:queue_group]
|
410
|
+
# # )
|
411
|
+
# #
|
412
|
+
# # Rails.logger.debug "📡 #{self.name}: Registered subscription for subjects: #{subjects}" if defined?(Rails)
|
413
|
+
# # end
|
414
|
+
# #
|
415
|
+
# # # Configure publishing with optional custom handler AND auto-publishing callbacks
|
416
|
+
# # def nats_wave_publishes_to(*subjects, **options, &block)
|
417
|
+
# # Rails.logger.debug "📤 #{self.name}: Setting up publishing to subjects: #{subjects.inspect}" if defined?(Rails)
|
418
|
+
# #
|
419
|
+
# # handler = options[:handler] || block
|
420
|
+
# #
|
421
|
+
# # publishing_config = {
|
422
|
+
# # subjects: subjects.flatten,
|
423
|
+
# # field_mappings: options[:field_mappings] || {},
|
424
|
+
# # transformations: options[:transformations] || {},
|
425
|
+
# # skip_fields: options[:skip_fields] || [],
|
426
|
+
# # conditions: options[:conditions] || {},
|
427
|
+
# # actions: options[:actions] || [:create, :update, :destroy],
|
428
|
+
# # handler: handler,
|
429
|
+
# # async: options[:async] != false, # Default to true
|
430
|
+
# # enabled: options[:enabled] != false # Default to true
|
431
|
+
# # }
|
432
|
+
# #
|
433
|
+
# # # Store the config
|
434
|
+
# # config_key = subjects.join(',')
|
435
|
+
# # self.nats_wave_publishing_config[config_key] = publishing_config
|
436
|
+
# #
|
437
|
+
# # # Add ActiveRecord callbacks for auto-publishing
|
438
|
+
# # setup_publishing_callbacks(publishing_config)
|
439
|
+
# #
|
440
|
+
# # Rails.logger.debug "📤 #{self.name}: Registered publishing config for subjects: #{subjects}" if defined?(Rails)
|
441
|
+
# # end
|
442
|
+
# #
|
443
|
+
# # # Get all subscription configurations
|
444
|
+
# # def nats_wave_subscription_configs
|
445
|
+
# # nats_wave_subscription_config
|
446
|
+
# # end
|
447
|
+
# #
|
448
|
+
# # # Get all publishing configurations
|
449
|
+
# # def nats_wave_publishing_configs
|
450
|
+
# # nats_wave_publishing_config
|
451
|
+
# # end
|
452
|
+
# #
|
453
|
+
# # # Get all subjects this model subscribes to
|
454
|
+
# # def nats_wave_subscribed_subjects
|
455
|
+
# # nats_wave_subscription_config.values.flat_map { |config| config[:subjects] }.uniq
|
456
|
+
# # end
|
457
|
+
# #
|
458
|
+
# # # Get all subjects this model publishes to
|
459
|
+
# # def nats_wave_published_subjects
|
460
|
+
# # nats_wave_publishing_config.values.flat_map { |config| config[:subjects] }.uniq
|
461
|
+
# # end
|
462
|
+
# #
|
463
|
+
# # private
|
464
|
+
# #
|
465
|
+
# # # Setup ActiveRecord callbacks for auto-publishing
|
466
|
+
# # def setup_publishing_callbacks(config)
|
467
|
+
# # # Only add callbacks if we're in ActiveRecord and actions are specified
|
468
|
+
# # return unless defined?(ActiveRecord::Base) && self < ActiveRecord::Base
|
469
|
+
# #
|
470
|
+
# # actions = config[:actions]
|
471
|
+
# #
|
472
|
+
# # if actions.include?(:create)
|
473
|
+
# # after_commit :trigger_nats_wave_auto_publish_on_create, on: :create
|
474
|
+
# # end
|
475
|
+
# #
|
476
|
+
# # if actions.include?(:update)
|
477
|
+
# # after_commit :trigger_nats_wave_auto_publish_on_update, on: :update
|
478
|
+
# # end
|
479
|
+
# #
|
480
|
+
# # if actions.include?(:destroy)
|
481
|
+
# # after_commit :trigger_nats_wave_auto_publish_on_destroy, on: :destroy
|
482
|
+
# # end
|
483
|
+
# # end
|
484
|
+
# #
|
485
|
+
# # # Create a subscription handler that processes data and calls custom handler or auto-sync
|
486
|
+
# # def create_subscription_handler(config)
|
487
|
+
# # model_class = self
|
488
|
+
# #
|
489
|
+
# # lambda do |message|
|
490
|
+
# # begin
|
491
|
+
# # Rails.logger.debug "📨 Processing subscription message for #{model_class.name}" if defined?(Rails)
|
492
|
+
# #
|
493
|
+
# # # Extract the raw data
|
494
|
+
# # raw_data = message['data'] || {}
|
495
|
+
# # model_name = message['model']
|
496
|
+
# # action = message['action']
|
497
|
+
# #
|
498
|
+
# # # Process the data through mappings and transformations
|
499
|
+
# # processed_data = process_subscription_data(raw_data, config)
|
500
|
+
# #
|
501
|
+
# # # If custom handler is provided, call it with model_name, action, processed_data
|
502
|
+
# # if config[:handler]
|
503
|
+
# # Rails.logger.debug "📨 Calling custom subscription handler" if defined?(Rails)
|
504
|
+
# # config[:handler].call(model_name, action, processed_data, message)
|
505
|
+
# # elsif config[:auto_sync]
|
506
|
+
# # Rails.logger.debug "📨 Performing auto-sync" if defined?(Rails)
|
507
|
+
# # # Default auto-sync behavior
|
508
|
+
# # model_class.perform_auto_sync(model_name, action, processed_data, config)
|
509
|
+
# # else
|
510
|
+
# # Rails.logger.debug "📨 No handler provided and auto_sync disabled" if defined?(Rails)
|
511
|
+
# # end
|
512
|
+
# #
|
513
|
+
# # rescue => e
|
514
|
+
# # Rails.logger.error "Error in subscription handler for #{model_class.name}: #{e.message}" if defined?(Rails)
|
515
|
+
# # Rails.logger.error e.backtrace.join("\n") if defined?(Rails)
|
516
|
+
# # raise
|
517
|
+
# # end
|
518
|
+
# # end
|
519
|
+
# # end
|
520
|
+
# #
|
521
|
+
# # # Process subscription data through field mappings and transformations (CLASS METHOD)
|
522
|
+
# # def process_subscription_data(raw_data, config)
|
523
|
+
# # processed_data = {}
|
524
|
+
# # field_mappings = config[:field_mappings]
|
525
|
+
# # transformations = config[:transformations]
|
526
|
+
# # skip_fields = config[:skip_fields]
|
527
|
+
# #
|
528
|
+
# # raw_data.each do |field, value|
|
529
|
+
# # field_str = field.to_s
|
530
|
+
# # field_sym = field.to_sym
|
531
|
+
# #
|
532
|
+
# # # Skip if in skip_fields
|
533
|
+
# # next if skip_fields.include?(field_str) || skip_fields.include?(field_sym)
|
534
|
+
# #
|
535
|
+
# # # Apply field mapping
|
536
|
+
# # mapped_field = field_mappings[field_str] ||
|
537
|
+
# # field_mappings[field_sym] ||
|
538
|
+
# # field
|
539
|
+
# #
|
540
|
+
# # # Apply transformation if any
|
541
|
+
# # transformed_value = apply_transformation(value, mapped_field, transformations, raw_data)
|
542
|
+
# #
|
543
|
+
# # processed_data[mapped_field.to_s] = transformed_value
|
544
|
+
# # end
|
545
|
+
# #
|
546
|
+
# # processed_data
|
547
|
+
# # end
|
548
|
+
# #
|
549
|
+
# # # Apply transformations to field values (CLASS METHOD)
|
550
|
+
# # def apply_transformation(value, field, transformations, full_record)
|
551
|
+
# # transformation = transformations[field] || transformations[field.to_sym]
|
552
|
+
# #
|
553
|
+
# # case transformation
|
554
|
+
# # when Proc
|
555
|
+
# # if transformation.arity == 2 || transformation.arity < 0
|
556
|
+
# # transformation.call(value, full_record)
|
557
|
+
# # else
|
558
|
+
# # transformation.call(value)
|
559
|
+
# # end
|
560
|
+
# # when Symbol
|
561
|
+
# # if self.respond_to?(transformation, true)
|
562
|
+
# # self.send(transformation, value)
|
563
|
+
# # elsif value.respond_to?(transformation)
|
564
|
+
# # value.send(transformation)
|
565
|
+
# # else
|
566
|
+
# # Rails.logger.warn "Transformation method #{transformation} not found" if defined?(Rails)
|
567
|
+
# # value
|
568
|
+
# # end
|
569
|
+
# # else
|
570
|
+
# # value
|
571
|
+
# # end
|
572
|
+
# # end
|
573
|
+
# #
|
574
|
+
# # # Default auto-sync behavior (CLASS METHOD)
|
575
|
+
# # def perform_auto_sync(model_name, action, processed_data, config)
|
576
|
+
# # unique_fields = config[:unique_fields]
|
577
|
+
# # sync_strategy = config[:sync_strategy]
|
578
|
+
# #
|
579
|
+
# # case action.to_s.downcase
|
580
|
+
# # when 'create', 'created'
|
581
|
+
# # handle_auto_create(processed_data, unique_fields, sync_strategy)
|
582
|
+
# # when 'update', 'updated'
|
583
|
+
# # handle_auto_update(processed_data, unique_fields, sync_strategy)
|
584
|
+
# # when 'delete', 'deleted', 'destroy', 'destroyed'
|
585
|
+
# # handle_auto_delete(processed_data, unique_fields)
|
586
|
+
# # else
|
587
|
+
# # Rails.logger.warn "Unknown action for auto-sync: #{action}" if defined?(Rails)
|
588
|
+
# # end
|
589
|
+
# # end
|
590
|
+
# #
|
591
|
+
# # def handle_auto_create(data, unique_fields, sync_strategy)
|
592
|
+
# # case sync_strategy
|
593
|
+
# # when :upsert
|
594
|
+
# # existing = find_by_unique_fields(data, unique_fields)
|
595
|
+
# # if existing
|
596
|
+
# # existing.update!(data)
|
597
|
+
# # Rails.logger.info "✅ Updated existing #{self.name}: #{existing.id}" if defined?(Rails)
|
598
|
+
# # else
|
599
|
+
# # record = self.create!(data)
|
600
|
+
# # Rails.logger.info "✅ Created new #{self.name}: #{record.id}" if defined?(Rails)
|
601
|
+
# # end
|
602
|
+
# # when :create_only
|
603
|
+
# # record = self.create!(data)
|
604
|
+
# # Rails.logger.info "✅ Created new #{self.name}: #{record.id}" if defined?(Rails)
|
605
|
+
# # end
|
606
|
+
# # rescue => e
|
607
|
+
# # Rails.logger.error "❌ Failed to create #{self.name}: #{e.message}" if defined?(Rails)
|
608
|
+
# # raise
|
609
|
+
# # end
|
610
|
+
# #
|
611
|
+
# # def handle_auto_update(data, unique_fields, sync_strategy)
|
612
|
+
# # existing = find_by_unique_fields(data, unique_fields)
|
613
|
+
# # if existing
|
614
|
+
# # existing.update!(data)
|
615
|
+
# # Rails.logger.info "✅ Updated #{self.name}: #{existing.id}" if defined?(Rails)
|
616
|
+
# # elsif sync_strategy == :upsert
|
617
|
+
# # record = self.create!(data)
|
618
|
+
# # Rails.logger.info "✅ Created new #{self.name} during update: #{record.id}" if defined?(Rails)
|
619
|
+
# # else
|
620
|
+
# # Rails.logger.warn "⚠️ Record not found for update: #{data.slice(*unique_fields.map(&:to_s))}" if defined?(Rails)
|
621
|
+
# # end
|
622
|
+
# # rescue => e
|
623
|
+
# # Rails.logger.error "❌ Failed to update #{self.name}: #{e.message}" if defined?(Rails)
|
624
|
+
# # raise
|
625
|
+
# # end
|
626
|
+
# #
|
627
|
+
# # def handle_auto_delete(data, unique_fields)
|
628
|
+
# # existing = find_by_unique_fields(data, unique_fields)
|
629
|
+
# # if existing
|
630
|
+
# # existing.destroy!
|
631
|
+
# # Rails.logger.info "✅ Deleted #{self.name}: #{existing.id}" if defined?(Rails)
|
632
|
+
# # else
|
633
|
+
# # Rails.logger.warn "⚠️ Record not found for deletion: #{data.slice(*unique_fields.map(&:to_s))}" if defined?(Rails)
|
634
|
+
# # end
|
635
|
+
# # rescue => e
|
636
|
+
# # Rails.logger.error "❌ Failed to delete #{self.name}: #{e.message}" if defined?(Rails)
|
637
|
+
# # raise
|
638
|
+
# # end
|
639
|
+
# #
|
640
|
+
# # def find_by_unique_fields(data, unique_fields)
|
641
|
+
# # conditions = {}
|
642
|
+
# # unique_fields.each do |field|
|
643
|
+
# # field_str = field.to_s
|
644
|
+
# # if data.key?(field_str)
|
645
|
+
# # conditions[field] = data[field_str]
|
646
|
+
# # elsif data.key?(field.to_sym)
|
647
|
+
# # conditions[field] = data[field.to_sym]
|
648
|
+
# # end
|
649
|
+
# # end
|
650
|
+
# #
|
651
|
+
# # return nil if conditions.empty?
|
652
|
+
# # self.find_by(conditions)
|
653
|
+
# # end
|
654
|
+
# # end
|
655
|
+
# #
|
656
|
+
# # # Instance methods for publishing
|
657
|
+
# # def nats_wave_publish(action = nil)
|
658
|
+
# # action ||= determine_action_from_context
|
659
|
+
# #
|
660
|
+
# # self.class.nats_wave_publishing_configs.each do |subjects_key, config|
|
661
|
+
# # next unless config[:enabled]
|
662
|
+
# # next unless config[:actions].include?(action.to_sym)
|
663
|
+
# #
|
664
|
+
# # # Check conditions (only for publishing)
|
665
|
+
# # if config[:conditions].any? && !evaluate_conditions(config[:conditions])
|
666
|
+
# # Rails.logger.debug "📤 Skipping publish - conditions not met" if defined?(Rails)
|
667
|
+
# # next
|
668
|
+
# # end
|
669
|
+
# #
|
670
|
+
# # # Get the raw data (current model attributes)
|
671
|
+
# # raw_data = get_raw_attributes
|
672
|
+
# #
|
673
|
+
# # # Process the data through mappings and transformations
|
674
|
+
# # processed_data = process_publishing_data(raw_data, config)
|
675
|
+
# #
|
676
|
+
# # # If custom handler is provided, call it
|
677
|
+
# # if config[:handler]
|
678
|
+
# # Rails.logger.debug "📤 Calling custom publishing handler" if defined?(Rails)
|
679
|
+
# # config[:handler].call(self.class.name, action, processed_data, self)
|
680
|
+
# # else
|
681
|
+
# # # Default publishing behavior - publish the processed data
|
682
|
+
# # config[:subjects].each do |subject|
|
683
|
+
# # NatsWave.client.publish(
|
684
|
+
# # subject: subject,
|
685
|
+
# # model: self.class.name,
|
686
|
+
# # action: action,
|
687
|
+
# # data: processed_data, # This is the processed data, not raw attributes
|
688
|
+
# # metadata: build_publishing_metadata
|
689
|
+
# # )
|
690
|
+
# #
|
691
|
+
# # Rails.logger.info "📤 Published #{self.class.name}##{id} to #{subject} (#{action})" if defined?(Rails)
|
692
|
+
# # end
|
693
|
+
# # end
|
694
|
+
# # end
|
695
|
+
# # rescue StandardError => e
|
696
|
+
# # Rails.logger.error("Failed to publish: #{e.message}") if defined?(Rails)
|
697
|
+
# # # Don't re-raise to avoid breaking transactions
|
698
|
+
# # end
|
699
|
+
# #
|
700
|
+
# # # Auto-publishing callback methods
|
701
|
+
# # def trigger_nats_wave_auto_publish_on_create
|
702
|
+
# # Rails.logger.debug "🚀 Auto-publishing on create" if defined?(Rails)
|
703
|
+
# # nats_wave_publish('create')
|
704
|
+
# # end
|
705
|
+
# #
|
706
|
+
# # def trigger_nats_wave_auto_publish_on_update
|
707
|
+
# # Rails.logger.debug "🚀 Auto-publishing on update" if defined?(Rails)
|
708
|
+
# # nats_wave_publish('update')
|
709
|
+
# # end
|
710
|
+
# #
|
711
|
+
# # def trigger_nats_wave_auto_publish_on_destroy
|
712
|
+
# # Rails.logger.debug "🚀 Auto-publishing on destroy" if defined?(Rails)
|
713
|
+
# # nats_wave_publish('destroy')
|
714
|
+
# # end
|
715
|
+
# #
|
716
|
+
# # private
|
717
|
+
# #
|
718
|
+
# # def determine_action_from_context
|
719
|
+
# # # Try to determine action from ActiveRecord context
|
720
|
+
# # if defined?(ActiveRecord) && is_a?(ActiveRecord::Base)
|
721
|
+
# # return 'create' if previously_new_record?
|
722
|
+
# # return 'update' if saved_changes.any?
|
723
|
+
# # return 'destroy' if destroyed?
|
724
|
+
# # end
|
725
|
+
# #
|
726
|
+
# # 'update' # Default fallback
|
727
|
+
# # end
|
728
|
+
# #
|
729
|
+
# # def get_raw_attributes
|
730
|
+
# # # Get attributes (works for both ActiveRecord and other objects)
|
731
|
+
# # if respond_to?(:attributes)
|
732
|
+
# # attributes
|
733
|
+
# # else
|
734
|
+
# # # For non-ActiveRecord objects, get instance variables
|
735
|
+
# # instance_variables.map { |v| [v.to_s.delete('@'), instance_variable_get(v)] }.to_h
|
736
|
+
# # end
|
737
|
+
# # end
|
738
|
+
# #
|
739
|
+
# # # Process publishing data through field mappings and transformations (INSTANCE METHOD)
|
740
|
+
# # def process_publishing_data(raw_data, config)
|
741
|
+
# # processed_data = {}
|
742
|
+
# # field_mappings = config[:field_mappings]
|
743
|
+
# # transformations = config[:transformations]
|
744
|
+
# # skip_fields = config[:skip_fields]
|
745
|
+
# #
|
746
|
+
# # raw_data.each do |field, value|
|
747
|
+
# # field_str = field.to_s
|
748
|
+
# # field_sym = field.to_sym
|
749
|
+
# #
|
750
|
+
# # # Skip if in skip_fields
|
751
|
+
# # next if skip_fields.include?(field_str) || skip_fields.include?(field_sym)
|
752
|
+
# #
|
753
|
+
# # # Apply field mapping (local -> external)
|
754
|
+
# # mapped_field = field_mappings[field_str] ||
|
755
|
+
# # field_mappings[field_sym] ||
|
756
|
+
# # field
|
757
|
+
# #
|
758
|
+
# # # Apply transformation if any
|
759
|
+
# # transformed_value = apply_publishing_transformation(value, mapped_field, transformations, raw_data)
|
760
|
+
# #
|
761
|
+
# # processed_data[mapped_field.to_s] = transformed_value
|
762
|
+
# # end
|
763
|
+
# #
|
764
|
+
# # processed_data
|
765
|
+
# # end
|
766
|
+
# #
|
767
|
+
# # def apply_publishing_transformation(value, field, transformations, full_record)
|
768
|
+
# # transformation = transformations[field] || transformations[field.to_sym]
|
769
|
+
# #
|
770
|
+
# # case transformation
|
771
|
+
# # when Proc
|
772
|
+
# # if transformation.arity == 2 || transformation.arity < 0
|
773
|
+
# # transformation.call(value, full_record)
|
774
|
+
# # else
|
775
|
+
# # transformation.call(value)
|
776
|
+
# # end
|
777
|
+
# # when Symbol
|
778
|
+
# # if self.respond_to?(transformation, true)
|
779
|
+
# # self.send(transformation, value)
|
780
|
+
# # elsif value.respond_to?(transformation)
|
781
|
+
# # value.send(transformation)
|
782
|
+
# # else
|
783
|
+
# # Rails.logger.warn "Publishing transformation method #{transformation} not found" if defined?(Rails)
|
784
|
+
# # value
|
785
|
+
# # end
|
786
|
+
# # else
|
787
|
+
# # value
|
788
|
+
# # end
|
789
|
+
# # end
|
790
|
+
# #
|
791
|
+
# # def evaluate_conditions(conditions)
|
792
|
+
# # conditions.all? do |condition, expected_value|
|
793
|
+
# # case condition
|
794
|
+
# # when Proc
|
795
|
+
# # condition.call(self)
|
796
|
+
# # when Symbol, String
|
797
|
+
# # if respond_to?(condition, true)
|
798
|
+
# # result = send(condition)
|
799
|
+
# # expected_value.nil? ? result : result == expected_value
|
800
|
+
# # else
|
801
|
+
# # false
|
802
|
+
# # end
|
803
|
+
# # else
|
804
|
+
# # false
|
805
|
+
# # end
|
806
|
+
# # end
|
807
|
+
# # end
|
808
|
+
# #
|
809
|
+
# # def build_publishing_metadata
|
810
|
+
# # {
|
811
|
+
# # source_model: self.class.name,
|
812
|
+
# # source_id: respond_to?(:id) ? id : object_id,
|
813
|
+
# # published_at: Time.current.iso8601
|
814
|
+
# # }
|
815
|
+
# # end
|
816
|
+
# # end
|
817
|
+
# # end
|
818
|
+
# # end
|
819
|
+
#
|
1
820
|
# # frozen_string_literal: true
|
2
821
|
#
|
3
822
|
# module NatsWave
|
@@ -16,7 +835,8 @@
|
|
16
835
|
# def nats_wave_subscribes_to(*subjects, **options, &block)
|
17
836
|
# Rails.logger.debug "📡 #{self.name}: Setting up subscription to subjects: #{subjects.inspect}" if defined?(Rails)
|
18
837
|
#
|
19
|
-
#
|
838
|
+
# # Custom handler is now optional and runs AFTER default behavior
|
839
|
+
# custom_handler = options[:handler] || block
|
20
840
|
#
|
21
841
|
# subscription_config = {
|
22
842
|
# subjects: subjects.flatten,
|
@@ -25,7 +845,7 @@
|
|
25
845
|
# skip_fields: options[:skip_fields] || [],
|
26
846
|
# unique_fields: options[:unique_fields] || [:id],
|
27
847
|
# sync_strategy: options[:sync_strategy] || :upsert,
|
28
|
-
#
|
848
|
+
# custom_handler: custom_handler, # Renamed to be clear it's optional
|
29
849
|
# queue_group: options[:queue_group],
|
30
850
|
# auto_sync: options[:auto_sync] != false # Default to true, can be disabled
|
31
851
|
# }
|
@@ -48,11 +868,12 @@
|
|
48
868
|
# Rails.logger.debug "📡 #{self.name}: Registered subscription for subjects: #{subjects}" if defined?(Rails)
|
49
869
|
# end
|
50
870
|
#
|
51
|
-
# # Configure publishing with optional custom handler
|
871
|
+
# # Configure publishing with optional custom handler AND auto-publishing callbacks
|
52
872
|
# def nats_wave_publishes_to(*subjects, **options, &block)
|
53
873
|
# Rails.logger.debug "📤 #{self.name}: Setting up publishing to subjects: #{subjects.inspect}" if defined?(Rails)
|
54
874
|
#
|
55
|
-
#
|
875
|
+
# # Custom handler is now optional and runs AFTER default behavior
|
876
|
+
# custom_handler = options[:handler] || block
|
56
877
|
#
|
57
878
|
# publishing_config = {
|
58
879
|
# subjects: subjects.flatten,
|
@@ -61,15 +882,19 @@
|
|
61
882
|
# skip_fields: options[:skip_fields] || [],
|
62
883
|
# conditions: options[:conditions] || {},
|
63
884
|
# actions: options[:actions] || [:create, :update, :destroy],
|
64
|
-
#
|
885
|
+
# custom_handler: custom_handler, # Renamed to be clear it's optional
|
65
886
|
# async: options[:async] != false, # Default to true
|
66
|
-
# enabled: options[:enabled] != false # Default to true
|
887
|
+
# enabled: options[:enabled] != false, # Default to true
|
888
|
+
# only_mapped_fields: options[:only_mapped_fields] != false # Default to true - only send mapped fields
|
67
889
|
# }
|
68
890
|
#
|
69
891
|
# # Store the config
|
70
892
|
# config_key = subjects.join(',')
|
71
893
|
# self.nats_wave_publishing_config[config_key] = publishing_config
|
72
894
|
#
|
895
|
+
# # Add ActiveRecord callbacks for auto-publishing (only once per model)
|
896
|
+
# setup_publishing_callbacks_once
|
897
|
+
#
|
73
898
|
# Rails.logger.debug "📤 #{self.name}: Registered publishing config for subjects: #{subjects}" if defined?(Rails)
|
74
899
|
# end
|
75
900
|
#
|
@@ -83,19 +908,23 @@
|
|
83
908
|
# nats_wave_publishing_config
|
84
909
|
# end
|
85
910
|
#
|
86
|
-
#
|
87
|
-
# def nats_wave_subscribed_subjects
|
88
|
-
# nats_wave_subscription_config.values.flat_map { |config| config[:subjects] }.uniq
|
89
|
-
# end
|
911
|
+
# private
|
90
912
|
#
|
91
|
-
# #
|
92
|
-
# def
|
93
|
-
#
|
94
|
-
#
|
913
|
+
# # Setup ActiveRecord callbacks for auto-publishing (only once per model)
|
914
|
+
# def setup_publishing_callbacks_once
|
915
|
+
# # Only add callbacks if we're in ActiveRecord and haven't added them yet
|
916
|
+
# return unless defined?(ActiveRecord::Base) && self < ActiveRecord::Base
|
917
|
+
# return if @nats_wave_callbacks_added
|
95
918
|
#
|
96
|
-
#
|
919
|
+
# after_commit :trigger_nats_wave_auto_publish_on_create, on: :create
|
920
|
+
# after_commit :trigger_nats_wave_auto_publish_on_update, on: :update
|
921
|
+
# after_commit :trigger_nats_wave_auto_publish_on_destroy, on: :destroy
|
97
922
|
#
|
98
|
-
#
|
923
|
+
# @nats_wave_callbacks_added = true
|
924
|
+
# Rails.logger.debug "📤 #{self.name}: Added publishing callbacks" if defined?(Rails)
|
925
|
+
# end
|
926
|
+
#
|
927
|
+
# # Create a subscription handler that processes data and calls custom handler AFTER auto-sync
|
99
928
|
# def create_subscription_handler(config)
|
100
929
|
# model_class = self
|
101
930
|
#
|
@@ -109,18 +938,18 @@
|
|
109
938
|
# action = message['action']
|
110
939
|
#
|
111
940
|
# # Process the data through mappings and transformations
|
112
|
-
# processed_data =
|
113
|
-
#
|
114
|
-
# #
|
115
|
-
# if config[:
|
116
|
-
# Rails.logger.debug "📨
|
117
|
-
# config[:handler].call(model_name, action, processed_data, message)
|
118
|
-
# elsif config[:auto_sync]
|
119
|
-
# Rails.logger.debug "📨 Performing auto-sync" if defined?(Rails)
|
120
|
-
# # Default auto-sync behavior
|
941
|
+
# processed_data = process_subscription_data(raw_data, config)
|
942
|
+
#
|
943
|
+
# # Always perform auto-sync first (if enabled)
|
944
|
+
# if config[:auto_sync]
|
945
|
+
# Rails.logger.debug "📨 Performing auto-sync first" if defined?(Rails)
|
121
946
|
# model_class.perform_auto_sync(model_name, action, processed_data, config)
|
122
|
-
#
|
123
|
-
#
|
947
|
+
# end
|
948
|
+
#
|
949
|
+
# # Then call custom handler if provided (optional)
|
950
|
+
# if config[:custom_handler]
|
951
|
+
# Rails.logger.debug "📨 Calling custom subscription handler after auto-sync" if defined?(Rails)
|
952
|
+
# config[:custom_handler].call(model_name, action, processed_data, message)
|
124
953
|
# end
|
125
954
|
#
|
126
955
|
# rescue => e
|
@@ -131,8 +960,8 @@
|
|
131
960
|
# end
|
132
961
|
# end
|
133
962
|
#
|
134
|
-
# # Process data through field mappings and transformations (
|
135
|
-
# def
|
963
|
+
# # Process subscription data through field mappings and transformations (CLASS METHOD)
|
964
|
+
# def process_subscription_data(raw_data, config)
|
136
965
|
# processed_data = {}
|
137
966
|
# field_mappings = config[:field_mappings]
|
138
967
|
# transformations = config[:transformations]
|
@@ -159,7 +988,7 @@
|
|
159
988
|
# processed_data
|
160
989
|
# end
|
161
990
|
#
|
162
|
-
# # Apply transformations to field values
|
991
|
+
# # Apply transformations to field values (CLASS METHOD)
|
163
992
|
# def apply_transformation(value, field, transformations, full_record)
|
164
993
|
# transformation = transformations[field] || transformations[field.to_sym]
|
165
994
|
#
|
@@ -184,7 +1013,7 @@
|
|
184
1013
|
# end
|
185
1014
|
# end
|
186
1015
|
#
|
187
|
-
# # Default auto-sync behavior
|
1016
|
+
# # Default auto-sync behavior (CLASS METHOD)
|
188
1017
|
# def perform_auto_sync(model_name, action, processed_data, config)
|
189
1018
|
# unique_fields = config[:unique_fields]
|
190
1019
|
# sync_strategy = config[:sync_strategy]
|
@@ -283,26 +1112,29 @@
|
|
283
1112
|
# # Get the raw data (current model attributes)
|
284
1113
|
# raw_data = get_raw_attributes
|
285
1114
|
#
|
286
|
-
# # Process the data through mappings and transformations
|
287
|
-
# processed_data =
|
1115
|
+
# # Process the data through mappings and transformations
|
1116
|
+
# processed_data = process_publishing_data(raw_data, config)
|
288
1117
|
#
|
289
|
-
# #
|
290
|
-
#
|
291
|
-
#
|
292
|
-
#
|
293
|
-
#
|
294
|
-
#
|
295
|
-
#
|
296
|
-
#
|
297
|
-
#
|
298
|
-
#
|
299
|
-
#
|
300
|
-
#
|
301
|
-
#
|
302
|
-
#
|
303
|
-
#
|
304
|
-
#
|
305
|
-
#
|
1118
|
+
# # Determine which subjects to publish to based on action
|
1119
|
+
# subjects_to_publish = determine_subjects_for_action(config[:subjects], action)
|
1120
|
+
#
|
1121
|
+
# # Always publish first (default behavior)
|
1122
|
+
# subjects_to_publish.each do |subject|
|
1123
|
+
# NatsWave.client.publish(
|
1124
|
+
# subject: subject,
|
1125
|
+
# model: self.class.name,
|
1126
|
+
# action: action,
|
1127
|
+
# data: processed_data, # This is the processed data, not raw attributes
|
1128
|
+
# metadata: build_publishing_metadata
|
1129
|
+
# )
|
1130
|
+
#
|
1131
|
+
# Rails.logger.info "📤 Published #{self.class.name}##{id} to #{subject} (#{action})" if defined?(Rails)
|
1132
|
+
# end
|
1133
|
+
#
|
1134
|
+
# # Then call custom handler if provided (optional)
|
1135
|
+
# if config[:custom_handler]
|
1136
|
+
# Rails.logger.debug "📤 Calling custom publishing handler after publishing" if defined?(Rails)
|
1137
|
+
# config[:custom_handler].call(self.class.name, action, processed_data, self)
|
306
1138
|
# end
|
307
1139
|
# end
|
308
1140
|
# rescue StandardError => e
|
@@ -310,6 +1142,37 @@
|
|
310
1142
|
# # Don't re-raise to avoid breaking transactions
|
311
1143
|
# end
|
312
1144
|
#
|
1145
|
+
# # Auto-publishing callback methods (only called once per action)
|
1146
|
+
# def trigger_nats_wave_auto_publish_on_create
|
1147
|
+
# return if @nats_wave_publishing_in_progress
|
1148
|
+
# @nats_wave_publishing_in_progress = true
|
1149
|
+
#
|
1150
|
+
# Rails.logger.debug "🚀 Auto-publishing on create" if defined?(Rails)
|
1151
|
+
# nats_wave_publish('create')
|
1152
|
+
#
|
1153
|
+
# @nats_wave_publishing_in_progress = false
|
1154
|
+
# end
|
1155
|
+
#
|
1156
|
+
# def trigger_nats_wave_auto_publish_on_update
|
1157
|
+
# return if @nats_wave_publishing_in_progress
|
1158
|
+
# @nats_wave_publishing_in_progress = true
|
1159
|
+
#
|
1160
|
+
# Rails.logger.debug "🚀 Auto-publishing on update" if defined?(Rails)
|
1161
|
+
# nats_wave_publish('update')
|
1162
|
+
#
|
1163
|
+
# @nats_wave_publishing_in_progress = false
|
1164
|
+
# end
|
1165
|
+
#
|
1166
|
+
# def trigger_nats_wave_auto_publish_on_destroy
|
1167
|
+
# return if @nats_wave_publishing_in_progress
|
1168
|
+
# @nats_wave_publishing_in_progress = true
|
1169
|
+
#
|
1170
|
+
# Rails.logger.debug "🚀 Auto-publishing on destroy" if defined?(Rails)
|
1171
|
+
# nats_wave_publish('destroy')
|
1172
|
+
#
|
1173
|
+
# @nats_wave_publishing_in_progress = false
|
1174
|
+
# end
|
1175
|
+
#
|
313
1176
|
# private
|
314
1177
|
#
|
315
1178
|
# def determine_action_from_context
|
@@ -333,6 +1196,86 @@
|
|
333
1196
|
# end
|
334
1197
|
# end
|
335
1198
|
#
|
1199
|
+
# # Process publishing data through field mappings and transformations (INSTANCE METHOD)
|
1200
|
+
# def process_publishing_data(raw_data, config)
|
1201
|
+
# processed_data = {}
|
1202
|
+
# field_mappings = config[:field_mappings]
|
1203
|
+
# transformations = config[:transformations]
|
1204
|
+
# skip_fields = config[:skip_fields]
|
1205
|
+
# only_mapped_fields = config[:only_mapped_fields]
|
1206
|
+
#
|
1207
|
+
# # If only_mapped_fields is true, only process fields that have mappings or transformations
|
1208
|
+
# fields_to_process = if only_mapped_fields && field_mappings.any?
|
1209
|
+
# # Only process fields that are explicitly mapped or have transformations
|
1210
|
+
# mapped_source_fields = field_mappings.keys.map(&:to_s)
|
1211
|
+
# transformation_fields = transformations.keys.map(&:to_s)
|
1212
|
+
# all_relevant_fields = (mapped_source_fields + transformation_fields).uniq
|
1213
|
+
#
|
1214
|
+
# raw_data.select { |field, _| all_relevant_fields.include?(field.to_s) }
|
1215
|
+
# else
|
1216
|
+
# # Process all fields (existing behavior)
|
1217
|
+
# raw_data
|
1218
|
+
# end
|
1219
|
+
#
|
1220
|
+
# fields_to_process.each do |field, value|
|
1221
|
+
# field_str = field.to_s
|
1222
|
+
# field_sym = field.to_sym
|
1223
|
+
#
|
1224
|
+
# # Skip if in skip_fields
|
1225
|
+
# next if skip_fields.include?(field_str) || skip_fields.include?(field_sym)
|
1226
|
+
#
|
1227
|
+
# # Apply field mapping (local -> external)
|
1228
|
+
# mapped_field = field_mappings[field_str] ||
|
1229
|
+
# field_mappings[field_sym] ||
|
1230
|
+
# field
|
1231
|
+
#
|
1232
|
+
# # Apply transformation if any
|
1233
|
+
# transformed_value = apply_publishing_transformation(value, mapped_field, transformations, raw_data)
|
1234
|
+
#
|
1235
|
+
# processed_data[mapped_field.to_s] = transformed_value
|
1236
|
+
# end
|
1237
|
+
#
|
1238
|
+
# processed_data
|
1239
|
+
# end
|
1240
|
+
#
|
1241
|
+
# def apply_publishing_transformation(value, field, transformations, full_record)
|
1242
|
+
# transformation = transformations[field] || transformations[field.to_sym]
|
1243
|
+
#
|
1244
|
+
# case transformation
|
1245
|
+
# when Proc
|
1246
|
+
# if transformation.arity == 2 || transformation.arity < 0
|
1247
|
+
# transformation.call(value, full_record)
|
1248
|
+
# else
|
1249
|
+
# transformation.call(value)
|
1250
|
+
# end
|
1251
|
+
# when Symbol
|
1252
|
+
# if self.respond_to?(transformation, true)
|
1253
|
+
# self.send(transformation, value)
|
1254
|
+
# elsif value.respond_to?(transformation)
|
1255
|
+
# value.send(transformation)
|
1256
|
+
# else
|
1257
|
+
# Rails.logger.warn "Publishing transformation method #{transformation} not found" if defined?(Rails)
|
1258
|
+
# value
|
1259
|
+
# end
|
1260
|
+
# else
|
1261
|
+
# value
|
1262
|
+
# end
|
1263
|
+
# end
|
1264
|
+
#
|
1265
|
+
# def determine_subjects_for_action(configured_subjects, action)
|
1266
|
+
# # If subjects contain placeholders like {action}, replace them
|
1267
|
+
# configured_subjects.map do |subject|
|
1268
|
+
# if subject.include?('{action}')
|
1269
|
+
# subject.gsub('{action}', action.to_s)
|
1270
|
+
# elsif subject.include?('*')
|
1271
|
+
# # Replace wildcard with specific action
|
1272
|
+
# subject.gsub('*', action.to_s)
|
1273
|
+
# else
|
1274
|
+
# subject
|
1275
|
+
# end
|
1276
|
+
# end
|
1277
|
+
# end
|
1278
|
+
#
|
336
1279
|
# def evaluate_conditions(conditions)
|
337
1280
|
# conditions.all? do |condition, expected_value|
|
338
1281
|
# case condition
|
@@ -361,7 +1304,6 @@
|
|
361
1304
|
# end
|
362
1305
|
# end
|
363
1306
|
# end
|
364
|
-
|
365
1307
|
# frozen_string_literal: true
|
366
1308
|
|
367
1309
|
module NatsWave
|
@@ -380,7 +1322,8 @@ module NatsWave
|
|
380
1322
|
def nats_wave_subscribes_to(*subjects, **options, &block)
|
381
1323
|
Rails.logger.debug "📡 #{self.name}: Setting up subscription to subjects: #{subjects.inspect}" if defined?(Rails)
|
382
1324
|
|
383
|
-
|
1325
|
+
# Custom handler is now optional and runs AFTER default behavior
|
1326
|
+
custom_handler = options[:handler] || block
|
384
1327
|
|
385
1328
|
subscription_config = {
|
386
1329
|
subjects: subjects.flatten,
|
@@ -389,7 +1332,7 @@ module NatsWave
|
|
389
1332
|
skip_fields: options[:skip_fields] || [],
|
390
1333
|
unique_fields: options[:unique_fields] || [:id],
|
391
1334
|
sync_strategy: options[:sync_strategy] || :upsert,
|
392
|
-
|
1335
|
+
custom_handler: custom_handler, # Renamed to be clear it's optional
|
393
1336
|
queue_group: options[:queue_group],
|
394
1337
|
auto_sync: options[:auto_sync] != false # Default to true, can be disabled
|
395
1338
|
}
|
@@ -416,7 +1359,8 @@ module NatsWave
|
|
416
1359
|
def nats_wave_publishes_to(*subjects, **options, &block)
|
417
1360
|
Rails.logger.debug "📤 #{self.name}: Setting up publishing to subjects: #{subjects.inspect}" if defined?(Rails)
|
418
1361
|
|
419
|
-
|
1362
|
+
# Custom handler is now optional and runs AFTER default behavior
|
1363
|
+
custom_handler = options[:handler] || block
|
420
1364
|
|
421
1365
|
publishing_config = {
|
422
1366
|
subjects: subjects.flatten,
|
@@ -425,7 +1369,7 @@ module NatsWave
|
|
425
1369
|
skip_fields: options[:skip_fields] || [],
|
426
1370
|
conditions: options[:conditions] || {},
|
427
1371
|
actions: options[:actions] || [:create, :update, :destroy],
|
428
|
-
|
1372
|
+
custom_handler: custom_handler, # Renamed to be clear it's optional
|
429
1373
|
async: options[:async] != false, # Default to true
|
430
1374
|
enabled: options[:enabled] != false # Default to true
|
431
1375
|
}
|
@@ -434,8 +1378,8 @@ module NatsWave
|
|
434
1378
|
config_key = subjects.join(',')
|
435
1379
|
self.nats_wave_publishing_config[config_key] = publishing_config
|
436
1380
|
|
437
|
-
# Add ActiveRecord callbacks for auto-publishing
|
438
|
-
|
1381
|
+
# Add ActiveRecord callbacks for auto-publishing (only once per model)
|
1382
|
+
setup_publishing_callbacks_once
|
439
1383
|
|
440
1384
|
Rails.logger.debug "📤 #{self.name}: Registered publishing config for subjects: #{subjects}" if defined?(Rails)
|
441
1385
|
end
|
@@ -450,39 +1394,23 @@ module NatsWave
|
|
450
1394
|
nats_wave_publishing_config
|
451
1395
|
end
|
452
1396
|
|
453
|
-
# Get all subjects this model subscribes to
|
454
|
-
def nats_wave_subscribed_subjects
|
455
|
-
nats_wave_subscription_config.values.flat_map { |config| config[:subjects] }.uniq
|
456
|
-
end
|
457
|
-
|
458
|
-
# Get all subjects this model publishes to
|
459
|
-
def nats_wave_published_subjects
|
460
|
-
nats_wave_publishing_config.values.flat_map { |config| config[:subjects] }.uniq
|
461
|
-
end
|
462
|
-
|
463
1397
|
private
|
464
1398
|
|
465
|
-
# Setup ActiveRecord callbacks for auto-publishing
|
466
|
-
def
|
467
|
-
# Only add callbacks if we're in ActiveRecord and
|
1399
|
+
# Setup ActiveRecord callbacks for auto-publishing (only once per model)
|
1400
|
+
def setup_publishing_callbacks_once
|
1401
|
+
# Only add callbacks if we're in ActiveRecord and haven't added them yet
|
468
1402
|
return unless defined?(ActiveRecord::Base) && self < ActiveRecord::Base
|
1403
|
+
return if @nats_wave_callbacks_added
|
469
1404
|
|
470
|
-
|
1405
|
+
after_commit :trigger_nats_wave_auto_publish_on_create, on: :create
|
1406
|
+
after_commit :trigger_nats_wave_auto_publish_on_update, on: :update
|
1407
|
+
after_commit :trigger_nats_wave_auto_publish_on_destroy, on: :destroy
|
471
1408
|
|
472
|
-
|
473
|
-
|
474
|
-
end
|
475
|
-
|
476
|
-
if actions.include?(:update)
|
477
|
-
after_commit :trigger_nats_wave_auto_publish_on_update, on: :update
|
478
|
-
end
|
479
|
-
|
480
|
-
if actions.include?(:destroy)
|
481
|
-
after_commit :trigger_nats_wave_auto_publish_on_destroy, on: :destroy
|
482
|
-
end
|
1409
|
+
@nats_wave_callbacks_added = true
|
1410
|
+
Rails.logger.debug "📤 #{self.name}: Added publishing callbacks" if defined?(Rails)
|
483
1411
|
end
|
484
1412
|
|
485
|
-
# Create a subscription handler that processes data and calls custom handler
|
1413
|
+
# Create a subscription handler that processes data and calls custom handler AFTER auto-sync
|
486
1414
|
def create_subscription_handler(config)
|
487
1415
|
model_class = self
|
488
1416
|
|
@@ -496,18 +1424,18 @@ module NatsWave
|
|
496
1424
|
action = message['action']
|
497
1425
|
|
498
1426
|
# Process the data through mappings and transformations
|
499
|
-
processed_data = process_subscription_data(raw_data, config)
|
1427
|
+
processed_data = model_class.process_subscription_data(raw_data, config)
|
500
1428
|
|
501
|
-
#
|
502
|
-
if config[:
|
503
|
-
Rails.logger.debug "📨
|
504
|
-
config[:handler].call(model_name, action, processed_data, message)
|
505
|
-
elsif config[:auto_sync]
|
506
|
-
Rails.logger.debug "📨 Performing auto-sync" if defined?(Rails)
|
507
|
-
# Default auto-sync behavior
|
1429
|
+
# Always perform auto-sync first (if enabled)
|
1430
|
+
if config[:auto_sync]
|
1431
|
+
Rails.logger.debug "📨 Performing auto-sync first" if defined?(Rails)
|
508
1432
|
model_class.perform_auto_sync(model_name, action, processed_data, config)
|
509
|
-
|
510
|
-
|
1433
|
+
end
|
1434
|
+
|
1435
|
+
# Then call custom handler if provided (optional)
|
1436
|
+
if config[:custom_handler]
|
1437
|
+
Rails.logger.debug "📨 Calling custom subscription handler after auto-sync" if defined?(Rails)
|
1438
|
+
config[:custom_handler].call(model_name, action, processed_data, message)
|
511
1439
|
end
|
512
1440
|
|
513
1441
|
rescue => e
|
@@ -518,7 +1446,7 @@ module NatsWave
|
|
518
1446
|
end
|
519
1447
|
end
|
520
1448
|
|
521
|
-
# Process subscription data through field mappings and transformations (CLASS METHOD)
|
1449
|
+
# Process subscription data through field mappings and transformations (PUBLIC CLASS METHOD)
|
522
1450
|
def process_subscription_data(raw_data, config)
|
523
1451
|
processed_data = {}
|
524
1452
|
field_mappings = config[:field_mappings]
|
@@ -532,7 +1460,7 @@ module NatsWave
|
|
532
1460
|
# Skip if in skip_fields
|
533
1461
|
next if skip_fields.include?(field_str) || skip_fields.include?(field_sym)
|
534
1462
|
|
535
|
-
# Apply field mapping
|
1463
|
+
# Apply field mapping (external -> local)
|
536
1464
|
mapped_field = field_mappings[field_str] ||
|
537
1465
|
field_mappings[field_sym] ||
|
538
1466
|
field
|
@@ -546,7 +1474,7 @@ module NatsWave
|
|
546
1474
|
processed_data
|
547
1475
|
end
|
548
1476
|
|
549
|
-
# Apply transformations to field values (CLASS METHOD)
|
1477
|
+
# Apply transformations to field values (PUBLIC CLASS METHOD)
|
550
1478
|
def apply_transformation(value, field, transformations, full_record)
|
551
1479
|
transformation = transformations[field] || transformations[field.to_sym]
|
552
1480
|
|
@@ -571,7 +1499,7 @@ module NatsWave
|
|
571
1499
|
end
|
572
1500
|
end
|
573
1501
|
|
574
|
-
# Default auto-sync behavior (CLASS METHOD)
|
1502
|
+
# Default auto-sync behavior (PUBLIC CLASS METHOD)
|
575
1503
|
def perform_auto_sync(model_name, action, processed_data, config)
|
576
1504
|
unique_fields = config[:unique_fields]
|
577
1505
|
sync_strategy = config[:sync_strategy]
|
@@ -673,23 +1601,26 @@ module NatsWave
|
|
673
1601
|
# Process the data through mappings and transformations
|
674
1602
|
processed_data = process_publishing_data(raw_data, config)
|
675
1603
|
|
676
|
-
#
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
1604
|
+
# Determine which subjects to publish to based on action
|
1605
|
+
subjects_to_publish = determine_subjects_for_action(config[:subjects], action)
|
1606
|
+
|
1607
|
+
# Always publish first (default behavior)
|
1608
|
+
subjects_to_publish.each do |subject|
|
1609
|
+
NatsWave.client.publish(
|
1610
|
+
subject: subject,
|
1611
|
+
model: self.class.name,
|
1612
|
+
action: action,
|
1613
|
+
data: processed_data, # This is the processed data, not raw attributes
|
1614
|
+
metadata: build_publishing_metadata
|
1615
|
+
)
|
1616
|
+
|
1617
|
+
Rails.logger.info "📤 Published #{self.class.name}##{id} to #{subject} (#{action})" if defined?(Rails)
|
1618
|
+
end
|
1619
|
+
|
1620
|
+
# Then call custom handler if provided (optional)
|
1621
|
+
if config[:custom_handler]
|
1622
|
+
Rails.logger.debug "📤 Calling custom publishing handler after publishing" if defined?(Rails)
|
1623
|
+
config[:custom_handler].call(self.class.name, action, processed_data, self)
|
693
1624
|
end
|
694
1625
|
end
|
695
1626
|
rescue StandardError => e
|
@@ -697,20 +1628,35 @@ module NatsWave
|
|
697
1628
|
# Don't re-raise to avoid breaking transactions
|
698
1629
|
end
|
699
1630
|
|
700
|
-
# Auto-publishing callback methods
|
1631
|
+
# Auto-publishing callback methods (only called once per action)
|
701
1632
|
def trigger_nats_wave_auto_publish_on_create
|
1633
|
+
return if @nats_wave_publishing_in_progress
|
1634
|
+
@nats_wave_publishing_in_progress = true
|
1635
|
+
|
702
1636
|
Rails.logger.debug "🚀 Auto-publishing on create" if defined?(Rails)
|
703
1637
|
nats_wave_publish('create')
|
1638
|
+
|
1639
|
+
@nats_wave_publishing_in_progress = false
|
704
1640
|
end
|
705
1641
|
|
706
1642
|
def trigger_nats_wave_auto_publish_on_update
|
1643
|
+
return if @nats_wave_publishing_in_progress
|
1644
|
+
@nats_wave_publishing_in_progress = true
|
1645
|
+
|
707
1646
|
Rails.logger.debug "🚀 Auto-publishing on update" if defined?(Rails)
|
708
1647
|
nats_wave_publish('update')
|
1648
|
+
|
1649
|
+
@nats_wave_publishing_in_progress = false
|
709
1650
|
end
|
710
1651
|
|
711
1652
|
def trigger_nats_wave_auto_publish_on_destroy
|
1653
|
+
return if @nats_wave_publishing_in_progress
|
1654
|
+
@nats_wave_publishing_in_progress = true
|
1655
|
+
|
712
1656
|
Rails.logger.debug "🚀 Auto-publishing on destroy" if defined?(Rails)
|
713
1657
|
nats_wave_publish('destroy')
|
1658
|
+
|
1659
|
+
@nats_wave_publishing_in_progress = false
|
714
1660
|
end
|
715
1661
|
|
716
1662
|
private
|
@@ -736,7 +1682,7 @@ module NatsWave
|
|
736
1682
|
end
|
737
1683
|
end
|
738
1684
|
|
739
|
-
# Process publishing data
|
1685
|
+
# Process publishing data - much simpler now! (INSTANCE METHOD)
|
740
1686
|
def process_publishing_data(raw_data, config)
|
741
1687
|
processed_data = {}
|
742
1688
|
field_mappings = config[:field_mappings]
|
@@ -750,10 +1696,10 @@ module NatsWave
|
|
750
1696
|
# Skip if in skip_fields
|
751
1697
|
next if skip_fields.include?(field_str) || skip_fields.include?(field_sym)
|
752
1698
|
|
753
|
-
# Apply field mapping (local -> external)
|
1699
|
+
# Apply field mapping (local -> external) - ONLY if mapping exists
|
754
1700
|
mapped_field = field_mappings[field_str] ||
|
755
1701
|
field_mappings[field_sym] ||
|
756
|
-
field
|
1702
|
+
field # Use original field name if no mapping
|
757
1703
|
|
758
1704
|
# Apply transformation if any
|
759
1705
|
transformed_value = apply_publishing_transformation(value, mapped_field, transformations, raw_data)
|
@@ -788,6 +1734,20 @@ module NatsWave
|
|
788
1734
|
end
|
789
1735
|
end
|
790
1736
|
|
1737
|
+
def determine_subjects_for_action(configured_subjects, action)
|
1738
|
+
# If subjects contain placeholders like {action}, replace them
|
1739
|
+
configured_subjects.map do |subject|
|
1740
|
+
if subject.include?('{action}')
|
1741
|
+
subject.gsub('{action}', action.to_s)
|
1742
|
+
elsif subject.include?('*')
|
1743
|
+
# Replace wildcard with specific action
|
1744
|
+
subject.gsub('*', action.to_s)
|
1745
|
+
else
|
1746
|
+
subject
|
1747
|
+
end
|
1748
|
+
end
|
1749
|
+
end
|
1750
|
+
|
791
1751
|
def evaluate_conditions(conditions)
|
792
1752
|
conditions.all? do |condition, expected_value|
|
793
1753
|
case condition
|