Class: WebhookProcessors::FreightquoteProcessor
- Inherits:
-
Object
- Object
- WebhookProcessors::FreightquoteProcessor
- Defined in:
- app/services/webhook_processors/freightquote_processor.rb
Overview
Processor for CH Robinson Navisphere event-callback webhooks
(Freightquote LTL bookings).
Responsibilities (this commit): parse the event payload via
FreightEvent.parse_payload, resolve the associated Delivery from
customerReferenceNumber, and persist a FreightEvent row (idempotent
on replay via the synthesised idempotency_key).
Out of scope (this commit): state-machine side effects for rejection /
cancellation events, ops alerts, the pickup-window safety sweep, and the
void-confirmation worker. Each lands as its own commit on this PR.
Class Method Summary collapse
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(webhook_log) ⇒ FreightquoteProcessor
constructor
A new instance of FreightquoteProcessor.
Constructor Details
#initialize(webhook_log) ⇒ FreightquoteProcessor
Returns a new instance of FreightquoteProcessor.
24 25 26 27 |
# File 'app/services/webhook_processors/freightquote_processor.rb', line 24 def initialize(webhook_log) @webhook_log = webhook_log @payload = webhook_log.data || {} end |
Class Method Details
.call(webhook_log) ⇒ Object
20 21 22 |
# File 'app/services/webhook_processors/freightquote_processor.rb', line 20 def self.call(webhook_log) new(webhook_log).call end |
Instance Method Details
#call ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'app/services/webhook_processors/freightquote_processor.rb', line 29 def call attrs = FreightEvent.parse_payload(@payload) if attrs[:event_type].blank? || attrs[:event_time].blank? return { status: 'invalid', reason: 'missing event_type or event_time' } end # CHR's order subsystem replays every booking ~20 sec after the load # subsystem with ORDER CREATED + ORDER UPDATED notifications that # carry no operational data (no carrier, no items, no locations — # just pointers back to the loadNumbers we already projected). They # don't drive any side effect and they clutter the Freight Events # tab. Skip the FreightEvent projection but keep the webhook_log # row so we still have the raw payload for forensics. if FreightEvent::NAVISPHERE_NOISE_EVENT_TYPES.include?(attrs[:event_type]) Rails.logger.info "[FreightquoteProcessor] Skipping Navisphere noise event #{attrs[:event_type]} (webhook_log_id=#{@webhook_log.id})" return { status: 'skipped', reason: 'navisphere_noise', event_type: attrs[:event_type] } end idempotency_key = FreightEvent.idempotency_key_for( event_type: attrs[:event_type], event_time: attrs[:event_time], load_number: attrs[:load_number], order_number: attrs[:order_number] ) existing = FreightEvent.find_by(idempotency_key: idempotency_key) if existing Rails.logger.info "[FreightquoteProcessor] Duplicate event #{idempotency_key} — skipping (existing FreightEvent##{existing.id})" return { status: 'duplicate', freight_event_id: existing.id, event_type: attrs[:event_type] } end delivery_id = resolve_delivery_id(attrs[:customer_reference_number]) event = begin FreightEvent.create!( attrs.merge( delivery_id: delivery_id, webhook_log_id: @webhook_log.id ) ) rescue ActiveRecord::RecordNotUnique # Concurrent insert from a parallel CHR retry / re-queued worker # raced past our find_by check. The unique index on # idempotency_key caught it; treat the same as the explicit # 'duplicate' branch above. existing = FreightEvent.find_by(idempotency_key: idempotency_key) Rails.logger.info "[FreightquoteProcessor] Duplicate event #{idempotency_key} (concurrent insert) — existing FreightEvent##{existing&.id}" return { status: 'duplicate', freight_event_id: existing&.id, event_type: attrs[:event_type] } end Rails.logger.info "[FreightquoteProcessor] Persisted FreightEvent##{event.id} (#{attrs[:event_type]}) delivery=#{delivery_id || '?'} load=#{attrs[:load_number] || '?'}" dispatch_side_effects(event) { status: 'success', freight_event_id: event.id, event_type: attrs[:event_type], delivery_id: delivery_id, load_number: attrs[:load_number], order_number: attrs[:order_number] } end |