Problem
Multiple UI list handlers repeat nearly identical Fetcher / FilteredFetcher closures that:
- Compute
limit, offset := pg.LimitAndOffset()
- Call the corresponding service
List(ctx, limit, offset) or ListWithOptions(...)
- Log an error on failure with
"error", err, "page", pg.Page, "page_size", pg.PageSize
- Return
items, err
Affected handlers (Fetcher closures)
ui_secrets.go:28 — h.Sinks.List(ctx, limit, offset)
ui_alert_sinks.go:59 — h.Sinks.List(ctx, limit, offset)
ui_allowlist.go:17 — h.Allowlist.List(ctx, limit, offset)
Affected handlers (FilteredFetcher closures)
ui_sites.go:130 — filter parse + h.Sites.ListWithOptions(...)
ui_iocs.go, ui_sources.go, ui_jobs.go — similar patterns with filter structs
Impact
- Maintenance burden: Adding a new list page means copy-pasting the same 5-line pattern. Changing pagination or error logging requires editing every handler.
- Drift risk: Some use
logger.Error(), others logger.ErrorContext(ctx, ...). Fields and messages vary slightly.
Proposed fix
Create a generic adapter in list_handler.go (or ui_base.go):
// WrapListFetcher adapts a service List method to the Fetcher signature.
func WrapListFetcher[T any](
svc func(ctx context.Context, limit, offset int) ([]T, error),
logger *slog.Logger,
msg string,
) func(ctx context.Context, pg pageOpts) ([]any, error) {
return func(ctx context.Context, pg pageOpts) ([]any, error) {
limit, offset := pg.LimitAndOffset()
items, err := svc(ctx, limit, offset)
if err != nil {
logger.ErrorContext(ctx, msg, "error", err, "page", pg.Page, "page_size", pg.PageSize)
}
return items, err
}
}
Each handler would then use a one-liner:
Fetcher: WrapListFetcher(h.Sinks.List, h.logger(), "failed to load alert sinks for UI"),
Files to change
services/merrymaker-go/internal/http/list_handler.go — add adapter(s)
services/merrymaker-go/internal/http/ui_secrets.go
services/merrymaker-go/internal/http/ui_alert_sinks.go
services/merrymaker-go/internal/http/ui_allowlist.go
services/merrymaker-go/internal/http/ui_sites.go (FilteredFetcher variant)
services/merrymaker-go/internal/http/ui_iocs.go
services/merrymaker-go/internal/http/ui_sources.go
services/merrymaker-go/internal/http/ui_jobs.go
Acceptance criteria
- All Fetcher/FilteredFetcher closures replaced with adapter calls
- No behavior change (same log fields, same pagination, same error handling)
- Lint + tests pass
Problem
Multiple UI list handlers repeat nearly identical
Fetcher/FilteredFetcherclosures that:limit, offset := pg.LimitAndOffset()List(ctx, limit, offset)orListWithOptions(...)"error", err, "page", pg.Page, "page_size", pg.PageSizeitems, errAffected handlers (Fetcher closures)
ui_secrets.go:28—h.Sinks.List(ctx, limit, offset)ui_alert_sinks.go:59—h.Sinks.List(ctx, limit, offset)ui_allowlist.go:17—h.Allowlist.List(ctx, limit, offset)Affected handlers (FilteredFetcher closures)
ui_sites.go:130— filter parse +h.Sites.ListWithOptions(...)ui_iocs.go,ui_sources.go,ui_jobs.go— similar patterns with filter structsImpact
logger.Error(), otherslogger.ErrorContext(ctx, ...). Fields and messages vary slightly.Proposed fix
Create a generic adapter in
list_handler.go(orui_base.go):Each handler would then use a one-liner:
Files to change
services/merrymaker-go/internal/http/list_handler.go— add adapter(s)services/merrymaker-go/internal/http/ui_secrets.goservices/merrymaker-go/internal/http/ui_alert_sinks.goservices/merrymaker-go/internal/http/ui_allowlist.goservices/merrymaker-go/internal/http/ui_sites.go(FilteredFetcher variant)services/merrymaker-go/internal/http/ui_iocs.goservices/merrymaker-go/internal/http/ui_sources.goservices/merrymaker-go/internal/http/ui_jobs.goAcceptance criteria