Implement the webhook dispatch subsystem so watch sessions can relay scan
results to external services when repositories change.
Webhook package (internal/webhook):
- Sender interface (Name + Send(ctx, Payload)) with three concrete
implementations:
- SlackSender: Block Kit message (header + mrkdwn section with summary
and change list + context footer), emoji status markers
- DiscordSender: single rich embed with color-coded sidebar (green=
clean, yellow=attention, red=errors) and markdown description
- GenericSender: raw JSON POST of the Payload struct, with custom
headers from config
- Config struct per destination: type, URL, send_all, on_change_only,
min_priority, rate_limit, timeout, custom headers, retry (max_attempts
+ backoff); TimeoutOrDefault helper
- NewSender factory switches on type; unknown types rejected
- Dispatcher: NewDispatcher builds a senderHandle per config (sender +
rate-limit state + its own http.Client with per-config timeout);
Dispatch() fans out with per-sender guards: on_change_only skips when
changedCount==0, rate_limit skips when too soon (lastSent check)
- senderHandle.sendWithRetry: retries with exponential backoff on
transient errors (5xx/timeout); 4xx errors fail immediately;
isRetryable helper
HTTP client (internal/httpclient):
- PostJSON now skips json.Unmarshal when out==nil, matching the common
webhook pattern where the response body is irrelevant
Testing:
- All three sender types round-trip through httptest (request payload
decoded and asserted)
- Discord embed colour: yellow for attention, red for errors
- Generic sender payload integrity check
- Dispatcher: OnChangeOnly guard skips when changedCount==0; multiple
senders all fire; HTTP errors surfaced (500)
- Retry: 504 Gateway Timeout retried 3× before succeeding via the
dispatcher's sendWithRetry
- NewSender rejects unknown types, requires a name for identification
Verified: go build, go vet, go test -race (13 packages), gofmt clean.
31 lines
635 B
Go
31 lines
635 B
Go
package webhook
|
|
|
|
import (
|
|
"gitea.oblak.solutions/dimitar/gitFlow/pkg/status"
|
|
)
|
|
|
|
// statusSymbol maps RepoStatus to an emoji marker suitable for webhook
|
|
// messages (Slack and Discord rich text).
|
|
func statusSymbol(s status.RepoStatus) string {
|
|
switch s {
|
|
case status.StatusClean:
|
|
return "✅"
|
|
case status.StatusModified:
|
|
return "✏️"
|
|
case status.StatusAhead:
|
|
return "⬆️"
|
|
case status.StatusBehind:
|
|
return "⬇️"
|
|
case status.StatusDiverged:
|
|
return "🔀"
|
|
case status.StatusDetached:
|
|
return "📌"
|
|
case status.StatusBare:
|
|
return "📦"
|
|
case status.StatusError:
|
|
return "❌"
|
|
default:
|
|
return "❓"
|
|
}
|
|
}
|