Class: Insika::Commands::RecordShadowReply
- Inherits:
-
Object
- Object
- Insika::Commands::RecordShadowReply
- Defined in:
- lib/insika/commands/record_shadow_reply.rb
Overview
C4 — the incumbent's half of a shadow pair, recorded on the mirror's word. One command, two doors: Shape 1 (the reply rides the mirror call itself) and Shape 2 (the follow-up route, for a consumer that mirrors before answering) both dispatch HERE, so there is one behaviour and one store method behind both.
Idempotency is first-write-wins: the customer received ONE reply, and letting a retry overwrite it would silently rewrite evidence. The guarantee lives in ShadowPairStore#record_incumbent (inside its transaction); the find below is only the cheap fast path for the common retry. A reply for a pair that does not exist yet creates it :open — the mirror may legitimately arrive before our turn finishes.
Instance Method Summary collapse
-
#call(command) ⇒ Object
payload: { channel:, external_id:, event_id:, reply:, at: } -> { pair_id:, status: }.
-
#initialize(shadow_pairs:, event_stream: nil) ⇒ RecordShadowReply
constructor
A new instance of RecordShadowReply.
Constructor Details
#initialize(shadow_pairs:, event_stream: nil) ⇒ RecordShadowReply
Returns a new instance of RecordShadowReply.
20 21 22 23 |
# File 'lib/insika/commands/record_shadow_reply.rb', line 20 def initialize(shadow_pairs:, event_stream: nil) @shadow_pairs = shadow_pairs @event_stream = event_stream end |
Instance Method Details
#call(command) ⇒ Object
payload: { channel:, external_id:, event_id:, reply:, at: } -> { pair_id:, status: }
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/insika/commands/record_shadow_reply.rb', line 27 def call(command) payload = command.payload channel = Coercion.presence(payload[:channel] || payload["channel"]) external_id = Coercion.presence(payload[:external_id] || payload["external_id"]) event_id = Coercion.presence(payload[:event_id] || payload["event_id"]) reply = payload[:reply] || payload["reply"] raise ValidationError, "channel is required" if channel.nil? raise ValidationError, "external_id is required" if external_id.nil? raise ValidationError, "event_id is required" if event_id.nil? raise ValidationError, "reply is required" if reply.to_s.strip.empty? id = ShadowPairStore.key_for(channel: channel, external_id: external_id, event_id: event_id) existing = @shadow_pairs.find(id) if existing && !existing.incumbent_reply.nil? emit(id, "already_recorded") return { pair_id: id, status: "already_recorded" } end pair = @shadow_pairs.record_incumbent(id: id, channel: channel, event_id: event_id, external_id: external_id, reply: reply.to_s, at: payload[:at] || payload["at"]) emit(id, pair.status.to_s) { pair_id: id, status: pair.status.to_s } end |