diff --git a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternPropertiesSection.tsx b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternPropertiesSection.tsx index 1bdbf9261..42f830708 100644 --- a/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternPropertiesSection.tsx +++ b/apps/console/src/pages/organizations/cookie-banners/configuration/trackers/_components/TrackerPatternPropertiesSection.tsx @@ -34,13 +34,10 @@ const trackerPatternPropertiesSectionFragment = graphql` name } thirdParty { - id name } commonThirdParty { - id name - logoUrl } } `; diff --git a/pkg/cookiebanner/tracker_mapping_worker.go b/pkg/cookiebanner/tracker_mapping_worker.go index 0daf5f342..6b3bfa07a 100644 --- a/pkg/cookiebanner/tracker_mapping_worker.go +++ b/pkg/cookiebanner/tracker_mapping_worker.go @@ -90,11 +90,13 @@ func (h *trackerMappingHandler) Claim(ctx context.Context) (coredata.TrackerPatt } // Process resolves the catalog mapping (when missing) and then promotes -// the pattern to an org ThirdParty (when eligible). When a pattern is -// re-triggered by a manual move (it already carries a -// common_tracker_pattern_id), we MUST NOT re-resolve the catalog: the -// existing link is preserved and we jump straight to third-party -// promotion. +// the pattern to an org ThirdParty (when eligible). Promotion is +// skipped for patterns still in the uncategorised category — the user +// must categorize a tracker before it creates or links an org +// ThirdParty. When a pattern is re-triggered by a manual move (it +// already carries a common_tracker_pattern_id), we MUST NOT re-resolve +// the catalog: the existing link is preserved and we jump straight to +// third-party promotion. func (h *trackerMappingHandler) Process(ctx context.Context, tp coredata.TrackerPattern) error { return h.pg.WithTx( ctx, @@ -139,12 +141,21 @@ func (h *trackerMappingHandler) Process(ctx context.Context, tp coredata.Tracker if thirdPartyID == nil && commonPatternID != nil && (tp.Source == nil || *tp.Source != coredata.CookieSourceExtension) { - promoted, err := h.promoteThirdParty(ctx, tx, tp, *commonPatternID) - if err != nil { - return fmt.Errorf("cannot promote third party: %w", err) + scope := coredata.NewScopeFromObjectID(tp.ID) + + var category coredata.CookieCategory + if err := category.LoadByID(ctx, tx, scope, tp.CookieCategoryID); err != nil { + return fmt.Errorf("cannot load cookie category: %w", err) } - thirdPartyID = promoted + if category.Kind != coredata.CookieCategoryKindUncategorised { + promoted, err := h.promoteThirdParty(ctx, tx, tp, *commonPatternID) + if err != nil { + return fmt.Errorf("cannot promote third party: %w", err) + } + + thirdPartyID = promoted + } } if commonPatternID != nil || thirdPartyID != nil { diff --git a/pkg/cookiebanner/tracker_mapping_worker_test.go b/pkg/cookiebanner/tracker_mapping_worker_test.go index c07c504e8..b7dbaac24 100644 --- a/pkg/cookiebanner/tracker_mapping_worker_test.go +++ b/pkg/cookiebanner/tracker_mapping_worker_test.go @@ -339,6 +339,51 @@ func TestProcess_PreservesCatalogMappingOnReTrigger(t *testing.T) { require.NotNil(t, reloaded.ThirdPartyID, "the worker should have promoted to an org ThirdParty") } +// TestProcess_UncategorisedPatternIsNotPromoted asserts that a pattern +// still in the uncategorised category gets its catalog mapping +// resolved but is NOT promoted to an org ThirdParty. +func TestProcess_UncategorisedPatternIsNotPromoted(t *testing.T) { + t.Parallel() + + client := newTestPgClient(t) + ctx := context.Background() + fx := seedPromotionFixture(t, ctx, client) + + require.NoError(t, client.WithTx(ctx, func(ctx context.Context, tx pg.Tx) error { + _, err := tx.Exec( + ctx, + `UPDATE tracker_patterns + SET cookie_category_id = $1, + mapping_requested_at = $2 + WHERE id = $3`, + fx.uncategorisedID, + time.Now().UTC().Truncate(time.Microsecond), + fx.trackerPattern.ID, + ) + + return err + })) + + var reloadedBefore coredata.TrackerPattern + + require.NoError(t, client.WithConn(ctx, func(ctx context.Context, conn pg.Querier) error { + return reloadedBefore.LoadByID(ctx, conn, fx.scope, fx.trackerPattern.ID) + })) + + h := newMappingHandler(client) + require.NoError(t, h.Process(ctx, reloadedBefore)) + + var reloaded coredata.TrackerPattern + + require.NoError(t, client.WithConn(ctx, func(ctx context.Context, conn pg.Querier) error { + return reloaded.LoadByID(ctx, conn, fx.scope, fx.trackerPattern.ID) + })) + + require.NotNil(t, reloaded.CommonTrackerPatternID, "catalog mapping must still be resolved") + assert.Equal(t, fx.commonPatternID, *reloaded.CommonTrackerPatternID) + assert.Nil(t, reloaded.ThirdPartyID, "uncategorised pattern must not be promoted to org ThirdParty") +} + // TestProcess_ExtensionPatternIsNotPromoted asserts that even when a // pattern has a catalog link, a Source=EXTENSION pattern stays // un-promoted.