From ed2dbe92c9c9b54b4e485b114e2e945046d352d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Wed, 10 Jun 2026 14:41:03 +0200 Subject: [PATCH] Surface catalog vendors in tracker policy doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated tracker policy listed only org ThirdParty rows linked to the banner's patterns. Now that the mapping worker no longer auto-creates those rows, a banner whose vendors have not been imported would render an empty third-party section. Build the list from the same union the banner's linkedThirdParties resolver exposes: org ThirdParty rows plus the CommonThirdParty catalog entries reached through patterns that carry no org link. Dedupe by name with org rows taking precedence, so the policy stays complete and accurate whether or not a vendor has been imported into the register. Signed-off-by: Émile Ré --- pkg/probo/tracker_policy_document.go | 94 +++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 10 deletions(-) diff --git a/pkg/probo/tracker_policy_document.go b/pkg/probo/tracker_policy_document.go index d0a45361f..7abb78b9a 100644 --- a/pkg/probo/tracker_policy_document.go +++ b/pkg/probo/tracker_policy_document.go @@ -269,28 +269,102 @@ func (s *GeneratedDocumentService) buildTrackerPolicyThirdParties( return nil, fmt.Errorf("cannot load distinct third party ids: %w", err) } - if len(thirdPartyIDs) == 0 { + var thirdParties coredata.ThirdParties + if len(thirdPartyIDs) > 0 { + if err := thirdParties.LoadByIDs(ctx, conn, scope, thirdPartyIDs); err != nil { + return nil, fmt.Errorf("cannot load third parties: %w", err) + } + } + + // Patterns not linked to an org ThirdParty still surface their catalog + // (CommonThirdParty) vendor, mirroring the banner's linkedThirdParties + // union, so the policy stays complete whether or not the vendor was + // imported into the org register. + commonPatternIDs, err := patterns.LoadDistinctCommonTrackerPatternIDsByCookieBannerID(ctx, conn, scope, cookieBannerID) + if err != nil { + return nil, fmt.Errorf("cannot load distinct common tracker pattern ids: %w", err) + } + + var commonParties coredata.CommonThirdParties + if len(commonPatternIDs) > 0 { + var commonPatterns coredata.CommonTrackerPatterns + if err := commonPatterns.LoadByIDs(ctx, conn, commonPatternIDs); err != nil { + return nil, fmt.Errorf("cannot load common tracker patterns: %w", err) + } + + seen := make(map[gid.GID]struct{}, len(commonPatterns)) + commonThirdPartyIDs := make([]gid.GID, 0, len(commonPatterns)) + + for _, cp := range commonPatterns { + if cp.CommonThirdPartyID == nil { + continue + } + + if _, ok := seen[*cp.CommonThirdPartyID]; ok { + continue + } + + seen[*cp.CommonThirdPartyID] = struct{}{} + commonThirdPartyIDs = append(commonThirdPartyIDs, *cp.CommonThirdPartyID) + } + + if len(commonThirdPartyIDs) > 0 { + if err := commonParties.LoadByIDs(ctx, conn, commonThirdPartyIDs); err != nil { + return nil, fmt.Errorf("cannot load common third parties: %w", err) + } + } + } + + if len(thirdParties) == 0 && len(commonParties) == 0 { return nil, nil } - var thirdParties coredata.ThirdParties - if err := thirdParties.LoadByIDs(ctx, conn, scope, thirdPartyIDs); err != nil { - return nil, fmt.Errorf("cannot load third parties: %w", err) + rows := make([]docgen.TrackerPolicyThirdParty, 0, len(thirdParties)+len(commonParties)) + + // Dedupe by name so a vendor present both as an org third party (one + // pattern) and as a catalog entry (an unlinked pattern) is listed + // once. Org third parties are appended first, so their richer + // (user-editable) data wins for a shared name. + seenName := make(map[string]struct{}, len(thirdParties)+len(commonParties)) + + addRow := func(name, description, privacyPolicyURL string) { + name = strings.TrimSpace(name) + + key := strings.ToLower(name) + if _, ok := seenName[key]; ok { + return + } + + seenName[key] = struct{}{} + + rows = append(rows, docgen.TrackerPolicyThirdParty{ + Name: name, + Description: collapseWhitespace(description), + PrivacyPolicyURL: strings.TrimSpace(privacyPolicyURL), + }) } - rows := make([]docgen.TrackerPolicyThirdParty, 0, len(thirdParties)) for _, tp := range thirdParties { - row := docgen.TrackerPolicyThirdParty{Name: strings.TrimSpace(tp.Name)} - + description := "" if tp.Description != nil { - row.Description = collapseWhitespace(*tp.Description) + description = *tp.Description } + privacyPolicyURL := "" if tp.PrivacyPolicyURL != nil { - row.PrivacyPolicyURL = strings.TrimSpace(*tp.PrivacyPolicyURL) + privacyPolicyURL = *tp.PrivacyPolicyURL } - rows = append(rows, row) + addRow(tp.Name, description, privacyPolicyURL) + } + + for _, cp := range commonParties { + privacyPolicyURL := "" + if cp.PrivacyPolicyURL != nil { + privacyPolicyURL = *cp.PrivacyPolicyURL + } + + addRow(cp.Name, "", privacyPolicyURL) } // LoadByIDs returns rows in an unspecified order (no ORDER BY), so sort