Class: Clickwrap::Integrity::AttestationReconciler

Inherits:
Object
  • Object
show all
Defined in:
lib/clickwrap/integrity/attestation_reconciler.rb

Overview

Finds committed events for which a configured external integrity adapter never left an attestation row, then asks only for those missing records. This closes the recoverable part of the after-commit crash window: the required evidence remains committed, and an operator can safely discover and retry the optional external work later.

No local database can prove whether a process died immediately before or immediately after a provider accepted a request. Adapters should therefore use the event digest (timestamps) or exact chain snapshot (anchors) as their idempotency key. A retry may otherwise create a second valid provider record; Clickwrap preserves both and never calls that exactly-once delivery.

Defined Under Namespace

Classes: Outcome, Result

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(scope: Event.all, retry_failed_attestations: false) ⇒ AttestationReconciler

Returns a new instance of AttestationReconciler.



64
65
66
67
# File 'lib/clickwrap/integrity/attestation_reconciler.rb', line 64

def initialize(scope: Event.all, retry_failed_attestations: false)
  @scope = scope
  @retry_failed_attestations = retry_failed_attestations
end

Instance Attribute Details

#retry_failed_attestationsObject (readonly)

Returns the value of attribute retry_failed_attestations.



69
70
71
# File 'lib/clickwrap/integrity/attestation_reconciler.rb', line 69

def retry_failed_attestations
  @retry_failed_attestations
end

#scopeObject (readonly)

Returns the value of attribute scope.



69
70
71
# File 'lib/clickwrap/integrity/attestation_reconciler.rb', line 69

def scope
  @scope
end

Class Method Details

.configured_kindsObject



52
53
54
55
56
57
# File 'lib/clickwrap/integrity/attestation_reconciler.rb', line 52

def configured_kinds
  kinds = []
  kinds << "third_party_timestamp" if Clickwrap.config.timestamp_receipts_with
  kinds << "event_anchor" if Clickwrap.config.anchor_event_history_with
  kinds
end

.eligible_scope(scope, kind) ⇒ Object



59
60
61
# File 'lib/clickwrap/integrity/attestation_reconciler.rb', line 59

def eligible_scope(scope, kind)
  kind == "event_anchor" ? scope.where.not(chain_scope: nil) : scope
end

.missing_counts(scope: Event.all) ⇒ Object



44
45
46
47
48
49
50
# File 'lib/clickwrap/integrity/attestation_reconciler.rb', line 44

def missing_counts(scope: Event.all)
  configured_kinds.to_h do |kind|
    eligible = eligible_scope(scope, kind)
    recorded = IntegrityAttestation.where(kind: kind).select(:event_id)
    [kind, eligible.where.not(id: recorded).count]
  end
end

Instance Method Details

#callObject



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/clickwrap/integrity/attestation_reconciler.rb', line 71

def call
  outcomes = []

  each_event do |event|
    configured_kinds_for(event).each do |kind|
      next unless attempt_needed?(event, kind)

      attestation = attest(event, kind)
      outcomes << Outcome.new(
        event_id: event.id,
        kind: kind,
        state: attestation&.state || "not_recorded",
        attestation_id: attestation&.id
      )
    end
  end

  Result.new(outcomes: outcomes.freeze)
end