Offer no Replay for a delivery to a deleted target (closes #387) #483
@@ -1856,7 +1856,11 @@ deliver where the destination has since been fixed. A target that has
|
|||||||
been deleted or deactivated therefore refuses the replay with a
|
been deleted or deactivated therefore refuses the replay with a
|
||||||
message on the event log rather than delivering from stale
|
message on the event log rather than delivering from stale
|
||||||
configuration, and a replay is refused while an earlier one for the
|
configuration, and a replay is refused while an earlier one for the
|
||||||
same event and target is still pending or retrying.
|
same event and target is still pending or retrying. A delivery whose
|
||||||
|
target has been deleted shows no **Replay** action at all: recreating
|
||||||
|
the target makes a new one that the old delivery does not name, so
|
||||||
|
**Resubmit** is how that event reaches the webhook's currently active
|
||||||
|
targets.
|
||||||
|
|
||||||
**Resubmit.** Replay recovers one delivery; **resubmit** re-injects one
|
**Resubmit.** Replay recovers one delivery; **resubmit** re-injects one
|
||||||
EVENT. The event log offers a per-event **Resubmit** action that stores
|
EVENT. The event log offers a per-event **Resubmit** action that stores
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ const (
|
|||||||
// replayTargetDeleted reports a target that once existed and has
|
// replayTargetDeleted reports a target that once existed and has
|
||||||
// since been deleted. Deletes are soft and deliveries carry no
|
// since been deleted. Deletes are soft and deliveries carry no
|
||||||
// foreign key to the target row, so the history survives its
|
// foreign key to the target row, so the history survives its
|
||||||
// target and this is the ordinary case for an old event.
|
// target and this is the ordinary case for an old event. The
|
||||||
|
// event log shows no Replay button for such a delivery, so only
|
||||||
|
// a page loaded before the delete reaches this.
|
||||||
replayTargetDeleted noticeCode = "replay-target-deleted"
|
replayTargetDeleted noticeCode = "replay-target-deleted"
|
||||||
|
|
||||||
// replayTargetMissing reports a target id that names no row at
|
// replayTargetMissing reports a target id that names no row at
|
||||||
|
|||||||
@@ -513,7 +513,11 @@ func TestHandleSourceLogs_RendersReplayControlAndBanner(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert.Contains(t, refused, "alert-error")
|
assert.Contains(t, refused, "alert-error")
|
||||||
assert.Contains(t, refused, "has been deleted")
|
assert.Contains(
|
||||||
|
t, refused,
|
||||||
|
"has been deleted. Use Resubmit to send the event "+
|
||||||
|
"to the webhook",
|
||||||
|
)
|
||||||
|
|
||||||
// An outcome code nobody issued renders no banner at all.
|
// An outcome code nobody issued renders no banner at all.
|
||||||
unknown := renderSourceLogsPageWithQuery(
|
unknown := renderSourceLogsPageWithQuery(
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ func noticeFor(r *http.Request) *notice {
|
|||||||
},
|
},
|
||||||
replayTargetDeleted: {
|
replayTargetDeleted: {
|
||||||
Text: "Not replayed: the target this delivery was for " +
|
Text: "Not replayed: the target this delivery was for " +
|
||||||
"has been deleted. Recreate the target, then replay.",
|
"has been deleted. Use Resubmit to send the event " +
|
||||||
|
"to the webhook's currently active targets.",
|
||||||
Failed: true,
|
Failed: true,
|
||||||
},
|
},
|
||||||
replayTargetMissing: {
|
replayTargetMissing: {
|
||||||
|
|||||||
@@ -94,6 +94,44 @@ func TestHandleSourceLogs_NamesDeletedTarget(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestHandleSourceLogs_OffersNoReplayForDeletedTarget proves a
|
||||||
|
// finished delivery offers Replay while its target lives and not
|
||||||
|
// once the target is deleted. A replay to a deleted target is always
|
||||||
|
// refused, and recreating the target makes a new one that the old
|
||||||
|
// delivery does not name.
|
||||||
|
func TestHandleSourceLogs_OffersNoReplayForDeletedTarget(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
h *handlers.Handlers
|
||||||
|
sess *session.Session
|
||||||
|
db *database.Database
|
||||||
|
dbMgr *database.WebhookDBManager
|
||||||
|
)
|
||||||
|
|
||||||
|
app := newTestApp(t, &h, &sess, &db, &dbMgr)
|
||||||
|
app.RequireStart()
|
||||||
|
|
||||||
|
t.Cleanup(app.RequireStop)
|
||||||
|
|
||||||
|
wh := seedWebhook(t, db)
|
||||||
|
tgt := seedTarget(t, db, wh.ID, database.TargetTypeLog)
|
||||||
|
|
||||||
|
_, failed := seedFailedDelivery(t, dbMgr, wh.ID, tgt.ID)
|
||||||
|
replayForm := `action="/hook/` + wh.ID + `/deliveries/` +
|
||||||
|
failed.ID + `/replay"`
|
||||||
|
|
||||||
|
before := renderSourceLogsPage(t, h, sess, wh.ID)
|
||||||
|
assert.Contains(t, before, replayForm)
|
||||||
|
|
||||||
|
deleteTargetThroughHandler(t, h, sess, wh.ID, tgt.ID)
|
||||||
|
|
||||||
|
after := renderSourceLogsPage(t, h, sess, wh.ID)
|
||||||
|
assert.NotContains(t, after, replayForm)
|
||||||
|
assert.NotContains(t, after, ">Replay<")
|
||||||
|
assert.Contains(t, after, tgt.Name+deletedMarker)
|
||||||
|
}
|
||||||
|
|
||||||
// TestHandleSourceLogs_MasksDeletedTargetConfig proves that
|
// TestHandleSourceLogs_MasksDeletedTargetConfig proves that
|
||||||
// naming a deleted target does not widen what the page shows of
|
// naming a deleted target does not widen what the page shows of
|
||||||
// it: its stored configuration stays masked by exactly the rules
|
// it: its stored configuration stays masked by exactly the rules
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
{{if .Status.Terminal}}
|
{{if and .Status.Terminal (not .Target.Deleted)}}
|
||||||
<form method="POST" action="/hook/{{$.Webhook.ID}}/deliveries/{{.ID}}/replay" class="inline">
|
<form method="POST" action="/hook/{{$.Webhook.ID}}/deliveries/{{.ID}}/replay" class="inline">
|
||||||
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
|
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
|
||||||
<input type="hidden" name="page" value="{{$.Page}}">
|
<input type="hidden" name="page" value="{{$.Page}}">
|
||||||
|
|||||||
Reference in New Issue
Block a user