Name a deleted target on its historical deliveries (closes #211)
All checks were successful
check / check (push) Successful in 3m8s
All checks were successful
check / check (push) Successful in 3m8s
Deleting a target soft deletes its row while its deliveries survive in the per-webhook database, so the event log kept rendering the history and lost the label: every historical row read ": delivered" and the deliveries block was headed by an empty name. loadTargetMap already loaded soft-deleted rows Unscoped for the redactor half, then discarded them before building the view half. It now builds both halves from every loaded row, and TargetView carries a Deleted flag with a DisplayName that renders "name (deleted)" — an operator debugging an old delivery needs to know the target is gone, not just what it was called. The views still come from NewTargetViews, so a deleted target's configuration is masked by exactly the code that masks a live one's. The widening is confined to this map, which feeds only DeliveryView.Target on the event log page: the source detail target list, the target edit form, the receiver and resubmit fan-out and the delivery engine each resolve targets through their own scoped queries, and the replay path keeps refusing a deleted target.
This commit is contained in:
@@ -860,11 +860,16 @@ func (h *Handlers) HandleSourceLogs() http.HandlerFunc {
|
||||
//
|
||||
// The load is Unscoped because deleting a target only soft
|
||||
// deletes the row while its deliveries survive in the
|
||||
// per-webhook database: a scoped load leaves those deliveries
|
||||
// with a zero redactor, which renders their response bodies
|
||||
// unredacted. Only the redactor half of the map is built from
|
||||
// deleted rows. The view half, which is what the page lists,
|
||||
// stays scoped.
|
||||
// per-webhook database. Both halves of the map need those rows:
|
||||
// a scoped load leaves an old delivery with a zero redactor,
|
||||
// which renders its response bodies unredacted, and with a zero
|
||||
// view, which renders its target as a blank name.
|
||||
//
|
||||
// This map is historical display only. It is built for the event
|
||||
// log page and reaches nothing but DeliveryView.Target: the
|
||||
// target list on the source detail page, the edit form and the
|
||||
// replay path each resolve targets themselves, and a deleted row
|
||||
// is refused there as before.
|
||||
func (h *Handlers) loadTargetMap(
|
||||
webhookID string,
|
||||
) (map[string]eventLogTarget, error) {
|
||||
@@ -880,21 +885,18 @@ func (h *Handlers) loadTargetMap(
|
||||
targetMap := make(
|
||||
map[string]eventLogTarget, len(targets),
|
||||
)
|
||||
live := make([]database.Target, 0, len(targets))
|
||||
|
||||
for i := range targets {
|
||||
targetMap[targets[i].ID] = eventLogTarget{
|
||||
Redactor: delivery.NewRedactor(&targets[i]),
|
||||
}
|
||||
|
||||
if !targets[i].DeletedAt.Valid {
|
||||
live = append(live, targets[i])
|
||||
}
|
||||
}
|
||||
|
||||
// The views come from NewTargetViews rather than being
|
||||
// rebuilt here, so the masking rules stay in one place.
|
||||
for _, v := range delivery.NewTargetViews(live) {
|
||||
// rebuilt here, so the masking rules stay in one place and a
|
||||
// deleted target's configuration is masked by the same code
|
||||
// that masks a live one's.
|
||||
for _, v := range delivery.NewTargetViews(targets) {
|
||||
entry := targetMap[v.ID]
|
||||
entry.View = v
|
||||
targetMap[v.ID] = entry
|
||||
|
||||
Reference in New Issue
Block a user