1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
53
54
55
require "rack"
require "sentry-ruby"

require "pro_tacts/exchange_log"

module ProTacts
  # Rack middleware that warns Sentry when a DAV exchange is refused
  # (docs/plans/2026-09-13-dav-observability.md, "Refusals alert"). The
  # warning says only which route refused what; the exchange log, under
  # the id the warning is tagged with, says what was sent.
  class RefusalAlerts
    # @rbs @app: Rack::_App

    # A report this server does not support (403), a resource that is not
    # here (404), a sync token naming a state that is gone (410), and a
    # write whose precondition failed (412), the one status that means a
    # client and this server disagree about a card.
    REFUSALS = [403, 404, 410, 412].freeze #: Array[Integer]

    #: (Rack::_App app) -> void
    def initialize(app)
      @app = app
    end

    # Only an exchange ExchangeLog marked is a DAV one: the admin half
    # reports exceptions and nothing else.
    #: (Rack::env env) -> Rack::response
    def call(env)
      status, headers, body = @app.call(env)
      alert(env, status) if env.key?(ExchangeLog::ENV_KEY) && REFUSALS.include?(status)
      [status, headers, body]
    end

    private

    # Fingerprinted on the route with the card id replaced, so Sentry
    # groups a refusal that repeats across cards as one issue. The id
    # rides as the `card` tag instead, so an issue's events can be
    # searched by card.
    #: (Rack::env env, Integer status) -> void
    def alert(env, status)
      method = env.fetch("REQUEST_METHOD")
      path = env["PATH_INFO"].to_s
      card = path[%r{/([^/]+)\.vcf\z}, 1]
      route = path.sub(%r{/[^/]+\.vcf\z}, "/{id}.vcf")

      Sentry.capture_message(
        "#{method} #{route} answered #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}",
        level: :warning,
        fingerprint: [method, route, status.to_s],
        tags: card ? { card: } : {},
      )
    end
  end
end