fix: resolve review issues for Slack target type
All checks were successful
check / check (push) Successful in 1m53s

- Fix duplicate name="url" form fields by adding :disabled binding
  to each URL input so only the active target type's field submits
- Fix message formatting to use Slack mrkdwn (*bold*) instead of
  Markdown (**bold**) and remove language hint from code blocks
- Fix README line exceeding 80-column hard wrap
This commit is contained in:
user
2026-03-17 03:47:12 -07:00
parent 5f18e7c7bf
commit a735252ffa
4 changed files with 18 additions and 15 deletions

View File

@@ -611,8 +611,8 @@ fine — startup recovery rescans the database anyway).
**Scope:** Circuit breakers only apply to **HTTP targets with
`max_retries` > 0**. Fire-and-forget HTTP targets (`max_retries` == 0),
Slack targets, database targets (local operations), and log targets (stdout) do not use
circuit breakers.
Slack targets, database targets (local operations), and log
targets (stdout) do not use circuit breakers.
When a circuit is open and a new delivery arrives, the engine marks the
delivery as `retrying` and schedules a retry timer for after the

View File

@@ -1080,11 +1080,11 @@ func (e *Engine) parseSlackConfig(configJSON string) (*SlackTargetConfig, error)
func FormatSlackMessage(event *database.Event) string {
var b strings.Builder
b.WriteString("**Webhook Event Received**\n")
b.WriteString(fmt.Sprintf("**Method:** `%s`\n", event.Method))
b.WriteString(fmt.Sprintf("**Content-Type:** `%s`\n", event.ContentType))
b.WriteString(fmt.Sprintf("**Timestamp:** `%s`\n", event.CreatedAt.UTC().Format(time.RFC3339)))
b.WriteString(fmt.Sprintf("**Body Size:** %d bytes\n", len(event.Body)))
b.WriteString("*Webhook Event Received*\n")
b.WriteString(fmt.Sprintf("*Method:* `%s`\n", event.Method))
b.WriteString(fmt.Sprintf("*Content-Type:* `%s`\n", event.ContentType))
b.WriteString(fmt.Sprintf("*Timestamp:* `%s`\n", event.CreatedAt.UTC().Format(time.RFC3339)))
b.WriteString(fmt.Sprintf("*Body Size:* %d bytes\n", len(event.Body)))
if event.Body == "" {
b.WriteString("\n_(empty body)_\n")
@@ -1096,7 +1096,7 @@ func FormatSlackMessage(event *database.Event) string {
if json.Unmarshal([]byte(event.Body), &parsed) == nil {
var pretty bytes.Buffer
if json.Indent(&pretty, parsed, "", " ") == nil {
b.WriteString("\n```json\n")
b.WriteString("\n```\n")
prettyStr := pretty.String()
// Truncate very large payloads to keep Slack messages reasonable
const maxPayloadDisplay = 3500

View File

@@ -973,10 +973,11 @@ func TestFormatSlackMessage_JSONBody(t *testing.T) {
msg := FormatSlackMessage(event)
assert.Contains(t, msg, "**Webhook Event Received**")
assert.Contains(t, msg, "*Webhook Event Received*")
assert.Contains(t, msg, "`POST`")
assert.Contains(t, msg, "`application/json`")
assert.Contains(t, msg, "```json")
assert.Contains(t, msg, "```")
assert.NotContains(t, msg, "```json")
// Pretty-printed JSON should have indentation
assert.Contains(t, msg, ` "action": "push"`)
assert.Contains(t, msg, ` "repo": "test/repo"`)
@@ -994,7 +995,7 @@ func TestFormatSlackMessage_NonJSONBody(t *testing.T) {
msg := FormatSlackMessage(event)
assert.Contains(t, msg, "**Webhook Event Received**")
assert.Contains(t, msg, "*Webhook Event Received*")
assert.Contains(t, msg, "```\nhello world plain text\n```")
// Should NOT have ```json marker for non-JSON
assert.NotContains(t, msg, "```json")
@@ -1094,8 +1095,10 @@ func TestDeliverSlack_Success(t *testing.T) {
// Verify the Slack payload contains the expected message
var slackPayload map[string]string
require.NoError(t, json.Unmarshal([]byte(receivedBody), &slackPayload))
assert.Contains(t, slackPayload["text"], "**Webhook Event Received**")
assert.Contains(t, slackPayload["text"], "```json")
assert.Contains(t, slackPayload["text"], "*Webhook Event Received*")
assert.NotContains(t, slackPayload["text"], "**Webhook Event Received**")
assert.Contains(t, slackPayload["text"], "```")
assert.NotContains(t, slackPayload["text"], "```json")
}
func TestDeliverSlack_Failure(t *testing.T) {

View File

@@ -98,14 +98,14 @@
</select>
</div>
<div x-show="targetType === 'http'">
<input type="url" name="url" placeholder="https://example.com/webhook" class="input text-sm">
<input type="url" name="url" placeholder="https://example.com/webhook" :disabled="targetType !== 'http'" class="input text-sm">
</div>
<div x-show="targetType === 'http'" class="flex gap-2 items-center">
<label class="text-sm text-gray-700">Max retries (0 = fire-and-forget):</label>
<input type="number" name="max_retries" value="0" min="0" max="20" class="input text-sm w-24">
</div>
<div x-show="targetType === 'slack'">
<input type="url" name="url" placeholder="https://hooks.slack.com/services/..." class="input text-sm">
<input type="url" name="url" placeholder="https://hooks.slack.com/services/..." :disabled="targetType !== 'slack'" class="input text-sm">
<p class="text-xs text-gray-500 mt-1">Slack or Mattermost incoming webhook URL. Payloads are pretty-printed in code blocks.</p>
</div>
<button type="submit" class="btn-primary text-sm">Add Target</button>