Add proboctl catalog upsert, link, and describe commands

Operators previously had no way to curate the global tracker catalog
beyond inspection and banner-scoped resets. Add three proboctl commands
backed by small coredata helpers:

- common-third-party upsert: create or update a vendor keyed by slug,
  with partial-merge so an unset flag never blanks an existing column.
- common-tracker-pattern link/unlink: repoint catalog rows at a common
  third party (re-arming enrichment and remapping the uncategorised org
  trackers so the mapping worker re-resolves the vendor) or detach them.
  Unlinking skips enrichment and remap since there is no new vendor.
- common-tracker-pattern set-description: write a description, mark the
  row enriched, and backfill linked org patterns lacking one.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-06-10 11:10:41 +02:00
parent ae61c20791
commit 31c4a0b39c
8 changed files with 683 additions and 0 deletions

View File

@@ -1383,6 +1383,49 @@ WHERE
return result.RowsAffected(), nil
}
// RequestMappingForUncategorisedByCommonTrackerPatternIDs re-arms mapping
// on the uncategorised org tracker patterns linked to the given common
// tracker patterns: it clears their resolved org third party and stamps
// mapping_requested_at so the mapping worker re-resolves the vendor from
// the catalog row's (now changed) common third party. Like the
// description backfill it is a global catalog operation, so it is
// intentionally not tenant-scoped. Excluded patterns and patterns in
// user-chosen categories are left untouched - only the uncategorised
// category is remapped, matching the reset-trackers philosophy. The
// cookie_categories subquery is used only for filtering. Returns the
// number of org patterns re-armed.
func (tps *TrackerPatterns) RequestMappingForUncategorisedByCommonTrackerPatternIDs(
ctx context.Context,
tx pg.Tx,
commonIDs []gid.GID,
) (int64, error) {
q := `
UPDATE tracker_patterns
SET
third_party_id = NULL,
mapping_requested_at = NOW(),
updated_at = NOW()
WHERE
common_tracker_pattern_id = ANY(@common_ids)
AND excluded = false
AND cookie_category_id IN (
SELECT id FROM cookie_categories WHERE kind = @uncategorised_kind
)
`
args := pgx.StrictNamedArgs{
"common_ids": commonIDs,
"uncategorised_kind": CookieCategoryKindUncategorised,
}
result, err := tx.Exec(ctx, q, args)
if err != nil {
return 0, fmt.Errorf("cannot request mapping for uncategorised tracker patterns: %w", err)
}
return result.RowsAffected(), nil
}
// ResetAndRequestMappingByCookieCategoryID detaches every pattern in the
// given category from its catalog row, org third party, and copied
// description, then re-arms mapping. Operators run this (via proboctl) on