Remove cookie_patterns legacy, migrate to tracker_patterns

Delete coredata.CookiePattern and all associated CRUD methods,
rename shared types (CookiePatternOrderField, CookiePatternFilter,
CookiePatternMatchType) to TrackerPattern equivalents, and migrate
all API surfaces (GraphQL, MCP, CLI, n8n) to tracker_pattern naming.

The worker was already migrated in the base branch; this commit
completes the removal by dropping the old GraphQL schema/resolvers,
service methods, CLI commands, and n8n operations that operated on
the legacy cookie_patterns table.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-05-06 10:21:33 +04:00
parent 21f92352d5
commit af6e420f54
39 changed files with 1008 additions and 2475 deletions

View File

@@ -104,9 +104,9 @@ func setPatternExcluded(t *testing.T, c *testutil.Client, patternID string, excl
t.Helper() t.Helper()
const query = ` const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { updateTrackerPattern(input: $input) {
cookiePattern { id excluded } trackerPattern { id excluded }
} }
} }
` `
@@ -114,10 +114,10 @@ func setPatternExcluded(t *testing.T, c *testutil.Client, patternID string, excl
var result struct{} var result struct{}
require.NoError(t, c.Execute(query, map[string]any{ require.NoError(t, c.Execute(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"excluded": excluded, "excluded": excluded,
}, },
}, &result), "updateCookiePattern excluded mutation failed") }, &result), "updateTrackerPattern excluded mutation failed")
} }
// upsertTranslation upserts a translation for a banner+language pair and // upsertTranslation upserts a translation for a banner+language pair and
@@ -323,13 +323,13 @@ func TestCookieBannerVersioning_NoOpUpdates(t *testing.T) {
assert.Equal(t, "PUBLISHED", got.State) assert.Equal(t, "PUBLISHED", got.State)
}) })
t.Run("UpdateCookiePattern on visible pattern with same value does not bump version", func(t *testing.T) { t.Run("UpdateTrackerPattern on visible pattern with same value does not bump version", func(t *testing.T) {
t.Parallel() t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner) owner := testutil.NewClient(t, testutil.RoleOwner)
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "visible-noop"}) categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "visible-noop"})
patternID := factory.CreateCookiePattern(owner, categoryID, factory.Attrs{ patternID := factory.CreateTrackerPattern(owner, categoryID, factory.Attrs{
"displayName": "GA Tracker", "displayName": "GA Tracker",
"description": "Original description", "description": "Original description",
}) })
@@ -338,15 +338,15 @@ func TestCookieBannerVersioning_NoOpUpdates(t *testing.T) {
baseline := published.Version baseline := published.Version
const query = ` const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { cookiePattern { id } } updateTrackerPattern(input: $input) { trackerPattern { id } }
} }
` `
var result struct{} var result struct{}
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"displayName": "GA Tracker", "displayName": "GA Tracker",
"description": "Original description", "description": "Original description",
}, },
@@ -368,7 +368,7 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "excl-update"}) categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "excl-update"})
patternID := factory.CreateCookiePattern(owner, categoryID, factory.Attrs{ patternID := factory.CreateTrackerPattern(owner, categoryID, factory.Attrs{
"displayName": "Original", "displayName": "Original",
}) })
@@ -377,32 +377,32 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
baseline := published.Version baseline := published.Version
const query = ` const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { updateTrackerPattern(input: $input) {
cookiePattern { id displayName description } trackerPattern { id displayName description }
} }
} }
` `
var result struct { var result struct {
UpdateCookiePattern struct { UpdateTrackerPattern struct {
CookiePattern struct { TrackerPattern struct {
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
Description string `json:"description"` Description string `json:"description"`
} `json:"cookiePattern"` } `json:"trackerPattern"`
} `json:"updateCookiePattern"` } `json:"updateTrackerPattern"`
} }
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"displayName": "Renamed Excluded", "displayName": "Renamed Excluded",
"description": "Now with notes", "description": "Now with notes",
}, },
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, "Renamed Excluded", result.UpdateCookiePattern.CookiePattern.DisplayName) assert.Equal(t, "Renamed Excluded", result.UpdateTrackerPattern.TrackerPattern.DisplayName)
assert.Equal(t, "Now with notes", result.UpdateCookiePattern.CookiePattern.Description) assert.Equal(t, "Now with notes", result.UpdateTrackerPattern.TrackerPattern.Description)
got := latestVersion(t, owner, bannerID) got := latestVersion(t, owner, bannerID)
assert.Equal(t, baseline, got.Version, "excluded pattern fields are invisible to visitors") assert.Equal(t, baseline, got.Version, "excluded pattern fields are invisible to visitors")
@@ -415,23 +415,23 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "excl-delete"}) categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "excl-delete"})
patternID := factory.CreateCookiePattern(owner, categoryID) patternID := factory.CreateTrackerPattern(owner, categoryID)
setPatternExcluded(t, owner, patternID, true) setPatternExcluded(t, owner, patternID, true)
published := publishBanner(t, owner, bannerID) published := publishBanner(t, owner, bannerID)
baseline := published.Version baseline := published.Version
const query = ` const query = `
mutation DeleteCookiePattern($input: DeleteCookiePatternInput!) { mutation DeleteTrackerPattern($input: DeleteTrackerPatternInput!) {
deleteCookiePattern(input: $input) { deleteTrackerPattern(input: $input) {
deletedCookiePatternId deletedTrackerPatternId
} }
} }
` `
var result struct{} var result struct{}
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{"cookiePatternId": patternID}, "input": map[string]any{"trackerPatternId": patternID},
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
@@ -447,16 +447,16 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryA := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "excl-move-a"}) categoryA := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "excl-move-a"})
categoryB := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "excl-move-b"}) categoryB := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "excl-move-b"})
patternID := factory.CreateCookiePattern(owner, categoryA) patternID := factory.CreateTrackerPattern(owner, categoryA)
setPatternExcluded(t, owner, patternID, true) setPatternExcluded(t, owner, patternID, true)
published := publishBanner(t, owner, bannerID) published := publishBanner(t, owner, bannerID)
baseline := published.Version baseline := published.Version
const query = ` const query = `
mutation MoveCookiePatternToCategory($input: MoveCookiePatternToCategoryInput!) { mutation MoveTrackerPatternToCategory($input: MoveTrackerPatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) { moveTrackerPatternToCategory(input: $input) {
cookiePattern { id } trackerPattern { id }
} }
} }
` `
@@ -464,7 +464,7 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
var result struct{} var result struct{}
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"targetCookieCategoryId": categoryB, "targetCookieCategoryId": categoryB,
}, },
}, &result) }, &result)
@@ -604,13 +604,13 @@ func TestCookieBannerVersioning_RealChangesStillBumpVersion(t *testing.T) {
assert.Equal(t, "DRAFT", got.State) assert.Equal(t, "DRAFT", got.State)
}) })
t.Run("UpdateCookiePattern displayName change on visible pattern creates a new draft", func(t *testing.T) { t.Run("UpdateTrackerPattern displayName change on visible pattern creates a new draft", func(t *testing.T) {
t.Parallel() t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner) owner := testutil.NewClient(t, testutil.RoleOwner)
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "real-change"}) categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "real-change"})
patternID := factory.CreateCookiePattern(owner, categoryID, factory.Attrs{ patternID := factory.CreateTrackerPattern(owner, categoryID, factory.Attrs{
"displayName": "Original", "displayName": "Original",
}) })
@@ -618,14 +618,14 @@ func TestCookieBannerVersioning_RealChangesStillBumpVersion(t *testing.T) {
baseline := published.Version baseline := published.Version
const query = ` const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { cookiePattern { id } } updateTrackerPattern(input: $input) { trackerPattern { id } }
} }
` `
var result struct{} var result struct{}
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"displayName": "Renamed", "displayName": "Renamed",
}, },
}, &result) }, &result)

View File

@@ -23,7 +23,7 @@ import (
"go.probo.inc/probo/e2e/internal/testutil" "go.probo.inc/probo/e2e/internal/testutil"
) )
func TestCookiePattern_Create(t *testing.T) { func TestTrackerPattern_Create(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("with EXACT match type", func(t *testing.T) { t.Run("with EXACT match type", func(t *testing.T) {
@@ -34,9 +34,9 @@ func TestCookiePattern_Create(t *testing.T) {
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
const query = ` const query = `
mutation CreateCookiePattern($input: CreateCookiePatternInput!) { mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createCookiePattern(input: $input) { createTrackerPattern(input: $input) {
cookiePatternEdge { trackerPatternEdge {
node { node {
id id
pattern pattern
@@ -57,8 +57,8 @@ func TestCookiePattern_Create(t *testing.T) {
` `
var result struct { var result struct {
CreateCookiePattern struct { CreateTrackerPattern struct {
CookiePatternEdge struct { TrackerPatternEdge struct {
Node struct { Node struct {
ID string `json:"id"` ID string `json:"id"`
Pattern string `json:"pattern"` Pattern string `json:"pattern"`
@@ -70,11 +70,11 @@ func TestCookiePattern_Create(t *testing.T) {
CreatedAt string `json:"createdAt"` CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"` UpdatedAt string `json:"updatedAt"`
} `json:"node"` } `json:"node"`
} `json:"cookiePatternEdge"` } `json:"trackerPatternEdge"`
CookieBanner struct { CookieBanner struct {
ID string `json:"id"` ID string `json:"id"`
} `json:"cookieBanner"` } `json:"cookieBanner"`
} `json:"createCookiePattern"` } `json:"createTrackerPattern"`
} }
maxAge := 86400 maxAge := 86400
@@ -90,7 +90,7 @@ func TestCookiePattern_Create(t *testing.T) {
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
node := result.CreateCookiePattern.CookiePatternEdge.Node node := result.CreateTrackerPattern.TrackerPatternEdge.Node
assert.NotEmpty(t, node.ID) assert.NotEmpty(t, node.ID)
assert.Equal(t, "_ga", node.Pattern) assert.Equal(t, "_ga", node.Pattern)
assert.Equal(t, "EXACT", node.MatchType) assert.Equal(t, "EXACT", node.MatchType)
@@ -99,7 +99,7 @@ func TestCookiePattern_Create(t *testing.T) {
assert.Equal(t, maxAge, *node.MaxAgeSeconds) assert.Equal(t, maxAge, *node.MaxAgeSeconds)
assert.Equal(t, "Google Analytics tracking cookie", node.Description) assert.Equal(t, "Google Analytics tracking cookie", node.Description)
assert.Equal(t, "SCRIPT", node.Source) assert.Equal(t, "SCRIPT", node.Source)
assert.Equal(t, bannerID, result.CreateCookiePattern.CookieBanner.ID) assert.Equal(t, bannerID, result.CreateTrackerPattern.CookieBanner.ID)
}) })
t.Run("with PREFIX match type", func(t *testing.T) { t.Run("with PREFIX match type", func(t *testing.T) {
@@ -110,9 +110,9 @@ func TestCookiePattern_Create(t *testing.T) {
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
const query = ` const query = `
mutation CreateCookiePattern($input: CreateCookiePatternInput!) { mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createCookiePattern(input: $input) { createTrackerPattern(input: $input) {
cookiePatternEdge { trackerPatternEdge {
node { node {
id id
pattern pattern
@@ -126,8 +126,8 @@ func TestCookiePattern_Create(t *testing.T) {
` `
var result struct { var result struct {
CreateCookiePattern struct { CreateTrackerPattern struct {
CookiePatternEdge struct { TrackerPatternEdge struct {
Node struct { Node struct {
ID string `json:"id"` ID string `json:"id"`
Pattern string `json:"pattern"` Pattern string `json:"pattern"`
@@ -135,8 +135,8 @@ func TestCookiePattern_Create(t *testing.T) {
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
MaxAgeSeconds *int `json:"maxAgeSeconds"` MaxAgeSeconds *int `json:"maxAgeSeconds"`
} `json:"node"` } `json:"node"`
} `json:"cookiePatternEdge"` } `json:"trackerPatternEdge"`
} `json:"createCookiePattern"` } `json:"createTrackerPattern"`
} }
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
@@ -150,7 +150,7 @@ func TestCookiePattern_Create(t *testing.T) {
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
node := result.CreateCookiePattern.CookiePatternEdge.Node node := result.CreateTrackerPattern.TrackerPatternEdge.Node
assert.Equal(t, "_gat_", node.Pattern) assert.Equal(t, "_gat_", node.Pattern)
assert.Equal(t, "PREFIX", node.MatchType) assert.Equal(t, "PREFIX", node.MatchType)
assert.Nil(t, node.MaxAgeSeconds) assert.Nil(t, node.MaxAgeSeconds)
@@ -163,15 +163,15 @@ func TestCookiePattern_Create(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
factory.CreateCookiePattern(owner, categoryID, factory.Attrs{ factory.CreateTrackerPattern(owner, categoryID, factory.Attrs{
"pattern": "duplicate_cookie", "pattern": "duplicate_cookie",
"displayName": "First", "displayName": "First",
}) })
_, err := owner.Do(` _, err := owner.Do(`
mutation CreateCookiePattern($input: CreateCookiePatternInput!) { mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createCookiePattern(input: $input) { createTrackerPattern(input: $input) {
cookiePatternEdge { node { id } } trackerPatternEdge { node { id } }
cookieBanner { id } cookieBanner { id }
} }
} }
@@ -188,7 +188,7 @@ func TestCookiePattern_Create(t *testing.T) {
}) })
} }
func TestCookiePattern_Update(t *testing.T) { func TestTrackerPattern_Update(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("update displayName and description", func(t *testing.T) { t.Run("update displayName and description", func(t *testing.T) {
@@ -197,15 +197,15 @@ func TestCookiePattern_Update(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID, factory.Attrs{ patternID := factory.CreateTrackerPattern(owner, categoryID, factory.Attrs{
"displayName": "Original Name", "displayName": "Original Name",
"description": "Original description", "description": "Original description",
}) })
const query = ` const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { updateTrackerPattern(input: $input) {
cookiePattern { trackerPattern {
id id
displayName displayName
description description
@@ -218,31 +218,31 @@ func TestCookiePattern_Update(t *testing.T) {
` `
var result struct { var result struct {
UpdateCookiePattern struct { UpdateTrackerPattern struct {
CookiePattern struct { TrackerPattern struct {
ID string `json:"id"` ID string `json:"id"`
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
Description string `json:"description"` Description string `json:"description"`
} `json:"cookiePattern"` } `json:"trackerPattern"`
CookieBanner struct { CookieBanner struct {
ID string `json:"id"` ID string `json:"id"`
} `json:"cookieBanner"` } `json:"cookieBanner"`
} `json:"updateCookiePattern"` } `json:"updateTrackerPattern"`
} }
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"displayName": "Updated Name", "displayName": "Updated Name",
"description": "Updated description", "description": "Updated description",
}, },
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, patternID, result.UpdateCookiePattern.CookiePattern.ID) assert.Equal(t, patternID, result.UpdateTrackerPattern.TrackerPattern.ID)
assert.Equal(t, "Updated Name", result.UpdateCookiePattern.CookiePattern.DisplayName) assert.Equal(t, "Updated Name", result.UpdateTrackerPattern.TrackerPattern.DisplayName)
assert.Equal(t, "Updated description", result.UpdateCookiePattern.CookiePattern.Description) assert.Equal(t, "Updated description", result.UpdateTrackerPattern.TrackerPattern.Description)
assert.Equal(t, bannerID, result.UpdateCookiePattern.CookieBanner.ID) assert.Equal(t, bannerID, result.UpdateTrackerPattern.CookieBanner.ID)
}) })
t.Run("update maxAgeSeconds", func(t *testing.T) { t.Run("update maxAgeSeconds", func(t *testing.T) {
@@ -251,12 +251,12 @@ func TestCookiePattern_Update(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID) patternID := factory.CreateTrackerPattern(owner, categoryID)
const query = ` const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { updateTrackerPattern(input: $input) {
cookiePattern { trackerPattern {
id id
maxAgeSeconds maxAgeSeconds
} }
@@ -265,28 +265,28 @@ func TestCookiePattern_Update(t *testing.T) {
` `
var result struct { var result struct {
UpdateCookiePattern struct { UpdateTrackerPattern struct {
CookiePattern struct { TrackerPattern struct {
ID string `json:"id"` ID string `json:"id"`
MaxAgeSeconds *int `json:"maxAgeSeconds"` MaxAgeSeconds *int `json:"maxAgeSeconds"`
} `json:"cookiePattern"` } `json:"trackerPattern"`
} `json:"updateCookiePattern"` } `json:"updateTrackerPattern"`
} }
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"maxAgeSeconds": 7200, "maxAgeSeconds": 7200,
}, },
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, result.UpdateCookiePattern.CookiePattern.MaxAgeSeconds) require.NotNil(t, result.UpdateTrackerPattern.TrackerPattern.MaxAgeSeconds)
assert.Equal(t, 7200, *result.UpdateCookiePattern.CookiePattern.MaxAgeSeconds) assert.Equal(t, 7200, *result.UpdateTrackerPattern.TrackerPattern.MaxAgeSeconds)
}) })
} }
func TestCookiePattern_Excluded(t *testing.T) { func TestTrackerPattern_Excluded(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("defaults to false on create", func(t *testing.T) { t.Run("defaults to false on create", func(t *testing.T) {
@@ -297,9 +297,9 @@ func TestCookiePattern_Excluded(t *testing.T) {
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
const query = ` const query = `
mutation CreateCookiePattern($input: CreateCookiePatternInput!) { mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createCookiePattern(input: $input) { createTrackerPattern(input: $input) {
cookiePatternEdge { trackerPatternEdge {
node { node {
id id
excluded excluded
@@ -310,14 +310,14 @@ func TestCookiePattern_Excluded(t *testing.T) {
` `
var result struct { var result struct {
CreateCookiePattern struct { CreateTrackerPattern struct {
CookiePatternEdge struct { TrackerPatternEdge struct {
Node struct { Node struct {
ID string `json:"id"` ID string `json:"id"`
Excluded bool `json:"excluded"` Excluded bool `json:"excluded"`
} `json:"node"` } `json:"node"`
} `json:"cookiePatternEdge"` } `json:"trackerPatternEdge"`
} `json:"createCookiePattern"` } `json:"createTrackerPattern"`
} }
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
@@ -331,7 +331,7 @@ func TestCookiePattern_Excluded(t *testing.T) {
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
assert.False(t, result.CreateCookiePattern.CookiePatternEdge.Node.Excluded) assert.False(t, result.CreateTrackerPattern.TrackerPatternEdge.Node.Excluded)
}) })
t.Run("can be set to true via update", func(t *testing.T) { t.Run("can be set to true via update", func(t *testing.T) {
@@ -340,12 +340,12 @@ func TestCookiePattern_Excluded(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID) patternID := factory.CreateTrackerPattern(owner, categoryID)
const query = ` const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { updateTrackerPattern(input: $input) {
cookiePattern { trackerPattern {
id id
excluded excluded
} }
@@ -354,23 +354,23 @@ func TestCookiePattern_Excluded(t *testing.T) {
` `
var result struct { var result struct {
UpdateCookiePattern struct { UpdateTrackerPattern struct {
CookiePattern struct { TrackerPattern struct {
ID string `json:"id"` ID string `json:"id"`
Excluded bool `json:"excluded"` Excluded bool `json:"excluded"`
} `json:"cookiePattern"` } `json:"trackerPattern"`
} `json:"updateCookiePattern"` } `json:"updateTrackerPattern"`
} }
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"excluded": true, "excluded": true,
}, },
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
assert.True(t, result.UpdateCookiePattern.CookiePattern.Excluded) assert.True(t, result.UpdateTrackerPattern.TrackerPattern.Excluded)
}) })
t.Run("can be toggled back to false", func(t *testing.T) { t.Run("can be toggled back to false", func(t *testing.T) {
@@ -379,12 +379,12 @@ func TestCookiePattern_Excluded(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID) patternID := factory.CreateTrackerPattern(owner, categoryID)
const updateQuery = ` const updateQuery = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { updateTrackerPattern(input: $input) {
cookiePattern { trackerPattern {
id id
excluded excluded
} }
@@ -393,35 +393,35 @@ func TestCookiePattern_Excluded(t *testing.T) {
` `
var result struct { var result struct {
UpdateCookiePattern struct { UpdateTrackerPattern struct {
CookiePattern struct { TrackerPattern struct {
ID string `json:"id"` ID string `json:"id"`
Excluded bool `json:"excluded"` Excluded bool `json:"excluded"`
} `json:"cookiePattern"` } `json:"trackerPattern"`
} `json:"updateCookiePattern"` } `json:"updateTrackerPattern"`
} }
err := owner.Execute(updateQuery, map[string]any{ err := owner.Execute(updateQuery, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"excluded": true, "excluded": true,
}, },
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
assert.True(t, result.UpdateCookiePattern.CookiePattern.Excluded) assert.True(t, result.UpdateTrackerPattern.TrackerPattern.Excluded)
err = owner.Execute(updateQuery, map[string]any{ err = owner.Execute(updateQuery, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"excluded": false, "excluded": false,
}, },
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
assert.False(t, result.UpdateCookiePattern.CookiePattern.Excluded) assert.False(t, result.UpdateTrackerPattern.TrackerPattern.Excluded)
}) })
} }
func TestCookiePattern_Delete(t *testing.T) { func TestTrackerPattern_Delete(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
@@ -430,12 +430,12 @@ func TestCookiePattern_Delete(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID) patternID := factory.CreateTrackerPattern(owner, categoryID)
const query = ` const query = `
mutation DeleteCookiePattern($input: DeleteCookiePatternInput!) { mutation DeleteTrackerPattern($input: DeleteTrackerPatternInput!) {
deleteCookiePattern(input: $input) { deleteTrackerPattern(input: $input) {
deletedCookiePatternId deletedTrackerPatternId
cookieBanner { cookieBanner {
id id
} }
@@ -444,25 +444,25 @@ func TestCookiePattern_Delete(t *testing.T) {
` `
var result struct { var result struct {
DeleteCookiePattern struct { DeleteTrackerPattern struct {
DeletedCookiePatternID string `json:"deletedCookiePatternId"` DeletedTrackerPatternID string `json:"deletedTrackerPatternId"`
CookieBanner struct { CookieBanner struct {
ID string `json:"id"` ID string `json:"id"`
} `json:"cookieBanner"` } `json:"cookieBanner"`
} `json:"deleteCookiePattern"` } `json:"deleteTrackerPattern"`
} }
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{"cookiePatternId": patternID}, "input": map[string]any{"trackerPatternId": patternID},
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, patternID, result.DeleteCookiePattern.DeletedCookiePatternID) assert.Equal(t, patternID, result.DeleteTrackerPattern.DeletedTrackerPatternID)
assert.Equal(t, bannerID, result.DeleteCookiePattern.CookieBanner.ID) assert.Equal(t, bannerID, result.DeleteTrackerPattern.CookieBanner.ID)
}) })
} }
func TestCookiePattern_MoveToCategory(t *testing.T) { func TestTrackerPattern_MoveToCategory(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
@@ -472,12 +472,12 @@ func TestCookiePattern_MoveToCategory(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryA := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "cat-a-move"}) categoryA := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "cat-a-move"})
categoryB := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "cat-b-move"}) categoryB := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "cat-b-move"})
patternID := factory.CreateCookiePattern(owner, categoryA) patternID := factory.CreateTrackerPattern(owner, categoryA)
const query = ` const query = `
mutation MoveCookiePatternToCategory($input: MoveCookiePatternToCategoryInput!) { mutation MoveTrackerPatternToCategory($input: MoveTrackerPatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) { moveTrackerPatternToCategory(input: $input) {
cookiePattern { trackerPattern {
id id
cookieCategory { cookieCategory {
id id
@@ -491,30 +491,30 @@ func TestCookiePattern_MoveToCategory(t *testing.T) {
` `
var result struct { var result struct {
MoveCookiePatternToCategory struct { MoveTrackerPatternToCategory struct {
CookiePattern struct { TrackerPattern struct {
ID string `json:"id"` ID string `json:"id"`
CookieCategory struct { CookieCategory struct {
ID string `json:"id"` ID string `json:"id"`
} `json:"cookieCategory"` } `json:"cookieCategory"`
} `json:"cookiePattern"` } `json:"trackerPattern"`
CookieBanner struct { CookieBanner struct {
ID string `json:"id"` ID string `json:"id"`
} `json:"cookieBanner"` } `json:"cookieBanner"`
} `json:"moveCookiePatternToCategory"` } `json:"moveTrackerPatternToCategory"`
} }
err := owner.Execute(query, map[string]any{ err := owner.Execute(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"targetCookieCategoryId": categoryB, "targetCookieCategoryId": categoryB,
}, },
}, &result) }, &result)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, patternID, result.MoveCookiePatternToCategory.CookiePattern.ID) assert.Equal(t, patternID, result.MoveTrackerPatternToCategory.TrackerPattern.ID)
assert.Equal(t, categoryB, result.MoveCookiePatternToCategory.CookiePattern.CookieCategory.ID) assert.Equal(t, categoryB, result.MoveTrackerPatternToCategory.TrackerPattern.CookieCategory.ID)
assert.Equal(t, bannerID, result.MoveCookiePatternToCategory.CookieBanner.ID) assert.Equal(t, bannerID, result.MoveTrackerPatternToCategory.CookieBanner.ID)
}) })
t.Run("cross-banner mismatch error", func(t *testing.T) { t.Run("cross-banner mismatch error", func(t *testing.T) {
@@ -525,18 +525,18 @@ func TestCookiePattern_MoveToCategory(t *testing.T) {
banner2 := factory.CreateCookieBanner(owner) banner2 := factory.CreateCookieBanner(owner)
category1 := factory.CreateCookieCategory(owner, banner1, factory.Attrs{"slug": "cat-x-mismatch"}) category1 := factory.CreateCookieCategory(owner, banner1, factory.Attrs{"slug": "cat-x-mismatch"})
category2 := factory.CreateCookieCategory(owner, banner2, factory.Attrs{"slug": "cat-y-mismatch"}) category2 := factory.CreateCookieCategory(owner, banner2, factory.Attrs{"slug": "cat-y-mismatch"})
patternID := factory.CreateCookiePattern(owner, category1) patternID := factory.CreateTrackerPattern(owner, category1)
_, err := owner.Do(` _, err := owner.Do(`
mutation MoveCookiePatternToCategory($input: MoveCookiePatternToCategoryInput!) { mutation MoveTrackerPatternToCategory($input: MoveTrackerPatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) { moveTrackerPatternToCategory(input: $input) {
cookiePattern { id } trackerPattern { id }
cookieBanner { id } cookieBanner { id }
} }
} }
`, map[string]any{ `, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"targetCookieCategoryId": category2, "targetCookieCategoryId": category2,
}, },
}) })
@@ -544,23 +544,23 @@ func TestCookiePattern_MoveToCategory(t *testing.T) {
}) })
} }
func TestCookiePattern_List(t *testing.T) { func TestTrackerPattern_List(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("via category cookiePatterns connection", func(t *testing.T) { t.Run("via category trackerPatterns connection", func(t *testing.T) {
t.Parallel() t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner) owner := testutil.NewClient(t, testutil.RoleOwner)
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
factory.CreateCookiePattern(owner, categoryID) factory.CreateTrackerPattern(owner, categoryID)
factory.CreateCookiePattern(owner, categoryID) factory.CreateTrackerPattern(owner, categoryID)
const query = ` const query = `
query($id: ID!) { query($id: ID!) {
node(id: $id) { node(id: $id) {
... on CookieCategory { ... on CookieCategory {
cookiePatterns(first: 10) { trackerPatterns(first: 10) {
totalCount totalCount
edges { edges {
node { node {
@@ -581,7 +581,7 @@ func TestCookiePattern_List(t *testing.T) {
var result struct { var result struct {
Node struct { Node struct {
CookiePatterns struct { TrackerPatterns struct {
TotalCount int `json:"totalCount"` TotalCount int `json:"totalCount"`
Edges []struct { Edges []struct {
Node struct { Node struct {
@@ -594,18 +594,18 @@ func TestCookiePattern_List(t *testing.T) {
HasNextPage bool `json:"hasNextPage"` HasNextPage bool `json:"hasNextPage"`
HasPreviousPage bool `json:"hasPreviousPage"` HasPreviousPage bool `json:"hasPreviousPage"`
} `json:"pageInfo"` } `json:"pageInfo"`
} `json:"cookiePatterns"` } `json:"trackerPatterns"`
} `json:"node"` } `json:"node"`
} }
err := owner.Execute(query, map[string]any{"id": categoryID}, &result) err := owner.Execute(query, map[string]any{"id": categoryID}, &result)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 2, result.Node.CookiePatterns.TotalCount) assert.Equal(t, 2, result.Node.TrackerPatterns.TotalCount)
assert.Len(t, result.Node.CookiePatterns.Edges, 2) assert.Len(t, result.Node.TrackerPatterns.Edges, 2)
}) })
} }
func TestCookiePattern_RBAC(t *testing.T) { func TestTrackerPattern_RBAC(t *testing.T) {
t.Parallel() t.Parallel()
t.Run("viewer cannot create pattern", func(t *testing.T) { t.Run("viewer cannot create pattern", func(t *testing.T) {
@@ -617,9 +617,9 @@ func TestCookiePattern_RBAC(t *testing.T) {
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
_, err := viewer.Do(` _, err := viewer.Do(`
mutation CreateCookiePattern($input: CreateCookiePatternInput!) { mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createCookiePattern(input: $input) { createTrackerPattern(input: $input) {
cookiePatternEdge { node { id } } trackerPatternEdge { node { id } }
cookieBanner { id } cookieBanner { id }
} }
} }
@@ -632,7 +632,7 @@ func TestCookiePattern_RBAC(t *testing.T) {
"description": "Should fail", "description": "Should fail",
}, },
}) })
testutil.RequireForbiddenError(t, err, "viewer should not be able to create cookie pattern") testutil.RequireForbiddenError(t, err, "viewer should not be able to create tracker pattern")
}) })
t.Run("viewer cannot update pattern", func(t *testing.T) { t.Run("viewer cannot update pattern", func(t *testing.T) {
@@ -642,22 +642,22 @@ func TestCookiePattern_RBAC(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID) patternID := factory.CreateTrackerPattern(owner, categoryID)
_, err := viewer.Do(` _, err := viewer.Do(`
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { updateTrackerPattern(input: $input) {
cookiePattern { id } trackerPattern { id }
cookieBanner { id } cookieBanner { id }
} }
} }
`, map[string]any{ `, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"displayName": "Updated by Viewer", "displayName": "Updated by Viewer",
}, },
}) })
testutil.RequireForbiddenError(t, err, "viewer should not be able to update cookie pattern") testutil.RequireForbiddenError(t, err, "viewer should not be able to update tracker pattern")
}) })
t.Run("viewer cannot delete pattern", func(t *testing.T) { t.Run("viewer cannot delete pattern", func(t *testing.T) {
@@ -667,19 +667,19 @@ func TestCookiePattern_RBAC(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID) categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID) patternID := factory.CreateTrackerPattern(owner, categoryID)
_, err := viewer.Do(` _, err := viewer.Do(`
mutation DeleteCookiePattern($input: DeleteCookiePatternInput!) { mutation DeleteTrackerPattern($input: DeleteTrackerPatternInput!) {
deleteCookiePattern(input: $input) { deleteTrackerPattern(input: $input) {
deletedCookiePatternId deletedTrackerPatternId
cookieBanner { id } cookieBanner { id }
} }
} }
`, map[string]any{ `, map[string]any{
"input": map[string]any{"cookiePatternId": patternID}, "input": map[string]any{"trackerPatternId": patternID},
}) })
testutil.RequireForbiddenError(t, err, "viewer should not be able to delete cookie pattern") testutil.RequireForbiddenError(t, err, "viewer should not be able to delete tracker pattern")
}) })
t.Run("viewer cannot move pattern", func(t *testing.T) { t.Run("viewer cannot move pattern", func(t *testing.T) {
@@ -690,21 +690,21 @@ func TestCookiePattern_RBAC(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner) bannerID := factory.CreateCookieBanner(owner)
categoryA := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "rbac-move-a"}) categoryA := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "rbac-move-a"})
categoryB := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "rbac-move-b"}) categoryB := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "rbac-move-b"})
patternID := factory.CreateCookiePattern(owner, categoryA) patternID := factory.CreateTrackerPattern(owner, categoryA)
_, err := viewer.Do(` _, err := viewer.Do(`
mutation MoveCookiePatternToCategory($input: MoveCookiePatternToCategoryInput!) { mutation MoveTrackerPatternToCategory($input: MoveTrackerPatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) { moveTrackerPatternToCategory(input: $input) {
cookiePattern { id } trackerPattern { id }
cookieBanner { id } cookieBanner { id }
} }
} }
`, map[string]any{ `, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": patternID, "trackerPatternId": patternID,
"targetCookieCategoryId": categoryB, "targetCookieCategoryId": categoryB,
}, },
}) })
testutil.RequireForbiddenError(t, err, "viewer should not be able to move cookie pattern") testutil.RequireForbiddenError(t, err, "viewer should not be able to move tracker pattern")
}) })
} }

View File

@@ -1343,7 +1343,7 @@ func CreateCookieCategory(c *testutil.Client, bannerID string, attrs ...Attrs) s
return result.CreateCookieCategory.CookieCategoryEdge.Node.ID return result.CreateCookieCategory.CookieCategoryEdge.Node.ID
} }
func CreateCookiePattern(c *testutil.Client, categoryID string, attrs ...Attrs) string { func CreateTrackerPattern(c *testutil.Client, categoryID string, attrs ...Attrs) string {
c.T.Helper() c.T.Helper()
var a Attrs var a Attrs
@@ -1352,9 +1352,9 @@ func CreateCookiePattern(c *testutil.Client, categoryID string, attrs ...Attrs)
} }
const query = ` const query = `
mutation($input: CreateCookiePatternInput!) { mutation($input: CreateTrackerPatternInput!) {
createCookiePattern(input: $input) { createTrackerPattern(input: $input) {
cookiePatternEdge { trackerPatternEdge {
node { id } node { id }
} }
} }
@@ -1363,27 +1363,28 @@ func CreateCookiePattern(c *testutil.Client, categoryID string, attrs ...Attrs)
input := map[string]any{ input := map[string]any{
"cookieCategoryId": categoryID, "cookieCategoryId": categoryID,
"trackerType": a.getString("trackerType", "COOKIE"),
"pattern": a.getString("pattern", gofakeit.LetterN(8)+"_cookie"), "pattern": a.getString("pattern", gofakeit.LetterN(8)+"_cookie"),
"matchType": a.getString("matchType", "EXACT"), "matchType": a.getString("matchType", "EXACT"),
"displayName": a.getString("displayName", SafeName("Pattern")), "displayName": a.getString("displayName", SafeName("Pattern")),
"description": a.getString("description", "Test cookie pattern"), "description": a.getString("description", "Test tracker pattern"),
} }
if _, ok := a["maxAgeSeconds"]; ok { if _, ok := a["maxAgeSeconds"]; ok {
input["maxAgeSeconds"] = a.getInt("maxAgeSeconds", 0) input["maxAgeSeconds"] = a.getInt("maxAgeSeconds", 0)
} }
var result struct { var result struct {
CreateCookiePattern struct { CreateTrackerPattern struct {
CookiePatternEdge struct { TrackerPatternEdge struct {
Node struct { Node struct {
ID string `json:"id"` ID string `json:"id"`
} `json:"node"` } `json:"node"`
} `json:"cookiePatternEdge"` } `json:"trackerPatternEdge"`
} `json:"createCookiePattern"` } `json:"createTrackerPattern"`
} }
err := c.Execute(query, map[string]any{"input": input}, &result) err := c.Execute(query, map[string]any{"input": input}, &result)
require.NoError(c.T, err, "createCookiePattern mutation failed") require.NoError(c.T, err, "createTrackerPattern mutation failed")
return result.CreateCookiePattern.CookiePatternEdge.Node.ID return result.CreateTrackerPattern.TrackerPatternEdge.Node.ID
} }

View File

@@ -116,11 +116,6 @@ export class Probo implements INodeType {
value: 'cookieConsentRecord', value: 'cookieConsentRecord',
description: 'View cookie consent records', description: 'View cookie consent records',
}, },
{
name: 'Cookie Pattern',
value: 'cookiePattern',
description: 'Manage cookie patterns',
},
{ {
name: 'Data', name: 'Data',
value: 'datum', value: 'datum',
@@ -206,6 +201,11 @@ export class Probo implements INodeType {
value: 'tia', value: 'tia',
description: 'Manage transfer impact assessments', description: 'Manage transfer impact assessments',
}, },
{
name: 'Tracker Pattern',
value: 'trackerPattern',
description: 'Manage tracker patterns',
},
{ {
name: 'Trust Center', name: 'Trust Center',
value: 'trustCenter', value: 'trustCenter',

View File

@@ -21,7 +21,7 @@ import * as control from './control';
import * as cookieBanner from './cookieBanner'; import * as cookieBanner from './cookieBanner';
import * as cookieCategory from './cookieCategory'; import * as cookieCategory from './cookieCategory';
import * as cookieConsentRecord from './cookieConsentRecord'; import * as cookieConsentRecord from './cookieConsentRecord';
import * as cookiePattern from './cookiePattern'; import * as trackerPattern from './trackerPattern';
import * as datum from './datum'; import * as datum from './datum';
import * as document from './document'; import * as document from './document';
import * as dpia from './dpia'; import * as dpia from './dpia';
@@ -63,7 +63,7 @@ export const resources: Record<string, ResourceModule> = {
cookieBanner: cookieBanner as ResourceModule, cookieBanner: cookieBanner as ResourceModule,
cookieCategory: cookieCategory as ResourceModule, cookieCategory: cookieCategory as ResourceModule,
cookieConsentRecord: cookieConsentRecord as ResourceModule, cookieConsentRecord: cookieConsentRecord as ResourceModule,
cookiePattern: cookiePattern as ResourceModule, trackerPattern: trackerPattern as ResourceModule,
datum: datum as ResourceModule, datum: datum as ResourceModule,
document: document as ResourceModule, document: document as ResourceModule,
dpia: dpia as ResourceModule, dpia: dpia as ResourceModule,

View File

@@ -22,7 +22,7 @@ export const description: INodeProperties[] = [
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['create'], operation: ['create'],
}, },
}, },
@@ -36,12 +36,12 @@ export const description: INodeProperties[] = [
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['create'], operation: ['create'],
}, },
}, },
default: '', default: '',
description: 'The cookie name pattern to match', description: 'The tracker name pattern to match',
required: true, required: true,
}, },
{ {
@@ -50,7 +50,7 @@ export const description: INodeProperties[] = [
type: 'options', type: 'options',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['create'], operation: ['create'],
}, },
}, },
@@ -65,7 +65,7 @@ export const description: INodeProperties[] = [
}, },
], ],
default: 'EXACT', default: 'EXACT',
description: 'How the pattern should be matched against cookie names', description: 'How the pattern should be matched against tracker names',
required: true, required: true,
}, },
{ {
@@ -74,12 +74,12 @@ export const description: INodeProperties[] = [
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['create'], operation: ['create'],
}, },
}, },
default: '', default: '',
description: 'The display name for the cookie pattern', description: 'The display name for the tracker pattern',
required: true, required: true,
}, },
{ {
@@ -88,12 +88,12 @@ export const description: INodeProperties[] = [
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['create'], operation: ['create'],
}, },
}, },
default: '', default: '',
description: 'The description of the cookie pattern', description: 'The description of the tracker pattern',
required: true, required: true,
}, },
{ {
@@ -102,7 +102,7 @@ export const description: INodeProperties[] = [
type: 'number', type: 'number',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['create'], operation: ['create'],
}, },
}, },
@@ -123,9 +123,9 @@ export async function execute(
const maxAgeSeconds = this.getNodeParameter('maxAgeSeconds', itemIndex, 0) as number; const maxAgeSeconds = this.getNodeParameter('maxAgeSeconds', itemIndex, 0) as number;
const query = ` const query = `
mutation CreateCookiePattern($input: CreateCookiePatternInput!) { mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createCookiePattern(input: $input) { createTrackerPattern(input: $input) {
cookiePatternEdge { trackerPatternEdge {
node { node {
id id
pattern pattern

View File

@@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions';
export const description: INodeProperties[] = [ export const description: INodeProperties[] = [
{ {
displayName: 'Cookie Pattern ID', displayName: 'Tracker Pattern ID',
name: 'cookiePatternId', name: 'trackerPatternId',
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['delete'], operation: ['delete'],
}, },
}, },
default: '', default: '',
description: 'The ID of the cookie pattern to delete', description: 'The ID of the tracker pattern to delete',
required: true, required: true,
}, },
]; ];
@@ -36,12 +36,12 @@ export async function execute(
this: IExecuteFunctions, this: IExecuteFunctions,
itemIndex: number, itemIndex: number,
): Promise<INodeExecutionData> { ): Promise<INodeExecutionData> {
const cookiePatternId = this.getNodeParameter('cookiePatternId', itemIndex) as string; const trackerPatternId = this.getNodeParameter('trackerPatternId', itemIndex) as string;
const query = ` const query = `
mutation DeleteCookiePattern($input: DeleteCookiePatternInput!) { mutation DeleteTrackerPattern($input: DeleteTrackerPatternInput!) {
deleteCookiePattern(input: $input) { deleteTrackerPattern(input: $input) {
deletedCookiePatternId deletedTrackerPatternId
cookieBanner { cookieBanner {
id id
name name
@@ -50,7 +50,7 @@ export async function execute(
} }
`; `;
const responseData = await proboApiRequest.call(this, query, { input: { cookiePatternId } }); const responseData = await proboApiRequest.call(this, query, { input: { trackerPatternId } });
return { return {
json: responseData, json: responseData,

View File

@@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions';
export const description: INodeProperties[] = [ export const description: INodeProperties[] = [
{ {
displayName: 'Cookie Pattern ID', displayName: 'Tracker Pattern ID',
name: 'cookiePatternId', name: 'trackerPatternId',
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['get'], operation: ['get'],
}, },
}, },
default: '', default: '',
description: 'The ID of the cookie pattern', description: 'The ID of the tracker pattern',
required: true, required: true,
}, },
]; ];
@@ -36,12 +36,12 @@ export async function execute(
this: IExecuteFunctions, this: IExecuteFunctions,
itemIndex: number, itemIndex: number,
): Promise<INodeExecutionData> { ): Promise<INodeExecutionData> {
const cookiePatternId = this.getNodeParameter('cookiePatternId', itemIndex) as string; const trackerPatternId = this.getNodeParameter('trackerPatternId', itemIndex) as string;
const query = ` const query = `
query GetCookiePattern($cookiePatternId: ID!) { query GetTrackerPattern($trackerPatternId: ID!) {
node(id: $cookiePatternId) { node(id: $trackerPatternId) {
... on CookiePattern { ... on TrackerPattern {
id id
pattern pattern
matchType matchType
@@ -58,7 +58,7 @@ export async function execute(
} }
`; `;
const responseData = await proboApiRequest.call(this, query, { cookiePatternId }); const responseData = await proboApiRequest.call(this, query, { trackerPatternId });
return { return {
json: responseData, json: responseData,

View File

@@ -22,7 +22,7 @@ export const description: INodeProperties[] = [
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['getAll'], operation: ['getAll'],
}, },
}, },
@@ -36,7 +36,7 @@ export const description: INodeProperties[] = [
type: 'boolean', type: 'boolean',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['getAll'], operation: ['getAll'],
}, },
}, },
@@ -49,7 +49,7 @@ export const description: INodeProperties[] = [
type: 'number', type: 'number',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['getAll'], operation: ['getAll'],
returnAll: [false], returnAll: [false],
}, },
@@ -71,10 +71,10 @@ export async function execute(
const limit = this.getNodeParameter('limit', itemIndex, 50) as number; const limit = this.getNodeParameter('limit', itemIndex, 50) as number;
const query = ` const query = `
query GetCookiePatterns($cookieCategoryId: ID!, $first: Int, $after: CursorKey) { query GetTrackerPatterns($cookieCategoryId: ID!, $first: Int, $after: CursorKey) {
node(id: $cookieCategoryId) { node(id: $cookieCategoryId) {
... on CookieCategory { ... on CookieCategory {
cookiePatterns(first: $first, after: $after) { trackerPatterns(first: $first, after: $after) {
edges { edges {
node { node {
id id
@@ -100,21 +100,21 @@ export async function execute(
} }
`; `;
const cookiePatterns = await proboApiRequestAllItems.call( const trackerPatterns = await proboApiRequestAllItems.call(
this, this,
query, query,
{ cookieCategoryId }, { cookieCategoryId },
(response) => { (response) => {
const data = response?.data as IDataObject | undefined; const data = response?.data as IDataObject | undefined;
const node = data?.node as IDataObject | undefined; const node = data?.node as IDataObject | undefined;
return node?.cookiePatterns as IDataObject | undefined; return node?.trackerPatterns as IDataObject | undefined;
}, },
returnAll, returnAll,
limit, limit,
); );
return { return {
json: { cookiePatterns }, json: { trackerPatterns },
pairedItem: { item: itemIndex }, pairedItem: { item: itemIndex },
}; };
} }

View File

@@ -28,45 +28,45 @@ export const description: INodeProperties[] = [
noDataExpression: true, noDataExpression: true,
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
}, },
}, },
options: [ options: [
{ {
name: 'Create', name: 'Create',
value: 'create', value: 'create',
description: 'Create a new cookie pattern', description: 'Create a new tracker pattern',
action: 'Create a cookie pattern', action: 'Create a tracker pattern',
}, },
{ {
name: 'Delete', name: 'Delete',
value: 'delete', value: 'delete',
description: 'Delete a cookie pattern', description: 'Delete a tracker pattern',
action: 'Delete a cookie pattern', action: 'Delete a tracker pattern',
}, },
{ {
name: 'Get', name: 'Get',
value: 'get', value: 'get',
description: 'Get a cookie pattern', description: 'Get a tracker pattern',
action: 'Get a cookie pattern', action: 'Get a tracker pattern',
}, },
{ {
name: 'Get Many', name: 'Get Many',
value: 'getAll', value: 'getAll',
description: 'Get many cookie patterns', description: 'Get many tracker patterns',
action: 'Get many cookie patterns', action: 'Get many tracker patterns',
}, },
{ {
name: 'Move', name: 'Move',
value: 'move', value: 'move',
description: 'Move a cookie pattern to a different category', description: 'Move a tracker pattern to a different category',
action: 'Move a cookie pattern', action: 'Move a tracker pattern',
}, },
{ {
name: 'Update', name: 'Update',
value: 'update', value: 'update',
description: 'Update an existing cookie pattern', description: 'Update an existing tracker pattern',
action: 'Update a cookie pattern', action: 'Update a tracker pattern',
}, },
], ],
default: 'create', default: 'create',

View File

@@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions';
export const description: INodeProperties[] = [ export const description: INodeProperties[] = [
{ {
displayName: 'Cookie Pattern ID', displayName: 'Tracker Pattern ID',
name: 'cookiePatternId', name: 'trackerPatternId',
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['move'], operation: ['move'],
}, },
}, },
default: '', default: '',
description: 'The ID of the cookie pattern to move', description: 'The ID of the tracker pattern to move',
required: true, required: true,
}, },
{ {
@@ -36,7 +36,7 @@ export const description: INodeProperties[] = [
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['move'], operation: ['move'],
}, },
}, },
@@ -50,13 +50,13 @@ export async function execute(
this: IExecuteFunctions, this: IExecuteFunctions,
itemIndex: number, itemIndex: number,
): Promise<INodeExecutionData> { ): Promise<INodeExecutionData> {
const cookiePatternId = this.getNodeParameter('cookiePatternId', itemIndex) as string; const trackerPatternId = this.getNodeParameter('trackerPatternId', itemIndex) as string;
const targetCookieCategoryId = this.getNodeParameter('targetCookieCategoryId', itemIndex) as string; const targetCookieCategoryId = this.getNodeParameter('targetCookieCategoryId', itemIndex) as string;
const query = ` const query = `
mutation MoveCookiePatternToCategory($input: MoveCookiePatternToCategoryInput!) { mutation MoveTrackerPatternToCategory($input: MoveTrackerPatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) { moveTrackerPatternToCategory(input: $input) {
cookiePattern { trackerPattern {
id id
pattern pattern
matchType matchType
@@ -77,7 +77,7 @@ export async function execute(
`; `;
const responseData = await proboApiRequest.call(this, query, { const responseData = await proboApiRequest.call(this, query, {
input: { cookiePatternId, targetCookieCategoryId }, input: { trackerPatternId, targetCookieCategoryId },
}); });
return { return {

View File

@@ -17,17 +17,17 @@ import { proboApiRequest } from '../../GenericFunctions';
export const description: INodeProperties[] = [ export const description: INodeProperties[] = [
{ {
displayName: 'Cookie Pattern ID', displayName: 'Tracker Pattern ID',
name: 'cookiePatternId', name: 'trackerPatternId',
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['update'], operation: ['update'],
}, },
}, },
default: '', default: '',
description: 'The ID of the cookie pattern to update', description: 'The ID of the tracker pattern to update',
required: true, required: true,
}, },
{ {
@@ -36,12 +36,12 @@ export const description: INodeProperties[] = [
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['update'], operation: ['update'],
}, },
}, },
default: '', default: '',
description: 'The display name for the cookie pattern', description: 'The display name for the tracker pattern',
}, },
{ {
displayName: 'Excluded', displayName: 'Excluded',
@@ -49,7 +49,7 @@ export const description: INodeProperties[] = [
type: 'options', type: 'options',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['update'], operation: ['update'],
}, },
}, },
@@ -68,7 +68,7 @@ export const description: INodeProperties[] = [
}, },
], ],
default: '', default: '',
description: 'Whether the cookie pattern is excluded from the banner', description: 'Whether the tracker pattern is excluded from the banner',
}, },
{ {
displayName: 'Description', displayName: 'Description',
@@ -76,12 +76,12 @@ export const description: INodeProperties[] = [
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['update'], operation: ['update'],
}, },
}, },
default: '', default: '',
description: 'The description of the cookie pattern', description: 'The description of the tracker pattern',
}, },
{ {
displayName: 'Additional Fields', displayName: 'Additional Fields',
@@ -91,7 +91,7 @@ export const description: INodeProperties[] = [
default: {}, default: {},
displayOptions: { displayOptions: {
show: { show: {
resource: ['cookiePattern'], resource: ['trackerPattern'],
operation: ['update'], operation: ['update'],
}, },
}, },
@@ -114,7 +114,7 @@ export async function execute(
this: IExecuteFunctions, this: IExecuteFunctions,
itemIndex: number, itemIndex: number,
): Promise<INodeExecutionData> { ): Promise<INodeExecutionData> {
const cookiePatternId = this.getNodeParameter('cookiePatternId', itemIndex) as string; const trackerPatternId = this.getNodeParameter('trackerPatternId', itemIndex) as string;
const displayName = this.getNodeParameter('displayName', itemIndex, '') as string; const displayName = this.getNodeParameter('displayName', itemIndex, '') as string;
const excluded = this.getNodeParameter('excluded', itemIndex, '') as string; const excluded = this.getNodeParameter('excluded', itemIndex, '') as string;
const patternDescription = this.getNodeParameter('patternDescription', itemIndex, '') as string; const patternDescription = this.getNodeParameter('patternDescription', itemIndex, '') as string;
@@ -123,9 +123,9 @@ export async function execute(
}; };
const query = ` const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) { mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { updateTrackerPattern(input: $input) {
cookiePattern { trackerPattern {
id id
pattern pattern
matchType matchType
@@ -145,7 +145,7 @@ export async function execute(
} }
`; `;
const input: Record<string, unknown> = { cookiePatternId }; const input: Record<string, unknown> = { trackerPatternId };
if (displayName) input.displayName = displayName; if (displayName) input.displayName = displayName;
if (excluded) input.excluded = excluded === 'true'; if (excluded) input.excluded = excluded === 'true';
if (patternDescription) input.description = patternDescription; if (patternDescription) input.description = patternDescription;

View File

@@ -31,7 +31,6 @@ import (
"go.probo.inc/probo/pkg/cmd/control" "go.probo.inc/probo/pkg/cmd/control"
cookiebanner "go.probo.inc/probo/pkg/cmd/cookie-banner" cookiebanner "go.probo.inc/probo/pkg/cmd/cookie-banner"
cookiecategory "go.probo.inc/probo/pkg/cmd/cookie-category" cookiecategory "go.probo.inc/probo/pkg/cmd/cookie-category"
cookiepattern "go.probo.inc/probo/pkg/cmd/cookie-pattern"
"go.probo.inc/probo/pkg/cmd/datum" "go.probo.inc/probo/pkg/cmd/datum"
"go.probo.inc/probo/pkg/cmd/document" "go.probo.inc/probo/pkg/cmd/document"
"go.probo.inc/probo/pkg/cmd/dpia" "go.probo.inc/probo/pkg/cmd/dpia"
@@ -48,6 +47,7 @@ import (
"go.probo.inc/probo/pkg/cmd/soa" "go.probo.inc/probo/pkg/cmd/soa"
"go.probo.inc/probo/pkg/cmd/task" "go.probo.inc/probo/pkg/cmd/task"
"go.probo.inc/probo/pkg/cmd/tia" "go.probo.inc/probo/pkg/cmd/tia"
trackerpattern "go.probo.inc/probo/pkg/cmd/tracker-pattern"
trustcenter "go.probo.inc/probo/pkg/cmd/trust-center" trustcenter "go.probo.inc/probo/pkg/cmd/trust-center"
"go.probo.inc/probo/pkg/cmd/user" "go.probo.inc/probo/pkg/cmd/user"
"go.probo.inc/probo/pkg/cmd/vendormgmt" "go.probo.inc/probo/pkg/cmd/vendormgmt"
@@ -99,7 +99,7 @@ func NewCmdRoot(f *cmdutil.Factory) *cobra.Command {
cmd.AddCommand(control.NewCmdControl(f)) cmd.AddCommand(control.NewCmdControl(f))
cmd.AddCommand(cookiebanner.NewCmdCookieBanner(f)) cmd.AddCommand(cookiebanner.NewCmdCookieBanner(f))
cmd.AddCommand(cookiecategory.NewCmdCookieCategory(f)) cmd.AddCommand(cookiecategory.NewCmdCookieCategory(f))
cmd.AddCommand(cookiepattern.NewCmdCookiePattern(f)) cmd.AddCommand(trackerpattern.NewCmdTrackerPattern(f))
cmd.AddCommand(datum.NewCmdDatum(f)) cmd.AddCommand(datum.NewCmdDatum(f))
cmd.AddCommand(document.NewCmdDocument(f)) cmd.AddCommand(document.NewCmdDocument(f))
cmd.AddCommand(dpia.NewCmdDPIA(f)) cmd.AddCommand(dpia.NewCmdDPIA(f))

View File

@@ -25,9 +25,9 @@ import (
) )
const createMutation = ` const createMutation = `
mutation($input: CreateCookiePatternInput!) { mutation($input: CreateTrackerPatternInput!) {
createCookiePattern(input: $input) { createTrackerPattern(input: $input) {
cookiePatternEdge { trackerPatternEdge {
node { node {
id id
pattern pattern
@@ -42,15 +42,15 @@ mutation($input: CreateCookiePatternInput!) {
` `
type createResponse struct { type createResponse struct {
CreateCookiePattern struct { CreateTrackerPattern struct {
CookiePatternEdge struct { TrackerPatternEdge struct {
Node struct { Node struct {
ID string `json:"id"` ID string `json:"id"`
Pattern string `json:"pattern"` Pattern string `json:"pattern"`
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
} `json:"node"` } `json:"node"`
} `json:"cookiePatternEdge"` } `json:"trackerPatternEdge"`
} `json:"createCookiePattern"` } `json:"createTrackerPattern"`
} }
func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
@@ -65,7 +65,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create", Use: "create",
Short: "Create a new cookie pattern", Short: "Create a new tracker pattern",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config() cfg, err := f.Config()
if err != nil { if err != nil {
@@ -87,7 +87,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
if f.IOStreams.IsInteractive() { if f.IOStreams.IsInteractive() {
if flagPattern == "" { if flagPattern == "" {
if err := huh.NewInput().Title("Cookie pattern").Value(&flagPattern).Run(); err != nil { if err := huh.NewInput().Title("Tracker pattern").Value(&flagPattern).Run(); err != nil {
return err return err
} }
} }
@@ -142,8 +142,8 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
return fmt.Errorf("cannot parse response: %w", err) return fmt.Errorf("cannot parse response: %w", err)
} }
p := resp.CreateCookiePattern.CookiePatternEdge.Node p := resp.CreateTrackerPattern.TrackerPatternEdge.Node
_, _ = fmt.Fprintf(f.IOStreams.Out, "Created cookie pattern %s (%s)\n", p.ID, p.DisplayName) _, _ = fmt.Fprintf(f.IOStreams.Out, "Created tracker pattern %s (%s)\n", p.ID, p.DisplayName)
return nil return nil
}, },
@@ -151,7 +151,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
cmd.Flags().StringVar(&flagCategoryID, "category-id", "", "Cookie category ID (required)") cmd.Flags().StringVar(&flagCategoryID, "category-id", "", "Cookie category ID (required)")
_ = cmd.MarkFlagRequired("category-id") _ = cmd.MarkFlagRequired("category-id")
cmd.Flags().StringVar(&flagPattern, "pattern", "", "Cookie pattern (required)") cmd.Flags().StringVar(&flagPattern, "pattern", "", "Tracker pattern (required)")
cmd.Flags().StringVar(&flagMatchType, "match-type", "", "Match type: EXACT or PREFIX (required)") cmd.Flags().StringVar(&flagMatchType, "match-type", "", "Match type: EXACT or PREFIX (required)")
cmd.Flags().StringVar(&flagDisplayName, "display-name", "", "Display name (required)") cmd.Flags().StringVar(&flagDisplayName, "display-name", "", "Display name (required)")
cmd.Flags().StringVar(&flagDescription, "description", "", "Description") cmd.Flags().StringVar(&flagDescription, "description", "", "Description")

View File

@@ -24,9 +24,9 @@ import (
) )
const deleteMutation = ` const deleteMutation = `
mutation($input: DeleteCookiePatternInput!) { mutation($input: DeleteTrackerPatternInput!) {
deleteCookiePattern(input: $input) { deleteTrackerPattern(input: $input) {
deletedCookiePatternId deletedTrackerPatternId
cookieBanner { cookieBanner {
id id
} }
@@ -39,15 +39,15 @@ func NewCmdDelete(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "delete <id>", Use: "delete <id>",
Short: "Delete a cookie pattern", Short: "Delete a tracker pattern",
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if !flagYes { if !flagYes {
if !f.IOStreams.IsInteractive() { if !f.IOStreams.IsInteractive() {
return fmt.Errorf("cannot delete cookie pattern: confirmation required, use --yes to confirm") return fmt.Errorf("cannot delete tracker pattern: confirmation required, use --yes to confirm")
} }
var confirmed bool var confirmed bool
if err := huh.NewConfirm().Title(fmt.Sprintf("Delete cookie pattern %s?", args[0])).Value(&confirmed).Run(); err != nil { if err := huh.NewConfirm().Title(fmt.Sprintf("Delete tracker pattern %s?", args[0])).Value(&confirmed).Run(); err != nil {
return err return err
} }
if !confirmed { if !confirmed {
@@ -74,13 +74,13 @@ func NewCmdDelete(f *cmdutil.Factory) *cobra.Command {
) )
_, err = client.Do(deleteMutation, map[string]any{ _, err = client.Do(deleteMutation, map[string]any{
"input": map[string]any{"cookiePatternId": args[0]}, "input": map[string]any{"trackerPatternId": args[0]},
}) })
if err != nil { if err != nil {
return err return err
} }
_, _ = fmt.Fprintf(f.IOStreams.Out, "Deleted cookie pattern %s\n", args[0]) _, _ = fmt.Fprintf(f.IOStreams.Out, "Deleted tracker pattern %s\n", args[0])
return nil return nil
}, },

View File

@@ -28,13 +28,14 @@ query($id: ID!, $first: Int, $after: CursorKey) {
node(id: $id) { node(id: $id) {
__typename __typename
... on CookieCategory { ... on CookieCategory {
cookiePatterns(first: $first, after: $after) { trackerPatterns(first: $first, after: $after) {
totalCount totalCount
edges { edges {
node { node {
id id
pattern pattern
matchType matchType
trackerType
displayName displayName
source source
excluded excluded
@@ -51,10 +52,11 @@ query($id: ID!, $first: Int, $after: CursorKey) {
} }
` `
type cookiePattern struct { type trackerPattern struct {
ID string `json:"id"` ID string `json:"id"`
Pattern string `json:"pattern"` Pattern string `json:"pattern"`
MatchType string `json:"matchType"` MatchType string `json:"matchType"`
TrackerType string `json:"trackerType"`
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
Source string `json:"source"` Source string `json:"source"`
Excluded bool `json:"excluded"` Excluded bool `json:"excluded"`
@@ -70,7 +72,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "list", Use: "list",
Short: "List cookie patterns in a category", Short: "List tracker patterns in a category",
Aliases: []string{"ls"}, Aliases: []string{"ls"},
Args: cobra.NoArgs, Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
@@ -103,11 +105,11 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
listQuery, listQuery,
variables, variables,
flagLimit, flagLimit,
func(data json.RawMessage) (*api.Connection[cookiePattern], error) { func(data json.RawMessage) (*api.Connection[trackerPattern], error) {
var resp struct { var resp struct {
Node *struct { Node *struct {
Typename string `json:"__typename"` Typename string `json:"__typename"`
CookiePatterns api.Connection[cookiePattern] `json:"cookiePatterns"` TrackerPatterns api.Connection[trackerPattern] `json:"trackerPatterns"`
} `json:"node"` } `json:"node"`
} }
if err := json.Unmarshal(data, &resp); err != nil { if err := json.Unmarshal(data, &resp); err != nil {
@@ -119,7 +121,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
if resp.Node.Typename != "CookieCategory" { if resp.Node.Typename != "CookieCategory" {
return nil, fmt.Errorf("expected CookieCategory node, got %s", resp.Node.Typename) return nil, fmt.Errorf("expected CookieCategory node, got %s", resp.Node.Typename)
} }
return &resp.Node.CookiePatterns, nil return &resp.Node.TrackerPatterns, nil
}, },
) )
if err != nil { if err != nil {
@@ -131,7 +133,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
} }
if len(patterns) == 0 { if len(patterns) == 0 {
_, _ = fmt.Fprintln(f.IOStreams.Out, "No cookie patterns found.") _, _ = fmt.Fprintln(f.IOStreams.Out, "No tracker patterns found.")
return nil return nil
} }
@@ -145,14 +147,14 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
if p.LastMatchedAt != nil { if p.LastMatchedAt != nil {
lastMatched = cmdutil.FormatTime(*p.LastMatchedAt) lastMatched = cmdutil.FormatTime(*p.LastMatchedAt)
} }
rows = append(rows, []string{p.ID, p.Pattern, p.MatchType, p.DisplayName, p.Source, excluded, lastMatched}) rows = append(rows, []string{p.ID, p.Pattern, p.MatchType, p.TrackerType, p.DisplayName, p.Source, excluded, lastMatched})
} }
t := cmdutil.NewTable("ID", "PATTERN", "MATCH TYPE", "DISPLAY NAME", "SOURCE", "EXCLUDED", "LAST MATCHED").Rows(rows...) t := cmdutil.NewTable("ID", "PATTERN", "MATCH TYPE", "TRACKER TYPE", "DISPLAY NAME", "SOURCE", "EXCLUDED", "LAST MATCHED").Rows(rows...)
_, _ = fmt.Fprintln(f.IOStreams.Out, t) _, _ = fmt.Fprintln(f.IOStreams.Out, t)
if totalCount > len(patterns) { if totalCount > len(patterns) {
_, _ = fmt.Fprintf(f.IOStreams.ErrOut, "\nShowing %d of %d cookie patterns\n", len(patterns), totalCount) _, _ = fmt.Fprintf(f.IOStreams.ErrOut, "\nShowing %d of %d tracker patterns\n", len(patterns), totalCount)
} }
return nil return nil

View File

@@ -24,9 +24,9 @@ import (
) )
const moveMutation = ` const moveMutation = `
mutation($input: MoveCookiePatternToCategoryInput!) { mutation($input: MoveTrackerPatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) { moveTrackerPatternToCategory(input: $input) {
cookiePattern { trackerPattern {
id id
cookieCategory { cookieCategory {
id id
@@ -41,15 +41,15 @@ mutation($input: MoveCookiePatternToCategoryInput!) {
` `
type moveResponse struct { type moveResponse struct {
MoveCookiePatternToCategory struct { MoveTrackerPatternToCategory struct {
CookiePattern struct { TrackerPattern struct {
ID string `json:"id"` ID string `json:"id"`
CookieCategory struct { CookieCategory struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
} `json:"cookieCategory"` } `json:"cookieCategory"`
} `json:"cookiePattern"` } `json:"trackerPattern"`
} `json:"moveCookiePatternToCategory"` } `json:"moveTrackerPatternToCategory"`
} }
func NewCmdMove(f *cmdutil.Factory) *cobra.Command { func NewCmdMove(f *cmdutil.Factory) *cobra.Command {
@@ -57,7 +57,7 @@ func NewCmdMove(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "move <id>", Use: "move <id>",
Short: "Move a cookie pattern to a different category", Short: "Move a tracker pattern to a different category",
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config() cfg, err := f.Config()
@@ -80,7 +80,7 @@ func NewCmdMove(f *cmdutil.Factory) *cobra.Command {
data, err := client.Do(moveMutation, map[string]any{ data, err := client.Do(moveMutation, map[string]any{
"input": map[string]any{ "input": map[string]any{
"cookiePatternId": args[0], "trackerPatternId": args[0],
"targetCookieCategoryId": flagTargetCategoryID, "targetCookieCategoryId": flagTargetCategoryID,
}, },
}) })
@@ -93,8 +93,8 @@ func NewCmdMove(f *cmdutil.Factory) *cobra.Command {
return fmt.Errorf("cannot parse response: %w", err) return fmt.Errorf("cannot parse response: %w", err)
} }
p := resp.MoveCookiePatternToCategory.CookiePattern p := resp.MoveTrackerPatternToCategory.TrackerPattern
_, _ = fmt.Fprintf(f.IOStreams.Out, "Moved cookie pattern %s to category %s\n", p.ID, p.CookieCategory.Name) _, _ = fmt.Fprintf(f.IOStreams.Out, "Moved tracker pattern %s to category %s\n", p.ID, p.CookieCategory.Name)
return nil return nil
}, },

View File

@@ -12,23 +12,23 @@
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE. // PERFORMANCE OF THIS SOFTWARE.
package cookiepattern package trackerpattern
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.probo.inc/probo/pkg/cmd/cmdutil" "go.probo.inc/probo/pkg/cmd/cmdutil"
"go.probo.inc/probo/pkg/cmd/cookie-pattern/create" "go.probo.inc/probo/pkg/cmd/tracker-pattern/create"
"go.probo.inc/probo/pkg/cmd/cookie-pattern/delete" "go.probo.inc/probo/pkg/cmd/tracker-pattern/delete"
"go.probo.inc/probo/pkg/cmd/cookie-pattern/list" "go.probo.inc/probo/pkg/cmd/tracker-pattern/list"
"go.probo.inc/probo/pkg/cmd/cookie-pattern/move" "go.probo.inc/probo/pkg/cmd/tracker-pattern/move"
"go.probo.inc/probo/pkg/cmd/cookie-pattern/update" "go.probo.inc/probo/pkg/cmd/tracker-pattern/update"
"go.probo.inc/probo/pkg/cmd/cookie-pattern/view" "go.probo.inc/probo/pkg/cmd/tracker-pattern/view"
) )
func NewCmdCookiePattern(f *cmdutil.Factory) *cobra.Command { func NewCmdTrackerPattern(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "cookie-pattern <command>", Use: "tracker-pattern <command>",
Short: "Manage cookie patterns", Short: "Manage tracker patterns",
} }
cmd.AddCommand(list.NewCmdList(f)) cmd.AddCommand(list.NewCmdList(f))

View File

@@ -24,9 +24,9 @@ import (
) )
const updateMutation = ` const updateMutation = `
mutation($input: UpdateCookiePatternInput!) { mutation($input: UpdateTrackerPatternInput!) {
updateCookiePattern(input: $input) { updateTrackerPattern(input: $input) {
cookiePattern { trackerPattern {
id id
displayName displayName
} }
@@ -38,12 +38,12 @@ mutation($input: UpdateCookiePatternInput!) {
` `
type updateResponse struct { type updateResponse struct {
UpdateCookiePattern struct { UpdateTrackerPattern struct {
CookiePattern struct { TrackerPattern struct {
ID string `json:"id"` ID string `json:"id"`
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
} `json:"cookiePattern"` } `json:"trackerPattern"`
} `json:"updateCookiePattern"` } `json:"updateTrackerPattern"`
} }
func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
@@ -56,7 +56,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "update <id>", Use: "update <id>",
Short: "Update a cookie pattern", Short: "Update a tracker pattern",
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := f.Config() cfg, err := f.Config()
@@ -77,7 +77,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
cmdutil.TokenRefreshOption(cfg, host, hc), cmdutil.TokenRefreshOption(cfg, host, hc),
) )
input := map[string]any{"cookiePatternId": args[0]} input := map[string]any{"trackerPatternId": args[0]}
if cmd.Flags().Changed("display-name") { if cmd.Flags().Changed("display-name") {
input["displayName"] = flagDisplayName input["displayName"] = flagDisplayName
@@ -106,8 +106,8 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
return fmt.Errorf("cannot parse response: %w", err) return fmt.Errorf("cannot parse response: %w", err)
} }
p := resp.UpdateCookiePattern.CookiePattern p := resp.UpdateTrackerPattern.TrackerPattern
_, _ = fmt.Fprintf(f.IOStreams.Out, "Updated cookie pattern %s (%s)\n", p.ID, p.DisplayName) _, _ = fmt.Fprintf(f.IOStreams.Out, "Updated tracker pattern %s (%s)\n", p.ID, p.DisplayName)
return nil return nil
}, },

View File

@@ -28,10 +28,11 @@ const viewQuery = `
query($id: ID!) { query($id: ID!) {
node(id: $id) { node(id: $id) {
__typename __typename
... on CookiePattern { ... on TrackerPattern {
id id
pattern pattern
matchType matchType
trackerType
displayName displayName
maxAgeSeconds maxAgeSeconds
description description
@@ -51,6 +52,7 @@ type viewResponse struct {
ID string `json:"id"` ID string `json:"id"`
Pattern string `json:"pattern"` Pattern string `json:"pattern"`
MatchType string `json:"matchType"` MatchType string `json:"matchType"`
TrackerType string `json:"trackerType"`
DisplayName string `json:"displayName"` DisplayName string `json:"displayName"`
MaxAgeSeconds *int `json:"maxAgeSeconds"` MaxAgeSeconds *int `json:"maxAgeSeconds"`
Description *string `json:"description"` Description *string `json:"description"`
@@ -67,7 +69,7 @@ func NewCmdView(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "view <id>", Use: "view <id>",
Short: "View a cookie pattern", Short: "View a tracker pattern",
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil { if err := cmdutil.ValidateOutputFlag(flagOutput); err != nil {
@@ -102,8 +104,8 @@ func NewCmdView(f *cmdutil.Factory) *cobra.Command {
return fmt.Errorf("cannot parse response: %w", err) return fmt.Errorf("cannot parse response: %w", err)
} }
if resp.Node == nil || resp.Node.Typename != "CookiePattern" { if resp.Node == nil || resp.Node.Typename != "TrackerPattern" {
return fmt.Errorf("cookie pattern %s not found", args[0]) return fmt.Errorf("tracker pattern %s not found", args[0])
} }
if *flagOutput == cmdutil.OutputJSON { if *flagOutput == cmdutil.OutputJSON {
@@ -120,6 +122,7 @@ func NewCmdView(f *cmdutil.Factory) *cobra.Command {
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("ID:"), v.ID) _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("ID:"), v.ID)
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Pattern:"), v.Pattern) _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Pattern:"), v.Pattern)
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Match Type:"), v.MatchType) _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Match Type:"), v.MatchType)
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Tracker Type:"), v.TrackerType)
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Source:"), v.Source) _, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Source:"), v.Source)
_, _ = fmt.Fprintf(out, "%s%v\n", label.Render("Excluded:"), v.Excluded) _, _ = fmt.Fprintf(out, "%s%v\n", label.Render("Excluded:"), v.Excluded)
if v.MaxAgeSeconds != nil { if v.MaxAgeSeconds != nil {

View File

@@ -32,7 +32,7 @@ var (
ErrCookieNotFound = errors.New("cookie not found") ErrCookieNotFound = errors.New("cookie not found")
ErrCategoriesBannerMismatch = errors.New("source and target categories belong to different banners") ErrCategoriesBannerMismatch = errors.New("source and target categories belong to different banners")
ErrPostHogConsentKindInvalid = errors.New("PostHog consent can only be enabled on normal categories") ErrPostHogConsentKindInvalid = errors.New("PostHog consent can only be enabled on normal categories")
ErrCookiePatternNotFound = errors.New("cookie pattern not found") ErrTrackerPatternNotFound = errors.New("tracker pattern not found")
ErrPatternAlreadyExists = errors.New("a pattern with this name already exists in this banner") ErrPatternAlreadyExists = errors.New("a pattern with this name already exists in this banner")
ErrSamePatternCategoryMove = errors.New("source and target cookie categories must be different") ErrSamePatternCategoryMove = errors.New("source and target cookie categories must be different")
) )

View File

@@ -84,33 +84,6 @@ type (
Rank int Rank int
} }
CreateCookiePatternRequest struct {
CookieCategoryID gid.GID
Pattern string
MatchType coredata.CookiePatternMatchType
DisplayName string
MaxAgeSeconds *int
Description string
}
UpdateCookiePatternRequest struct {
CookiePatternID gid.GID
DisplayName *string
MaxAgeSeconds **int
Description *string
Excluded *bool
}
MoveCookiePatternToCategoryRequest struct {
CookiePatternID gid.GID
TargetCookieCategoryID gid.GID
}
MoveCookiePatternToCategoryResult struct {
CookiePattern *coredata.CookiePattern
Banner *coredata.CookieBanner
}
CreateCookieConsentRecordRequest struct { CreateCookieConsentRecordRequest struct {
CookieBannerID gid.GID CookieBannerID gid.GID
Version int Version int
@@ -159,6 +132,17 @@ type (
Resources []DetectedResourceItem Resources []DetectedResourceItem
} }
CreateTrackerPatternRequest struct {
CookieCategoryID gid.GID
TrackerType coredata.TrackerType
Pattern string
MatchType coredata.TrackerPatternMatchType
DisplayName string
MaxAgeSeconds *int
Description string
Source *coredata.CookieSource
}
UpdateTrackerPatternRequest struct { UpdateTrackerPatternRequest struct {
TrackerPatternID gid.GID TrackerPatternID gid.GID
DisplayName *string DisplayName *string
@@ -266,46 +250,6 @@ func (r *ReorderCookieCategoryRequest) Validate() error {
return v.Error() return v.Error()
} }
func (r *CreateCookiePatternRequest) Validate() error {
v := validator.New()
v.Check(r.CookieCategoryID, "cookie_category_id", validator.Required(), validator.GID(coredata.CookieCategoryEntityType))
v.Check(r.Pattern, "pattern", validator.Required(), validator.SafeTextNoNewLine(255))
v.Check(string(r.MatchType), "match_type", validator.Required(), validator.OneOfSlice(
func() []string {
types := coredata.CookiePatternMatchTypes()
s := make([]string, len(types))
for i, t := range types {
s[i] = string(t)
}
return s
}(),
))
v.Check(r.DisplayName, "display_name", validator.Required(), validator.SafeTextNoNewLine(255))
v.Check(r.Description, "description", validator.SafeText(1000))
return v.Error()
}
func (r *UpdateCookiePatternRequest) Validate() error {
v := validator.New()
v.Check(r.CookiePatternID, "cookie_pattern_id", validator.Required(), validator.GID(coredata.CookiePatternEntityType))
v.Check(r.DisplayName, "display_name", validator.SafeTextNoNewLine(255))
v.Check(r.Description, "description", validator.SafeText(1000))
return v.Error()
}
func (r *MoveCookiePatternToCategoryRequest) Validate() error {
v := validator.New()
v.Check(r.CookiePatternID, "cookie_pattern_id", validator.Required(), validator.GID(coredata.CookiePatternEntityType))
v.Check(r.TargetCookieCategoryID, "target_cookie_category_id", validator.Required(), validator.GID(coredata.CookieCategoryEntityType))
return v.Error()
}
func (r *CreateCookieConsentRecordRequest) Validate() error { func (r *CreateCookieConsentRecordRequest) Validate() error {
v := validator.New() v := validator.New()
@@ -373,6 +317,27 @@ func (r *UpsertCookieBannerTranslationRequest) Validate() error {
return v.Error() return v.Error()
} }
func (r *CreateTrackerPatternRequest) Validate() error {
v := validator.New()
v.Check(r.CookieCategoryID, "cookie_category_id", validator.Required(), validator.GID(coredata.CookieCategoryEntityType))
v.Check(r.Pattern, "pattern", validator.Required(), validator.SafeTextNoNewLine(255))
v.Check(string(r.MatchType), "match_type", validator.Required(), validator.OneOfSlice(
func() []string {
types := coredata.TrackerPatternMatchTypes()
s := make([]string, len(types))
for i, t := range types {
s[i] = string(t)
}
return s
}(),
))
v.Check(r.DisplayName, "display_name", validator.Required(), validator.SafeTextNoNewLine(255))
v.Check(r.Description, "description", validator.SafeText(1000))
return v.Error()
}
func CanonicalizeOrigin(raw string) string { func CanonicalizeOrigin(raw string) string {
u, err := url.Parse(raw) u, err := url.Parse(raw)
if err != nil { if err != nil {
@@ -473,7 +438,7 @@ func (s *Service) ensureDraftVersionForBanner(
tx, tx,
scope, scope,
bannerID, bannerID,
coredata.NewCookiePatternFilter(nil, nil, new(false)), coredata.NewTrackerPatternFilter(nil, nil, new(false)),
nil, nil,
); err != nil { ); err != nil {
return nil, fmt.Errorf("cannot load tracker patterns: %w", err) return nil, fmt.Errorf("cannot load tracker patterns: %w", err)
@@ -550,17 +515,18 @@ func (s *Service) CreateCookieBanner(
if dc.Kind == coredata.CookieCategoryKindNecessary { if dc.Kind == coredata.CookieCategoryKindNecessary {
consentMaxAge := req.ConsentExpiryDays * 86400 consentMaxAge := req.ConsentExpiryDays * 86400
consentPattern := &coredata.CookiePattern{ consentPattern := &coredata.TrackerPattern{
ID: gid.New(scope.GetTenantID(), coredata.CookiePatternEntityType), ID: gid.New(scope.GetTenantID(), coredata.TrackerPatternEntityType),
OrganizationID: banner.OrganizationID, OrganizationID: banner.OrganizationID,
CookieBannerID: banner.ID, CookieBannerID: banner.ID,
CookieCategoryID: category.ID, CookieCategoryID: category.ID,
TrackerType: coredata.TrackerTypeCookie,
Pattern: "probo_consent", Pattern: "probo_consent",
MatchType: coredata.CookiePatternMatchTypeExact, MatchType: coredata.TrackerPatternMatchTypeExact,
DisplayName: "probo_consent", DisplayName: "probo_consent",
MaxAgeSeconds: &consentMaxAge, MaxAgeSeconds: &consentMaxAge,
Description: "Stores your cookie consent preferences for this website.", Description: "Stores your cookie consent preferences for this website.",
Source: coredata.CookieSourceScript, Source: new(coredata.CookieSourceScript),
CreatedAt: now, CreatedAt: now,
UpdatedAt: now, UpdatedAt: now,
} }
@@ -1156,442 +1122,6 @@ func (s *Service) CountCookieCategoriesForBanner(
return count, nil return count, nil
} }
func (s *Service) GetCookiePattern(
ctx context.Context,
scope coredata.Scoper,
cookiePatternID gid.GID,
) (*coredata.CookiePattern, error) {
var pattern coredata.CookiePattern
err := s.pg.WithConn(
ctx,
func(ctx context.Context, conn pg.Querier) error {
if err := pattern.LoadByID(ctx, conn, scope, cookiePatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound
}
return fmt.Errorf("cannot load cookie pattern: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return &pattern, nil
}
func (s *Service) CreateCookiePattern(
ctx context.Context,
scope coredata.Scoper,
req CreateCookiePatternRequest,
) (*coredata.CookiePattern, error) {
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
var pattern *coredata.CookiePattern
err := s.pg.WithTx(
ctx,
func(ctx context.Context, tx pg.Tx) error {
var category coredata.CookieCategory
if err := category.LoadByID(ctx, tx, scope, req.CookieCategoryID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCategoryNotFound
}
return fmt.Errorf("cannot load cookie category: %w", err)
}
now := time.Now()
pattern = &coredata.CookiePattern{
ID: gid.New(scope.GetTenantID(), coredata.CookiePatternEntityType),
OrganizationID: category.OrganizationID,
CookieBannerID: category.CookieBannerID,
CookieCategoryID: category.ID,
Pattern: req.Pattern,
MatchType: req.MatchType,
DisplayName: req.DisplayName,
MaxAgeSeconds: req.MaxAgeSeconds,
Description: req.Description,
Source: coredata.CookieSourceScript,
CreatedAt: now,
UpdatedAt: now,
}
if err := pattern.Insert(ctx, tx, scope); err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return ErrPatternAlreadyExists
}
return fmt.Errorf("cannot insert cookie pattern: %w", err)
}
tp := &coredata.TrackerPattern{
ID: gid.New(scope.GetTenantID(), coredata.TrackerPatternEntityType),
OrganizationID: category.OrganizationID,
CookieBannerID: category.CookieBannerID,
CookieCategoryID: category.ID,
TrackerType: coredata.TrackerTypeCookie,
Pattern: req.Pattern,
MatchType: req.MatchType,
DisplayName: req.DisplayName,
MaxAgeSeconds: req.MaxAgeSeconds,
Description: req.Description,
Source: new(coredata.CookieSourceScript),
CreatedAt: now,
UpdatedAt: now,
}
if _, err := tp.InsertIfNotExists(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot insert tracker pattern: %w", err)
}
if _, err := s.ensureDraftVersionForBanner(ctx, tx, scope, category.CookieBannerID); err != nil {
return fmt.Errorf("cannot ensure draft version: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return pattern, nil
}
func (s *Service) UpdateCookiePattern(
ctx context.Context,
scope coredata.Scoper,
req UpdateCookiePatternRequest,
) (*coredata.CookiePattern, error) {
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
var pattern coredata.CookiePattern
err := s.pg.WithTx(
ctx,
func(ctx context.Context, tx pg.Tx) error {
if err := pattern.LoadByID(ctx, tx, scope, req.CookiePatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound
}
return fmt.Errorf("cannot load cookie pattern: %w", err)
}
displayNameChanged := req.DisplayName != nil && *req.DisplayName != pattern.DisplayName
maxAgeChanged := req.MaxAgeSeconds != nil && !ptrEqual(*req.MaxAgeSeconds, pattern.MaxAgeSeconds)
descChanged := req.Description != nil && *req.Description != pattern.Description
excludedChanged := req.Excluded != nil && *req.Excluded != pattern.Excluded
if !displayNameChanged && !maxAgeChanged && !descChanged && !excludedChanged {
return nil
}
// A pattern that was excluded and stays excluded is invisible to visitors,
// so any field updates do not affect the published snapshot. We persist
// the row but skip the version bump.
staysExcluded := pattern.Excluded && (req.Excluded == nil || *req.Excluded)
if req.DisplayName != nil {
pattern.DisplayName = *req.DisplayName
}
if req.MaxAgeSeconds != nil {
pattern.MaxAgeSeconds = *req.MaxAgeSeconds
}
if req.Description != nil {
pattern.Description = *req.Description
}
if req.Excluded != nil {
pattern.Excluded = *req.Excluded
}
pattern.UpdatedAt = time.Now()
if err := pattern.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update cookie pattern: %w", err)
}
var tp coredata.TrackerPattern
if err := tp.LoadByBannerIDTypeAndPattern(ctx, tx, scope, pattern.CookieBannerID, coredata.TrackerTypeCookie, pattern.Pattern); err != nil {
if !errors.Is(err, coredata.ErrResourceNotFound) {
return fmt.Errorf("cannot load tracker pattern: %w", err)
}
} else {
if req.DisplayName != nil {
tp.DisplayName = *req.DisplayName
}
if req.MaxAgeSeconds != nil {
tp.MaxAgeSeconds = *req.MaxAgeSeconds
}
if req.Description != nil {
tp.Description = *req.Description
}
if req.Excluded != nil {
tp.Excluded = *req.Excluded
}
tp.UpdatedAt = time.Now()
if err := tp.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update tracker pattern: %w", err)
}
}
if !staysExcluded {
if _, err := s.ensureDraftVersionForBanner(ctx, tx, scope, pattern.CookieBannerID); err != nil {
return fmt.Errorf("cannot ensure draft version: %w", err)
}
}
return nil
},
)
if err != nil {
return nil, err
}
return &pattern, nil
}
func (s *Service) DeleteCookiePattern(
ctx context.Context,
scope coredata.Scoper,
cookiePatternID gid.GID,
) error {
return s.pg.WithTx(
ctx,
func(ctx context.Context, tx pg.Tx) error {
var pattern coredata.CookiePattern
if err := pattern.LoadByID(ctx, tx, scope, cookiePatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound
}
return fmt.Errorf("cannot load cookie pattern: %w", err)
}
wasExcluded := pattern.Excluded
if err := pattern.Delete(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot delete cookie pattern: %w", err)
}
var tp coredata.TrackerPattern
if err := tp.LoadByBannerIDTypeAndPattern(ctx, tx, scope, pattern.CookieBannerID, coredata.TrackerTypeCookie, pattern.Pattern); err != nil {
if !errors.Is(err, coredata.ErrResourceNotFound) {
return fmt.Errorf("cannot load tracker pattern: %w", err)
}
} else {
if err := tp.Delete(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot delete tracker pattern: %w", err)
}
}
if !wasExcluded {
if _, err := s.ensureDraftVersionForBanner(ctx, tx, scope, pattern.CookieBannerID); err != nil {
return fmt.Errorf("cannot ensure draft version: %w", err)
}
}
return nil
},
)
}
func (s *Service) MoveCookiePatternToCategory(
ctx context.Context,
scope coredata.Scoper,
req MoveCookiePatternToCategoryRequest,
) (*MoveCookiePatternToCategoryResult, error) {
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
var result MoveCookiePatternToCategoryResult
err := s.pg.WithTx(
ctx,
func(ctx context.Context, tx pg.Tx) error {
var pattern coredata.CookiePattern
if err := pattern.LoadByID(ctx, tx, scope, req.CookiePatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound
}
return fmt.Errorf("cannot load cookie pattern: %w", err)
}
var target coredata.CookieCategory
if err := target.LoadByID(ctx, tx, scope, req.TargetCookieCategoryID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCategoryNotFound
}
return fmt.Errorf("cannot load target cookie category: %w", err)
}
if pattern.CookieCategoryID == target.ID {
return ErrSamePatternCategoryMove
}
if pattern.CookieBannerID != target.CookieBannerID {
return ErrCategoriesBannerMismatch
}
wasExcluded := pattern.Excluded
pattern.CookieCategoryID = target.ID
pattern.UpdatedAt = time.Now()
if err := pattern.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update cookie pattern: %w", err)
}
var tp coredata.TrackerPattern
if err := tp.LoadByBannerIDTypeAndPattern(ctx, tx, scope, pattern.CookieBannerID, coredata.TrackerTypeCookie, pattern.Pattern); err != nil {
if !errors.Is(err, coredata.ErrResourceNotFound) {
return fmt.Errorf("cannot load tracker pattern: %w", err)
}
} else {
tp.CookieCategoryID = target.ID
tp.UpdatedAt = time.Now()
if err := tp.Update(ctx, tx, scope); err != nil {
return fmt.Errorf("cannot update tracker pattern: %w", err)
}
}
var banner coredata.CookieBanner
if err := banner.LoadByID(ctx, tx, scope, pattern.CookieBannerID); err != nil {
return fmt.Errorf("cannot load cookie banner: %w", err)
}
if !wasExcluded {
if _, err := s.ensureDraftVersionForBanner(ctx, tx, scope, pattern.CookieBannerID); err != nil {
return fmt.Errorf("cannot ensure draft version: %w", err)
}
}
result.CookiePattern = &pattern
result.Banner = &banner
return nil
},
)
if err != nil {
return nil, err
}
return &result, nil
}
func (s *Service) ListCookiePatternsForCategory(
ctx context.Context,
scope coredata.Scoper,
categoryID gid.GID,
cursor *page.Cursor[coredata.CookiePatternOrderField],
) (coredata.CookiePatterns, error) {
var patterns coredata.CookiePatterns
err := s.pg.WithConn(
ctx,
func(ctx context.Context, conn pg.Querier) error {
if err := patterns.LoadByCookieCategoryID(ctx, conn, scope, categoryID, cursor); err != nil {
return fmt.Errorf("cannot list cookie patterns: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return patterns, nil
}
func (s *Service) CountCookiePatternsForCategory(
ctx context.Context,
scope coredata.Scoper,
categoryID gid.GID,
) (int, error) {
var count int
err := s.pg.WithConn(
ctx,
func(ctx context.Context, conn pg.Querier) error {
var patterns coredata.CookiePatterns
var err error
count, err = patterns.CountByCookieCategoryID(ctx, conn, scope, categoryID)
if err != nil {
return fmt.Errorf("cannot count cookie patterns: %w", err)
}
return nil
},
)
if err != nil {
return 0, err
}
return count, nil
}
func (s *Service) ListUncategorisedCookiePatterns(
ctx context.Context,
scope coredata.Scoper,
bannerID gid.GID,
cursor *page.Cursor[coredata.CookiePatternOrderField],
filter *coredata.CookiePatternFilter,
) (coredata.CookiePatterns, error) {
var patterns coredata.CookiePatterns
err := s.pg.WithConn(
ctx,
func(ctx context.Context, conn pg.Querier) error {
if err := patterns.LoadUncategorisedByCookieBannerID(ctx, conn, scope, bannerID, cursor, filter); err != nil {
return fmt.Errorf("cannot list uncategorised cookie patterns: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return patterns, nil
}
func (s *Service) CountUncategorisedCookiePatterns(
ctx context.Context,
scope coredata.Scoper,
bannerID gid.GID,
filter *coredata.CookiePatternFilter,
) (int, error) {
var count int
err := s.pg.WithConn(
ctx,
func(ctx context.Context, conn pg.Querier) error {
var patterns coredata.CookiePatterns
var err error
count, err = patterns.CountUncategorisedByCookieBannerID(ctx, conn, scope, bannerID, filter)
if err != nil {
return fmt.Errorf("cannot count uncategorised cookie patterns: %w", err)
}
return nil
},
)
if err != nil {
return 0, err
}
return count, nil
}
func (s *Service) CountCookiesForPattern( func (s *Service) CountCookiesForPattern(
ctx context.Context, ctx context.Context,
scope coredata.Scoper, scope coredata.Scoper,
@@ -1777,9 +1307,9 @@ func (s *Service) DeleteCookieCategory(
return fmt.Errorf("cannot load uncategorised cookie category: %w", err) return fmt.Errorf("cannot load uncategorised cookie category: %w", err)
} }
var patterns coredata.CookiePatterns var patterns coredata.TrackerPatterns
if err := patterns.MoveToCategoryByCookieCategoryID(ctx, tx, scope, category.ID, uncategorised.ID); err != nil { if err := patterns.MoveToCategoryByCookieCategoryID(ctx, tx, scope, category.ID, uncategorised.ID); err != nil {
return fmt.Errorf("cannot move cookie patterns to uncategorised: %w", err) return fmt.Errorf("cannot move tracker patterns to uncategorised: %w", err)
} }
if err := category.Delete(ctx, tx, scope); err != nil { if err := category.Delete(ctx, tx, scope); err != nil {
@@ -2513,7 +2043,7 @@ func (s *Service) reportDetectedTracker(
CookieCategoryID: uncategorisedID, CookieCategoryID: uncategorisedID,
TrackerType: info.TrackerType, TrackerType: info.TrackerType,
Pattern: info.Identifier, Pattern: info.Identifier,
MatchType: coredata.CookiePatternMatchTypeExact, MatchType: coredata.TrackerPatternMatchTypeExact,
DisplayName: info.Identifier, DisplayName: info.Identifier,
Description: "", Description: "",
MaxAgeSeconds: info.MaxAgeSeconds, MaxAgeSeconds: info.MaxAgeSeconds,
@@ -2558,6 +2088,114 @@ func (s *Service) reportDetectedTracker(
return nil return nil
} }
func (s *Service) CreateTrackerPattern(
ctx context.Context,
scope coredata.Scoper,
req CreateTrackerPatternRequest,
) (*coredata.TrackerPattern, error) {
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("invalid request: %w", err)
}
var pattern *coredata.TrackerPattern
err := s.pg.WithTx(
ctx,
func(ctx context.Context, tx pg.Tx) error {
var category coredata.CookieCategory
if err := category.LoadByID(ctx, tx, scope, req.CookieCategoryID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCategoryNotFound
}
return fmt.Errorf("cannot load cookie category: %w", err)
}
now := time.Now()
pattern = &coredata.TrackerPattern{
ID: gid.New(scope.GetTenantID(), coredata.TrackerPatternEntityType),
OrganizationID: category.OrganizationID,
CookieBannerID: category.CookieBannerID,
CookieCategoryID: category.ID,
TrackerType: req.TrackerType,
Pattern: req.Pattern,
MatchType: req.MatchType,
DisplayName: req.DisplayName,
MaxAgeSeconds: req.MaxAgeSeconds,
Description: req.Description,
Source: req.Source,
CreatedAt: now,
UpdatedAt: now,
}
if err := pattern.Insert(ctx, tx, scope); err != nil {
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
return ErrPatternAlreadyExists
}
return fmt.Errorf("cannot insert tracker pattern: %w", err)
}
if !pattern.Excluded && pattern.TrackerType == coredata.TrackerTypeCookie {
if _, err := s.ensureDraftVersionForBanner(ctx, tx, scope, pattern.CookieBannerID); err != nil {
return fmt.Errorf("cannot ensure draft version: %w", err)
}
}
return nil
},
)
if err != nil {
return nil, err
}
return pattern, nil
}
func (s *Service) ListTrackerPatternsForCategory(
ctx context.Context,
scope coredata.Scoper,
categoryID gid.GID,
cursor *page.Cursor[coredata.TrackerPatternOrderField],
) (coredata.TrackerPatterns, error) {
var patterns coredata.TrackerPatterns
err := s.pg.WithConn(
ctx,
func(ctx context.Context, conn pg.Querier) error {
return patterns.LoadByCookieCategoryID(ctx, conn, scope, categoryID, cursor)
},
)
if err != nil {
return nil, fmt.Errorf("cannot list tracker patterns for category: %w", err)
}
return patterns, nil
}
func (s *Service) CountTrackerPatternsForCategory(
ctx context.Context,
scope coredata.Scoper,
categoryID gid.GID,
) (int, error) {
var count int
err := s.pg.WithConn(
ctx,
func(ctx context.Context, conn pg.Querier) error {
var patterns coredata.TrackerPatterns
var err error
count, err = patterns.CountByCookieCategoryID(ctx, conn, scope, categoryID)
return err
},
)
if err != nil {
return 0, fmt.Errorf("cannot count tracker patterns for category: %w", err)
}
return count, nil
}
func (s *Service) GetTrackerPattern( func (s *Service) GetTrackerPattern(
ctx context.Context, ctx context.Context,
scope coredata.Scoper, scope coredata.Scoper,
@@ -2570,7 +2208,7 @@ func (s *Service) GetTrackerPattern(
func(ctx context.Context, conn pg.Querier) error { func(ctx context.Context, conn pg.Querier) error {
if err := pattern.LoadByID(ctx, conn, scope, trackerPatternID); err != nil { if err := pattern.LoadByID(ctx, conn, scope, trackerPatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) { if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound return ErrTrackerPatternNotFound
} }
return fmt.Errorf("cannot load tracker pattern: %w", err) return fmt.Errorf("cannot load tracker pattern: %w", err)
} }
@@ -2597,7 +2235,7 @@ func (s *Service) UpdateTrackerPattern(
func(ctx context.Context, tx pg.Tx) error { func(ctx context.Context, tx pg.Tx) error {
if err := pattern.LoadByID(ctx, tx, scope, req.TrackerPatternID); err != nil { if err := pattern.LoadByID(ctx, tx, scope, req.TrackerPatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) { if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound return ErrTrackerPatternNotFound
} }
return fmt.Errorf("cannot load tracker pattern: %w", err) return fmt.Errorf("cannot load tracker pattern: %w", err)
} }
@@ -2659,7 +2297,7 @@ func (s *Service) DeleteTrackerPattern(
var pattern coredata.TrackerPattern var pattern coredata.TrackerPattern
if err := pattern.LoadByID(ctx, tx, scope, trackerPatternID); err != nil { if err := pattern.LoadByID(ctx, tx, scope, trackerPatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) { if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound return ErrTrackerPatternNotFound
} }
return fmt.Errorf("cannot load tracker pattern: %w", err) return fmt.Errorf("cannot load tracker pattern: %w", err)
} }
@@ -2694,7 +2332,7 @@ func (s *Service) MoveTrackerPatternToCategory(
var pattern coredata.TrackerPattern var pattern coredata.TrackerPattern
if err := pattern.LoadByID(ctx, tx, scope, req.TrackerPatternID); err != nil { if err := pattern.LoadByID(ctx, tx, scope, req.TrackerPatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) { if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound return ErrTrackerPatternNotFound
} }
return fmt.Errorf("cannot load tracker pattern: %w", err) return fmt.Errorf("cannot load tracker pattern: %w", err)
} }
@@ -2752,8 +2390,8 @@ func (s *Service) ListUncategorisedTrackerPatterns(
ctx context.Context, ctx context.Context,
scope coredata.Scoper, scope coredata.Scoper,
bannerID gid.GID, bannerID gid.GID,
cursor *page.Cursor[coredata.CookiePatternOrderField], cursor *page.Cursor[coredata.TrackerPatternOrderField],
filter *coredata.CookiePatternFilter, filter *coredata.TrackerPatternFilter,
) (coredata.TrackerPatterns, error) { ) (coredata.TrackerPatterns, error) {
var patterns coredata.TrackerPatterns var patterns coredata.TrackerPatterns
@@ -2778,7 +2416,7 @@ func (s *Service) CountUncategorisedTrackerPatterns(
ctx context.Context, ctx context.Context,
scope coredata.Scoper, scope coredata.Scoper,
bannerID gid.GID, bannerID gid.GID,
filter *coredata.CookiePatternFilter, filter *coredata.TrackerPatternFilter,
) (int, error) { ) (int, error) {
var count int var count int

View File

@@ -100,7 +100,7 @@ func (h *patternAnalysisHandler) Process(ctx context.Context, banner coredata.Co
tx, tx,
scope, scope,
banner.ID, banner.ID,
coredata.NewCookiePatternFilter(new(coredata.CookiePatternMatchTypeExact), nil, new(false)), coredata.NewTrackerPatternFilter(new(coredata.TrackerPatternMatchTypeExact), nil, new(false)),
nil, nil,
); err != nil { ); err != nil {
return fmt.Errorf("cannot load exact patterns: %w", err) return fmt.Errorf("cannot load exact patterns: %w", err)
@@ -121,7 +121,7 @@ func (h *patternAnalysisHandler) Process(ctx context.Context, banner coredata.Co
CookieCategoryID: key.categoryID, CookieCategoryID: key.categoryID,
TrackerType: key.trackerType, TrackerType: key.trackerType,
Pattern: key.prefix, Pattern: key.prefix,
MatchType: coredata.CookiePatternMatchTypePrefix, MatchType: coredata.TrackerPatternMatchTypePrefix,
DisplayName: key.prefix + "*", DisplayName: key.prefix + "*",
MaxAgeSeconds: maxAge, MaxAgeSeconds: maxAge,
Description: "", Description: "",
@@ -139,7 +139,7 @@ func (h *patternAnalysisHandler) Process(ctx context.Context, banner coredata.Co
return fmt.Errorf("cannot load existing prefix pattern %q: %w", key.prefix, err) return fmt.Errorf("cannot load existing prefix pattern %q: %w", key.prefix, err)
} }
if prefixPattern.CookieCategoryID != key.categoryID || prefixPattern.MatchType != coredata.CookiePatternMatchTypePrefix { if prefixPattern.CookieCategoryID != key.categoryID || prefixPattern.MatchType != coredata.TrackerPatternMatchTypePrefix {
continue continue
} }
} }
@@ -319,7 +319,7 @@ func (h *patternAnalysisHandler) adoptUncategorisedPatterns(
tx, tx,
scope, scope,
banner.ID, banner.ID,
coredata.NewCookiePatternFilter(new(coredata.CookiePatternMatchTypePrefix), nil, new(false)), coredata.NewTrackerPatternFilter(new(coredata.TrackerPatternMatchTypePrefix), nil, new(false)),
nil, nil,
); err != nil { ); err != nil {
return false, fmt.Errorf("cannot load prefix patterns: %w", err) return false, fmt.Errorf("cannot load prefix patterns: %w", err)
@@ -333,14 +333,14 @@ func (h *patternAnalysisHandler) adoptUncategorisedPatterns(
return len(prefixPatterns[i].Pattern) > len(prefixPatterns[j].Pattern) return len(prefixPatterns[i].Pattern) > len(prefixPatterns[j].Pattern)
}) })
exactMatchType := coredata.CookiePatternMatchTypeExact exactMatchType := coredata.TrackerPatternMatchTypeExact
var uncategorisedExact coredata.TrackerPatterns var uncategorisedExact coredata.TrackerPatterns
if err := uncategorisedExact.LoadAllByCookieBannerID( if err := uncategorisedExact.LoadAllByCookieBannerID(
ctx, ctx,
tx, tx,
scope, scope,
banner.ID, banner.ID,
coredata.NewCookiePatternFilter(&exactMatchType, &uncategorised.ID, new(false)), coredata.NewTrackerPatternFilter(&exactMatchType, &uncategorised.ID, new(false)),
nil, nil,
); err != nil { ); err != nil {
return false, fmt.Errorf("cannot load uncategorised exact patterns: %w", err) return false, fmt.Errorf("cannot load uncategorised exact patterns: %w", err)

View File

@@ -92,7 +92,7 @@ func TestFindMergeGroups(t *testing.T) {
return &coredata.TrackerPattern{ return &coredata.TrackerPattern{
Pattern: name, Pattern: name,
TrackerType: coredata.TrackerTypeCookie, TrackerType: coredata.TrackerTypeCookie,
MatchType: coredata.CookiePatternMatchTypeExact, MatchType: coredata.TrackerPatternMatchTypeExact,
} }
} }

View File

@@ -1,794 +0,0 @@
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package coredata
import (
"context"
"errors"
"fmt"
"maps"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
CookiePattern struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
CookieBannerID gid.GID `db:"cookie_banner_id"`
CookieCategoryID gid.GID `db:"cookie_category_id"`
Pattern string `db:"pattern"`
MatchType CookiePatternMatchType `db:"match_type"`
DisplayName string `db:"display_name"`
MaxAgeSeconds *int `db:"max_age_seconds"`
Description string `db:"description"`
Source CookieSource `db:"source"`
Excluded bool `db:"excluded"`
LastMatchedAt *time.Time `db:"last_matched_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
CookiePatterns []*CookiePattern
)
func (cp *CookiePattern) CursorKey(field CookiePatternOrderField) page.CursorKey {
switch field {
case CookiePatternOrderFieldCreatedAt:
return page.NewCursorKey(cp.ID, cp.CreatedAt)
case CookiePatternOrderFieldName:
return page.NewCursorKey(cp.ID, cp.DisplayName)
case CookiePatternOrderFieldLastMatchedAt:
if cp.LastMatchedAt == nil {
return page.NewCursorKey(cp.ID, time.Time{})
}
return page.NewCursorKey(cp.ID, *cp.LastMatchedAt)
case CookiePatternOrderFieldUpdatedAt:
return page.NewCursorKey(cp.ID, cp.UpdatedAt)
case CookiePatternOrderFieldSource:
return page.NewCursorKey(cp.ID, string(cp.Source))
}
panic(fmt.Sprintf("unsupported order by: %s", field))
}
func (cp *CookiePattern) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) {
q := `SELECT organization_id FROM cookie_patterns WHERE id = $1 LIMIT 1;`
var organizationID gid.GID
if err := conn.QueryRow(ctx, q, cp.ID).Scan(&organizationID); err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrResourceNotFound
}
return nil, fmt.Errorf("cannot query cookie pattern authorization attributes: %w", err)
}
return map[string]string{"organization_id": organizationID.String()}, nil
}
func (cp *CookiePattern) LoadByID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookiePatternID gid.GID,
) error {
q := `
SELECT
id,
organization_id,
cookie_banner_id,
cookie_category_id,
pattern,
match_type,
display_name,
max_age_seconds,
description,
source,
excluded,
last_matched_at,
created_at,
updated_at
FROM
cookie_patterns
WHERE
%s
AND id = @cookie_pattern_id
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"cookie_pattern_id": cookiePatternID}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query cookie patterns: %w", err)
}
pattern, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CookiePattern])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrResourceNotFound
}
return fmt.Errorf("cannot collect cookie pattern: %w", err)
}
*cp = pattern
return nil
}
func (cp *CookiePattern) LoadByBannerIDAndPattern(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookieBannerID gid.GID,
pattern string,
) error {
q := `
SELECT
id,
organization_id,
cookie_banner_id,
cookie_category_id,
pattern,
match_type,
display_name,
max_age_seconds,
description,
source,
excluded,
last_matched_at,
created_at,
updated_at
FROM
cookie_patterns
WHERE
%s
AND cookie_banner_id = @cookie_banner_id
AND pattern = @pattern
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"cookie_banner_id": cookieBannerID,
"pattern": pattern,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query cookie patterns: %w", err)
}
p, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CookiePattern])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrResourceNotFound
}
return fmt.Errorf("cannot collect cookie pattern: %w", err)
}
*cp = p
return nil
}
func (cp *CookiePattern) FindMatchingPattern(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookieBannerID gid.GID,
cookieName string,
) error {
q := `
SELECT
id,
organization_id,
cookie_banner_id,
cookie_category_id,
pattern,
match_type,
display_name,
max_age_seconds,
description,
source,
excluded,
last_matched_at,
created_at,
updated_at
FROM
cookie_patterns
WHERE
%s
AND cookie_banner_id = @cookie_banner_id
AND (
(match_type = @match_type_prefix AND starts_with(@cookie_name, pattern))
OR (match_type = @match_type_exact AND pattern = @cookie_name)
)
ORDER BY
CASE WHEN match_type = @match_type_exact AND pattern = @cookie_name THEN 0
WHEN match_type = @match_type_prefix THEN 1
ELSE 2
END,
LENGTH(pattern) DESC
LIMIT 1;
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"cookie_banner_id": cookieBannerID,
"cookie_name": cookieName,
"match_type_prefix": CookiePatternMatchTypePrefix,
"match_type_exact": CookiePatternMatchTypeExact,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query cookie patterns: %w", err)
}
pattern, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[CookiePattern])
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return ErrResourceNotFound
}
return fmt.Errorf("cannot collect cookie pattern: %w", err)
}
*cp = pattern
return nil
}
func (cps *CookiePatterns) LoadByCookieCategoryID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookieCategoryID gid.GID,
cursor *page.Cursor[CookiePatternOrderField],
) error {
q := `
SELECT
id,
organization_id,
cookie_banner_id,
cookie_category_id,
pattern,
match_type,
display_name,
max_age_seconds,
description,
source,
excluded,
last_matched_at,
created_at,
updated_at
FROM
cookie_patterns
WHERE
%s
AND cookie_category_id = @cookie_category_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"cookie_category_id": cookieCategoryID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query cookie patterns: %w", err)
}
patterns, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[CookiePattern])
if err != nil {
return fmt.Errorf("cannot collect cookie patterns: %w", err)
}
*cps = patterns
return nil
}
func (cps *CookiePatterns) CountByCookieCategoryID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookieCategoryID gid.GID,
) (int, error) {
q := `
SELECT
COUNT(id)
FROM
cookie_patterns
WHERE
%s
AND cookie_category_id = @cookie_category_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"cookie_category_id": cookieCategoryID}
maps.Copy(args, scope.SQLArguments())
row := conn.QueryRow(ctx, q, args)
var count int
if err := row.Scan(&count); err != nil {
return 0, fmt.Errorf("cannot scan count: %w", err)
}
return count, nil
}
func (cps *CookiePatterns) LoadAllByCookieBannerID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookieBannerID gid.GID,
filter *CookiePatternFilter,
) error {
q := `
SELECT
id,
organization_id,
cookie_banner_id,
cookie_category_id,
pattern,
match_type,
display_name,
max_age_seconds,
description,
source,
excluded,
last_matched_at,
created_at,
updated_at
FROM
cookie_patterns
WHERE
%s
AND cookie_banner_id = @cookie_banner_id
AND %s
ORDER BY
created_at ASC, id ASC;
`
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
args := pgx.StrictNamedArgs{"cookie_banner_id": cookieBannerID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query cookie patterns: %w", err)
}
patterns, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[CookiePattern])
if err != nil {
return fmt.Errorf("cannot collect cookie patterns: %w", err)
}
*cps = patterns
return nil
}
func (cp *CookiePattern) Insert(
ctx context.Context,
tx pg.Tx,
scope Scoper,
) error {
q := `
INSERT INTO cookie_patterns (
id,
tenant_id,
organization_id,
cookie_banner_id,
cookie_category_id,
pattern,
match_type,
display_name,
max_age_seconds,
description,
source,
excluded,
last_matched_at,
created_at,
updated_at
) VALUES (
@id,
@tenant_id,
@organization_id,
@cookie_banner_id,
@cookie_category_id,
@pattern,
@match_type,
@display_name,
@max_age_seconds,
@description,
@source,
@excluded,
@last_matched_at,
@created_at,
@updated_at
)
`
args := pgx.StrictNamedArgs{
"id": cp.ID,
"tenant_id": scope.GetTenantID(),
"organization_id": cp.OrganizationID,
"cookie_banner_id": cp.CookieBannerID,
"cookie_category_id": cp.CookieCategoryID,
"pattern": cp.Pattern,
"match_type": cp.MatchType,
"display_name": cp.DisplayName,
"max_age_seconds": cp.MaxAgeSeconds,
"description": cp.Description,
"source": cp.Source,
"excluded": cp.Excluded,
"last_matched_at": cp.LastMatchedAt,
"created_at": cp.CreatedAt,
"updated_at": cp.UpdatedAt,
}
_, err := tx.Exec(ctx, q, args)
if err != nil {
if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok {
if pgErr.Code == "23505" && pgErr.ConstraintName == "idx_cookie_patterns_unique_pattern_per_banner" {
return ErrResourceAlreadyExists
}
}
return fmt.Errorf("cannot insert cookie pattern: %w", err)
}
return nil
}
func (cp *CookiePattern) InsertIfNotExists(
ctx context.Context,
tx pg.Tx,
scope Scoper,
) (bool, error) {
q := `
INSERT INTO cookie_patterns (
id,
tenant_id,
organization_id,
cookie_banner_id,
cookie_category_id,
pattern,
match_type,
display_name,
max_age_seconds,
description,
source,
excluded,
last_matched_at,
created_at,
updated_at
) VALUES (
@id,
@tenant_id,
@organization_id,
@cookie_banner_id,
@cookie_category_id,
@pattern,
@match_type,
@display_name,
@max_age_seconds,
@description,
@source,
@excluded,
@last_matched_at,
@created_at,
@updated_at
)
ON CONFLICT (cookie_banner_id, pattern) DO NOTHING
`
args := pgx.StrictNamedArgs{
"id": cp.ID,
"tenant_id": scope.GetTenantID(),
"organization_id": cp.OrganizationID,
"cookie_banner_id": cp.CookieBannerID,
"cookie_category_id": cp.CookieCategoryID,
"pattern": cp.Pattern,
"match_type": cp.MatchType,
"display_name": cp.DisplayName,
"max_age_seconds": cp.MaxAgeSeconds,
"description": cp.Description,
"source": cp.Source,
"excluded": cp.Excluded,
"last_matched_at": cp.LastMatchedAt,
"created_at": cp.CreatedAt,
"updated_at": cp.UpdatedAt,
}
result, err := tx.Exec(ctx, q, args)
if err != nil {
return false, fmt.Errorf("cannot insert cookie pattern: %w", err)
}
return result.RowsAffected() > 0, nil
}
func (cp *CookiePattern) Update(
ctx context.Context,
tx pg.Tx,
scope Scoper,
) error {
q := `
UPDATE cookie_patterns
SET
cookie_category_id = @cookie_category_id,
display_name = @display_name,
max_age_seconds = @max_age_seconds,
description = @description,
excluded = @excluded,
updated_at = @updated_at
WHERE
%s
AND id = @id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"id": cp.ID,
"cookie_category_id": cp.CookieCategoryID,
"display_name": cp.DisplayName,
"max_age_seconds": cp.MaxAgeSeconds,
"description": cp.Description,
"excluded": cp.Excluded,
"updated_at": cp.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
result, err := tx.Exec(ctx, q, args)
if err != nil {
if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok {
if pgErr.Code == "23505" && pgErr.ConstraintName == "idx_cookie_patterns_unique_pattern_per_banner" {
return ErrResourceAlreadyExists
}
}
return fmt.Errorf("cannot update cookie pattern: %w", err)
}
if result.RowsAffected() == 0 {
return ErrResourceNotFound
}
return nil
}
func (cp *CookiePattern) Delete(
ctx context.Context,
tx pg.Tx,
scope Scoper,
) error {
q := `
DELETE FROM cookie_patterns
WHERE
%s
AND id = @id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"id": cp.ID}
maps.Copy(args, scope.SQLArguments())
_, err := tx.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot delete cookie pattern: %w", err)
}
return nil
}
func (cps *CookiePatterns) RefreshLastMatchedAtByCookieBannerID(
ctx context.Context,
tx pg.Tx,
scope Scoper,
cookieBannerID gid.GID,
) error {
q := `
UPDATE cookie_patterns
SET
last_matched_at = sub.max_detected
FROM (
SELECT cookie_pattern_id, MAX(last_detected_at) AS max_detected
FROM cookies
WHERE %[1]s AND cookie_banner_id = @cookie_banner_id
GROUP BY cookie_pattern_id
) sub
WHERE
cookie_patterns.id = sub.cookie_pattern_id
AND %[1]s
AND cookie_patterns.cookie_banner_id = @cookie_banner_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"cookie_banner_id": cookieBannerID}
maps.Copy(args, scope.SQLArguments())
_, err := tx.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot refresh last_matched_at for banner patterns: %w", err)
}
return nil
}
func (cps *CookiePatterns) LoadUncategorisedByCookieBannerID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookieBannerID gid.GID,
cursor *page.Cursor[CookiePatternOrderField],
filter *CookiePatternFilter,
) error {
q := `
SELECT
id,
organization_id,
cookie_banner_id,
cookie_category_id,
pattern,
match_type,
display_name,
max_age_seconds,
description,
source,
excluded,
last_matched_at,
created_at,
updated_at
FROM
cookie_patterns
WHERE
%s
AND cookie_banner_id = @cookie_banner_id
AND cookie_category_id = (
SELECT id FROM cookie_categories
WHERE cookie_banner_id = @cookie_banner_id
AND kind = @category_kind
AND %s
LIMIT 1
)
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{
"cookie_banner_id": cookieBannerID,
"category_kind": CookieCategoryKindUncategorised,
}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query uncategorised cookie patterns: %w", err)
}
patterns, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[CookiePattern])
if err != nil {
return fmt.Errorf("cannot collect uncategorised cookie patterns: %w", err)
}
*cps = patterns
return nil
}
func (cps *CookiePatterns) CountUncategorisedByCookieBannerID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookieBannerID gid.GID,
filter *CookiePatternFilter,
) (int, error) {
q := `
SELECT
COUNT(id)
FROM
cookie_patterns
WHERE
%s
AND cookie_banner_id = @cookie_banner_id
AND cookie_category_id = (
SELECT id FROM cookie_categories
WHERE cookie_banner_id = @cookie_banner_id
AND kind = @category_kind
AND %s
LIMIT 1
)
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), scope.SQLFragment(), filter.SQLFragment())
args := pgx.StrictNamedArgs{
"cookie_banner_id": cookieBannerID,
"category_kind": CookieCategoryKindUncategorised,
}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
row := conn.QueryRow(ctx, q, args)
var count int
if err := row.Scan(&count); err != nil {
return 0, fmt.Errorf("cannot scan count: %w", err)
}
return count, nil
}
func (cps *CookiePatterns) MoveToCategoryByCookieCategoryID(
ctx context.Context,
tx pg.Tx,
scope Scoper,
sourceCategoryID gid.GID,
targetCategoryID gid.GID,
) error {
q := `
UPDATE cookie_patterns
SET
cookie_category_id = @target_category_id,
updated_at = @updated_at
WHERE
%s
AND cookie_category_id = @source_category_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"source_category_id": sourceCategoryID,
"target_category_id": targetCategoryID,
"updated_at": time.Now(),
}
maps.Copy(args, scope.SQLArguments())
_, err := tx.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot move cookie patterns to category: %w", err)
}
return nil
}

View File

@@ -1,71 +0,0 @@
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package coredata
import "fmt"
type CookiePatternOrderField string
const (
CookiePatternOrderFieldCreatedAt CookiePatternOrderField = "CREATED_AT"
CookiePatternOrderFieldName CookiePatternOrderField = "NAME"
CookiePatternOrderFieldLastMatchedAt CookiePatternOrderField = "LAST_MATCHED_AT"
CookiePatternOrderFieldUpdatedAt CookiePatternOrderField = "UPDATED_AT"
CookiePatternOrderFieldSource CookiePatternOrderField = "SOURCE"
)
func (p CookiePatternOrderField) Column() string {
switch p {
case CookiePatternOrderFieldCreatedAt:
return "created_at"
case CookiePatternOrderFieldName:
return "display_name"
case CookiePatternOrderFieldLastMatchedAt:
return "COALESCE(last_matched_at, '0001-01-01T00:00:00Z'::timestamptz)"
case CookiePatternOrderFieldUpdatedAt:
return "updated_at"
case CookiePatternOrderFieldSource:
return "source"
}
panic(fmt.Sprintf("unsupported order by: %s", p))
}
func (p CookiePatternOrderField) IsValid() bool {
switch p {
case CookiePatternOrderFieldCreatedAt,
CookiePatternOrderFieldName,
CookiePatternOrderFieldLastMatchedAt,
CookiePatternOrderFieldUpdatedAt,
CookiePatternOrderFieldSource:
return true
}
return false
}
func (p CookiePatternOrderField) String() string {
return string(p)
}
func (p *CookiePatternOrderField) UnmarshalText(text []byte) error {
*p = CookiePatternOrderField(text)
if !p.IsValid() {
return fmt.Errorf("%s is not a valid CookiePatternOrderField", string(text))
}
return nil
}
func (p CookiePatternOrderField) MarshalText() ([]byte, error) {
return []byte(p.String()), nil
}

View File

@@ -111,7 +111,7 @@ const (
CookieEntityType uint16 = 85 CookieEntityType uint16 = 85
CookieBannerTranslationEntityType uint16 = 86 CookieBannerTranslationEntityType uint16 = 86
AgentRunEntityType uint16 = 87 AgentRunEntityType uint16 = 87
CookiePatternEntityType uint16 = 88 _ uint16 = 88 // CookiePatternEntityType - removed
TrackerPatternEntityType uint16 = 89 TrackerPatternEntityType uint16 = 89
DetectedTrackerEntityType uint16 = 90 DetectedTrackerEntityType uint16 = 90
) )
@@ -282,8 +282,6 @@ func NewEntityFromID(id gid.GID) (any, bool) {
return &CookieBannerTranslation{ID: id}, true return &CookieBannerTranslation{ID: id}, true
case AgentRunEntityType: case AgentRunEntityType:
return &AgentRun{ID: id}, true return &AgentRun{ID: id}, true
case CookiePatternEntityType:
return &CookiePattern{ID: id}, true
case TrackerPatternEntityType: case TrackerPatternEntityType:
return &TrackerPattern{ID: id}, true return &TrackerPattern{ID: id}, true
case DetectedTrackerEntityType: case DetectedTrackerEntityType:

View File

@@ -36,7 +36,7 @@ type (
CookieCategoryID gid.GID `db:"cookie_category_id"` CookieCategoryID gid.GID `db:"cookie_category_id"`
TrackerType TrackerType `db:"tracker_type"` TrackerType TrackerType `db:"tracker_type"`
Pattern string `db:"pattern"` Pattern string `db:"pattern"`
MatchType CookiePatternMatchType `db:"match_type"` MatchType TrackerPatternMatchType `db:"match_type"`
DisplayName string `db:"display_name"` DisplayName string `db:"display_name"`
Description string `db:"description"` Description string `db:"description"`
Excluded bool `db:"excluded"` Excluded bool `db:"excluded"`
@@ -50,20 +50,20 @@ type (
TrackerPatterns []*TrackerPattern TrackerPatterns []*TrackerPattern
) )
func (tp *TrackerPattern) CursorKey(field CookiePatternOrderField) page.CursorKey { func (tp *TrackerPattern) CursorKey(field TrackerPatternOrderField) page.CursorKey {
switch field { switch field {
case CookiePatternOrderFieldCreatedAt: case TrackerPatternOrderFieldCreatedAt:
return page.NewCursorKey(tp.ID, tp.CreatedAt) return page.NewCursorKey(tp.ID, tp.CreatedAt)
case CookiePatternOrderFieldName: case TrackerPatternOrderFieldName:
return page.NewCursorKey(tp.ID, tp.DisplayName) return page.NewCursorKey(tp.ID, tp.DisplayName)
case CookiePatternOrderFieldLastMatchedAt: case TrackerPatternOrderFieldLastMatchedAt:
if tp.LastMatchedAt == nil { if tp.LastMatchedAt == nil {
return page.NewCursorKey(tp.ID, time.Time{}) return page.NewCursorKey(tp.ID, time.Time{})
} }
return page.NewCursorKey(tp.ID, *tp.LastMatchedAt) return page.NewCursorKey(tp.ID, *tp.LastMatchedAt)
case CookiePatternOrderFieldUpdatedAt: case TrackerPatternOrderFieldUpdatedAt:
return page.NewCursorKey(tp.ID, tp.UpdatedAt) return page.NewCursorKey(tp.ID, tp.UpdatedAt)
case CookiePatternOrderFieldSource: case TrackerPatternOrderFieldSource:
if tp.Source == nil { if tp.Source == nil {
return page.NewCursorKey(tp.ID, "") return page.NewCursorKey(tp.ID, "")
} }
@@ -254,8 +254,8 @@ LIMIT 1;
"cookie_banner_id": cookieBannerID, "cookie_banner_id": cookieBannerID,
"tracker_type": trackerType, "tracker_type": trackerType,
"identifier": identifier, "identifier": identifier,
"match_type_prefix": CookiePatternMatchTypePrefix, "match_type_prefix": TrackerPatternMatchTypePrefix,
"match_type_exact": CookiePatternMatchTypeExact, "match_type_exact": TrackerPatternMatchTypeExact,
} }
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
@@ -504,7 +504,7 @@ func (tps *TrackerPatterns) LoadAllByCookieBannerID(
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
cookieBannerID gid.GID, cookieBannerID gid.GID,
filter *CookiePatternFilter, filter *TrackerPatternFilter,
trackerType *TrackerType, trackerType *TrackerType,
) error { ) error {
trackerTypeFragment := "TRUE" trackerTypeFragment := "TRUE"
@@ -605,8 +605,8 @@ func (tps *TrackerPatterns) LoadUncategorisedByCookieBannerID(
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
cookieBannerID gid.GID, cookieBannerID gid.GID,
cursor *page.Cursor[CookiePatternOrderField], cursor *page.Cursor[TrackerPatternOrderField],
filter *CookiePatternFilter, filter *TrackerPatternFilter,
) error { ) error {
q := ` q := `
SELECT SELECT
@@ -671,7 +671,7 @@ func (tps *TrackerPatterns) CountUncategorisedByCookieBannerID(
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
cookieBannerID gid.GID, cookieBannerID gid.GID,
filter *CookiePatternFilter, filter *TrackerPatternFilter,
) (int, error) { ) (int, error) {
q := ` q := `
SELECT SELECT
@@ -709,3 +709,120 @@ WHERE
return count, nil return count, nil
} }
func (tps *TrackerPatterns) LoadByCookieCategoryID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookieCategoryID gid.GID,
cursor *page.Cursor[TrackerPatternOrderField],
) error {
q := `
SELECT
id,
organization_id,
cookie_banner_id,
cookie_category_id,
tracker_type,
pattern,
match_type,
display_name,
description,
excluded,
max_age_seconds,
source,
last_matched_at,
created_at,
updated_at
FROM
tracker_patterns
WHERE
%s
AND cookie_category_id = @cookie_category_id
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.StrictNamedArgs{"cookie_category_id": cookieCategoryID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query tracker patterns: %w", err)
}
patterns, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[TrackerPattern])
if err != nil {
return fmt.Errorf("cannot collect tracker patterns: %w", err)
}
*tps = patterns
return nil
}
func (tps *TrackerPatterns) CountByCookieCategoryID(
ctx context.Context,
conn pg.Querier,
scope Scoper,
cookieCategoryID gid.GID,
) (int, error) {
q := `
SELECT
COUNT(id)
FROM
tracker_patterns
WHERE
%s
AND cookie_category_id = @cookie_category_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"cookie_category_id": cookieCategoryID}
maps.Copy(args, scope.SQLArguments())
row := conn.QueryRow(ctx, q, args)
var count int
if err := row.Scan(&count); err != nil {
return 0, fmt.Errorf("cannot scan count: %w", err)
}
return count, nil
}
func (tps *TrackerPatterns) MoveToCategoryByCookieCategoryID(
ctx context.Context,
tx pg.Tx,
scope Scoper,
sourceCategoryID gid.GID,
targetCategoryID gid.GID,
) error {
q := `
UPDATE tracker_patterns
SET
cookie_category_id = @target_category_id,
updated_at = @updated_at
WHERE
%s
AND cookie_category_id = @source_category_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"source_category_id": sourceCategoryID,
"target_category_id": targetCategoryID,
"updated_at": time.Now(),
}
maps.Copy(args, scope.SQLArguments())
_, err := tx.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot move tracker patterns to category: %w", err)
}
return nil
}

View File

@@ -19,37 +19,37 @@ import (
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
) )
type CookiePatternFilter struct { type TrackerPatternFilter struct {
matchType *CookiePatternMatchType matchType *TrackerPatternMatchType
cookieCategoryID *gid.GID cookieCategoryID *gid.GID
excluded *bool excluded *bool
query *string query *string
source *CookieSource source *CookieSource
} }
func NewCookiePatternFilter( func NewTrackerPatternFilter(
matchType *CookiePatternMatchType, matchType *TrackerPatternMatchType,
cookieCategoryID *gid.GID, cookieCategoryID *gid.GID,
excluded *bool, excluded *bool,
) *CookiePatternFilter { ) *TrackerPatternFilter {
return &CookiePatternFilter{ return &TrackerPatternFilter{
matchType: matchType, matchType: matchType,
cookieCategoryID: cookieCategoryID, cookieCategoryID: cookieCategoryID,
excluded: excluded, excluded: excluded,
} }
} }
func (f *CookiePatternFilter) WithQuery(query *string) *CookiePatternFilter { func (f *TrackerPatternFilter) WithQuery(query *string) *TrackerPatternFilter {
f.query = query f.query = query
return f return f
} }
func (f *CookiePatternFilter) WithSource(source *CookieSource) *CookiePatternFilter { func (f *TrackerPatternFilter) WithSource(source *CookieSource) *TrackerPatternFilter {
f.source = source f.source = source
return f return f
} }
func (f *CookiePatternFilter) SQLFragment() string { func (f *TrackerPatternFilter) SQLFragment() string {
if f == nil { if f == nil {
return "TRUE" return "TRUE"
} }
@@ -93,7 +93,7 @@ func (f *CookiePatternFilter) SQLFragment() string {
)` )`
} }
func (f *CookiePatternFilter) SQLArguments() pgx.StrictNamedArgs { func (f *TrackerPatternFilter) SQLArguments() pgx.StrictNamedArgs {
if f == nil { if f == nil {
return pgx.StrictNamedArgs{} return pgx.StrictNamedArgs{}
} }

View File

@@ -19,25 +19,25 @@ import (
"fmt" "fmt"
) )
type CookiePatternMatchType string type TrackerPatternMatchType string
const ( const (
CookiePatternMatchTypeExact CookiePatternMatchType = "EXACT" TrackerPatternMatchTypeExact TrackerPatternMatchType = "EXACT"
CookiePatternMatchTypePrefix CookiePatternMatchType = "PREFIX" TrackerPatternMatchTypePrefix TrackerPatternMatchType = "PREFIX"
) )
func CookiePatternMatchTypes() []CookiePatternMatchType { func TrackerPatternMatchTypes() []TrackerPatternMatchType {
return []CookiePatternMatchType{ return []TrackerPatternMatchType{
CookiePatternMatchTypeExact, TrackerPatternMatchTypeExact,
CookiePatternMatchTypePrefix, TrackerPatternMatchTypePrefix,
} }
} }
func (m CookiePatternMatchType) String() string { func (m TrackerPatternMatchType) String() string {
return string(m) return string(m)
} }
func (m *CookiePatternMatchType) Scan(value any) error { func (m *TrackerPatternMatchType) Scan(value any) error {
var v string var v string
switch val := value.(type) { switch val := value.(type) {
case string: case string:
@@ -45,26 +45,26 @@ func (m *CookiePatternMatchType) Scan(value any) error {
case []byte: case []byte:
v = string(val) v = string(val)
default: default:
return fmt.Errorf("unsupported type for CookiePatternMatchType: %T", value) return fmt.Errorf("unsupported type for TrackerPatternMatchType: %T", value)
} }
switch CookiePatternMatchType(v) { switch TrackerPatternMatchType(v) {
case CookiePatternMatchTypeExact: case TrackerPatternMatchTypeExact:
*m = CookiePatternMatchTypeExact *m = TrackerPatternMatchTypeExact
case CookiePatternMatchTypePrefix: case TrackerPatternMatchTypePrefix:
*m = CookiePatternMatchTypePrefix *m = TrackerPatternMatchTypePrefix
default: default:
return fmt.Errorf("invalid CookiePatternMatchType value: %q", v) return fmt.Errorf("invalid TrackerPatternMatchType value: %q", v)
} }
return nil return nil
} }
func (m CookiePatternMatchType) Value() (driver.Value, error) { func (m TrackerPatternMatchType) Value() (driver.Value, error) {
switch m { switch m {
case CookiePatternMatchTypeExact, case TrackerPatternMatchTypeExact,
CookiePatternMatchTypePrefix: TrackerPatternMatchTypePrefix:
return string(m), nil return string(m), nil
default: default:
return nil, fmt.Errorf("invalid CookiePatternMatchType: %s", m) return nil, fmt.Errorf("invalid TrackerPatternMatchType: %s", m)
} }
} }

View File

@@ -0,0 +1,71 @@
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package coredata
import "fmt"
type TrackerPatternOrderField string
const (
TrackerPatternOrderFieldCreatedAt TrackerPatternOrderField = "CREATED_AT"
TrackerPatternOrderFieldName TrackerPatternOrderField = "NAME"
TrackerPatternOrderFieldLastMatchedAt TrackerPatternOrderField = "LAST_MATCHED_AT"
TrackerPatternOrderFieldUpdatedAt TrackerPatternOrderField = "UPDATED_AT"
TrackerPatternOrderFieldSource TrackerPatternOrderField = "SOURCE"
)
func (p TrackerPatternOrderField) Column() string {
switch p {
case TrackerPatternOrderFieldCreatedAt:
return "created_at"
case TrackerPatternOrderFieldName:
return "display_name"
case TrackerPatternOrderFieldLastMatchedAt:
return "COALESCE(last_matched_at, '0001-01-01T00:00:00Z'::timestamptz)"
case TrackerPatternOrderFieldUpdatedAt:
return "updated_at"
case TrackerPatternOrderFieldSource:
return "source"
}
panic(fmt.Sprintf("unsupported order by: %s", p))
}
func (p TrackerPatternOrderField) IsValid() bool {
switch p {
case TrackerPatternOrderFieldCreatedAt,
TrackerPatternOrderFieldName,
TrackerPatternOrderFieldLastMatchedAt,
TrackerPatternOrderFieldUpdatedAt,
TrackerPatternOrderFieldSource:
return true
}
return false
}
func (p TrackerPatternOrderField) String() string {
return string(p)
}
func (p *TrackerPatternOrderField) UnmarshalText(text []byte) error {
*p = TrackerPatternOrderField(text)
if !p.IsValid() {
return fmt.Errorf("%s is not a valid TrackerPatternOrderField", string(text))
}
return nil
}
func (p TrackerPatternOrderField) MarshalText() ([]byte, error) {
return []byte(p.String()), nil
}

View File

@@ -403,12 +403,12 @@ const (
ActionCookieUpdate = "core:cookie:update" ActionCookieUpdate = "core:cookie:update"
ActionCookieDelete = "core:cookie:delete" ActionCookieDelete = "core:cookie:delete"
// CookiePattern actions // TrackerPattern actions
ActionCookiePatternGet = "core:cookie-pattern:get" ActionTrackerPatternGet = "core:tracker-pattern:get"
ActionCookiePatternList = "core:cookie-pattern:list" ActionTrackerPatternList = "core:tracker-pattern:list"
ActionCookiePatternCreate = "core:cookie-pattern:create" ActionTrackerPatternCreate = "core:tracker-pattern:create"
ActionCookiePatternUpdate = "core:cookie-pattern:update" ActionTrackerPatternUpdate = "core:tracker-pattern:update"
ActionCookiePatternDelete = "core:cookie-pattern:delete" ActionTrackerPatternDelete = "core:tracker-pattern:delete"
// CookieConsentRecord actions // CookieConsentRecord actions
ActionCookieConsentRecordList = "core:cookie-consent-record:list" ActionCookieConsentRecordList = "core:cookie-consent-record:list"

View File

@@ -177,54 +177,18 @@ func (r *cookieBannerResolver) ConsentRecords(ctx context.Context, obj *types.Co
return types.NewCookieConsentRecordConnection(p, r, obj.ID, coredataFilter), nil return types.NewCookieConsentRecordConnection(p, r, obj.ID, coredataFilter), nil
} }
// UncategorisedPatterns is the resolver for the uncategorisedPatterns field.
func (r *cookieBannerResolver) UncategorisedPatterns(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.CookiePatternOrderBy, filter *types.CookiePatternFilter) (*types.CookiePatternConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionCookiePatternList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.CookiePatternOrderField]{
Field: coredata.CookiePatternOrderFieldName,
Direction: page.OrderDirectionAsc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.CookiePatternOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
scope := coredata.NewScopeFromObjectID(obj.ID)
coredataFilter := coredata.NewCookiePatternFilter(nil, nil, nil)
if filter != nil {
coredataFilter = coredataFilter.WithQuery(filter.Query).WithSource(filter.Source)
}
patterns, err := r.cookieBanner.ListUncategorisedCookiePatterns(ctx, scope, obj.ID, cursor, coredataFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list uncategorised cookie patterns", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
p := page.NewPage(patterns, cursor)
return types.NewCookiePatternConnectionWithFilter(p, r, obj.ID, coredataFilter), nil
}
// UncategorisedTrackerPatterns is the resolver for the uncategorisedTrackerPatterns field. // UncategorisedTrackerPatterns is the resolver for the uncategorisedTrackerPatterns field.
func (r *cookieBannerResolver) UncategorisedTrackerPatterns(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TrackerPatternOrderBy, filter *types.TrackerPatternFilter) (*types.TrackerPatternConnection, error) { func (r *cookieBannerResolver) UncategorisedTrackerPatterns(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TrackerPatternOrderBy, filter *types.TrackerPatternFilter) (*types.TrackerPatternConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionCookiePatternList); err != nil { if err := r.authorize(ctx, obj.ID, probo.ActionTrackerPatternList); err != nil {
return nil, err return nil, err
} }
pageOrderBy := page.OrderBy[coredata.CookiePatternOrderField]{ pageOrderBy := page.OrderBy[coredata.TrackerPatternOrderField]{
Field: coredata.CookiePatternOrderFieldName, Field: coredata.TrackerPatternOrderFieldName,
Direction: page.OrderDirectionAsc, Direction: page.OrderDirectionAsc,
} }
if orderBy != nil { if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.CookiePatternOrderField]{ pageOrderBy = page.OrderBy[coredata.TrackerPatternOrderField]{
Field: orderBy.Field, Field: orderBy.Field,
Direction: orderBy.Direction, Direction: orderBy.Direction,
} }
@@ -233,7 +197,7 @@ func (r *cookieBannerResolver) UncategorisedTrackerPatterns(ctx context.Context,
cursor := types.NewCursor(first, after, last, before, pageOrderBy) cursor := types.NewCursor(first, after, last, before, pageOrderBy)
scope := coredata.NewScopeFromObjectID(obj.ID) scope := coredata.NewScopeFromObjectID(obj.ID)
coredataFilter := coredata.NewCookiePatternFilter(nil, nil, nil) coredataFilter := coredata.NewTrackerPatternFilter(nil, nil, nil)
if filter != nil { if filter != nil {
coredataFilter = coredataFilter.WithQuery(filter.Query).WithSource(filter.Source) coredataFilter = coredataFilter.WithQuery(filter.Query).WithSource(filter.Source)
} }
@@ -338,18 +302,18 @@ func (r *cookieCategoryResolver) CookieBanner(ctx context.Context, obj *types.Co
return types.NewCookieBanner(banner), nil return types.NewCookieBanner(banner), nil
} }
// CookiePatterns is the resolver for the cookiePatterns field. // TrackerPatterns is the resolver for the trackerPatterns field.
func (r *cookieCategoryResolver) CookiePatterns(ctx context.Context, obj *types.CookieCategory, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.CookiePatternOrderBy) (*types.CookiePatternConnection, error) { func (r *cookieCategoryResolver) TrackerPatterns(ctx context.Context, obj *types.CookieCategory, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TrackerPatternOrderBy) (*types.TrackerPatternConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionCookiePatternList); err != nil { if err := r.authorize(ctx, obj.ID, probo.ActionTrackerPatternList); err != nil {
return nil, err return nil, err
} }
pageOrderBy := page.OrderBy[coredata.CookiePatternOrderField]{ pageOrderBy := page.OrderBy[coredata.TrackerPatternOrderField]{
Field: coredata.CookiePatternOrderFieldCreatedAt, Field: coredata.TrackerPatternOrderFieldCreatedAt,
Direction: page.OrderDirectionAsc, Direction: page.OrderDirectionAsc,
} }
if orderBy != nil { if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.CookiePatternOrderField]{ pageOrderBy = page.OrderBy[coredata.TrackerPatternOrderField]{
Field: orderBy.Field, Field: orderBy.Field,
Direction: orderBy.Direction, Direction: orderBy.Direction,
} }
@@ -358,15 +322,15 @@ func (r *cookieCategoryResolver) CookiePatterns(ctx context.Context, obj *types.
cursor := types.NewCursor(first, after, last, before, pageOrderBy) cursor := types.NewCursor(first, after, last, before, pageOrderBy)
scope := coredata.NewScopeFromObjectID(obj.ID) scope := coredata.NewScopeFromObjectID(obj.ID)
patterns, err := r.cookieBanner.ListCookiePatternsForCategory(ctx, scope, obj.ID, cursor) patterns, err := r.cookieBanner.ListTrackerPatternsForCategory(ctx, scope, obj.ID, cursor)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot list cookie patterns", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot list tracker patterns", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
} }
p := page.NewPage(patterns, cursor) p := page.NewPage(patterns, cursor)
return types.NewCookiePatternConnection(p, r, obj.ID), nil return types.NewTrackerPatternConnection(p, r, obj.ID), nil
} }
// Permission is the resolver for the permission field. // Permission is the resolver for the permission field.
@@ -391,74 +355,6 @@ func (r *cookieCategoryConnectionResolver) TotalCount(ctx context.Context, obj *
return count, nil return count, nil
} }
// CookieCategory is the resolver for the cookieCategory field.
func (r *cookiePatternResolver) CookieCategory(ctx context.Context, obj *types.CookiePattern) (*types.CookieCategory, error) {
if err := r.authorize(ctx, obj.CookieCategory.ID, probo.ActionCookieCategoryGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
category, err := loaders.CookieCategory.Load(ctx, obj.CookieCategory.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get cookie category", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewCookieCategory(category), nil
}
// CookieCount is the resolver for the cookieCount field.
func (r *cookiePatternResolver) CookieCount(ctx context.Context, obj *types.CookiePattern) (int, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionCookiePatternGet); err != nil {
return 0, err
}
scope := coredata.NewScopeFromObjectID(obj.ID)
count, err := r.cookieBanner.CountCookiesForPattern(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count cookies for pattern", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// Permission is the resolver for the permission field.
func (r *cookiePatternResolver) Permission(ctx context.Context, obj *types.CookiePattern, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *cookiePatternConnectionResolver) TotalCount(ctx context.Context, obj *types.CookiePatternConnection) (int, error) {
if err := r.authorize(ctx, obj.ParentID, probo.ActionCookiePatternList); err != nil {
return 0, err
}
scope := coredata.NewScopeFromObjectID(obj.ParentID)
switch obj.Resolver.(type) {
case *cookieBannerResolver:
count, err := r.cookieBanner.CountUncategorisedCookiePatterns(ctx, scope, obj.ParentID, obj.Filter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count uncategorised cookie patterns", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
default:
count, err := r.cookieBanner.CountCookiePatternsForCategory(ctx, scope, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count cookie patterns", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
}
// CreateCookieBanner is the resolver for the createCookieBanner field. // CreateCookieBanner is the resolver for the createCookieBanner field.
func (r *mutationResolver) CreateCookieBanner(ctx context.Context, input types.CreateCookieBannerInput) (*types.CreateCookieBannerPayload, error) { func (r *mutationResolver) CreateCookieBanner(ctx context.Context, input types.CreateCookieBannerInput) (*types.CreateCookieBannerPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionCookieBannerCreate); err != nil { if err := r.authorize(ctx, input.OrganizationID, probo.ActionCookieBannerCreate); err != nil {
@@ -819,180 +715,6 @@ func (r *mutationResolver) ReorderCookieCategory(ctx context.Context, input type
}, nil }, nil
} }
// CreateCookiePattern is the resolver for the createCookiePattern field.
func (r *mutationResolver) CreateCookiePattern(ctx context.Context, input types.CreateCookiePatternInput) (*types.CreateCookiePatternPayload, error) {
if err := r.authorize(ctx, input.CookieCategoryID, probo.ActionCookiePatternCreate); err != nil {
return nil, err
}
scope := coredata.NewScopeFromObjectID(input.CookieCategoryID)
pattern, err := r.cookieBanner.CreateCookiePattern(
ctx,
scope,
cookiebanner.CreateCookiePatternRequest{
CookieCategoryID: input.CookieCategoryID,
Pattern: input.Pattern,
MatchType: input.MatchType,
DisplayName: input.DisplayName,
MaxAgeSeconds: input.MaxAgeSeconds,
Description: input.Description,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrPatternAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
if errors.Is(err, cookiebanner.ErrCategoryNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create cookie pattern", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerScope := coredata.NewScopeFromObjectID(pattern.CookieBannerID)
banner, err := r.cookieBanner.GetCookieBanner(ctx, bannerScope, pattern.CookieBannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateCookiePatternPayload{
CookiePatternEdge: types.NewCookiePatternEdge(pattern, coredata.CookiePatternOrderFieldCreatedAt),
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// UpdateCookiePattern is the resolver for the updateCookiePattern field.
func (r *mutationResolver) UpdateCookiePattern(ctx context.Context, input types.UpdateCookiePatternInput) (*types.UpdateCookiePatternPayload, error) {
if err := r.authorize(ctx, input.CookiePatternID, probo.ActionCookiePatternUpdate); err != nil {
return nil, err
}
scope := coredata.NewScopeFromObjectID(input.CookiePatternID)
pattern, err := r.cookieBanner.UpdateCookiePattern(
ctx,
scope,
cookiebanner.UpdateCookiePatternRequest{
CookiePatternID: input.CookiePatternID,
DisplayName: input.DisplayName,
MaxAgeSeconds: gqlutils.UnwrapOmittable(input.MaxAgeSeconds),
Description: input.Description,
Excluded: input.Excluded,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrCookiePatternNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update cookie pattern", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerScope := coredata.NewScopeFromObjectID(pattern.CookieBannerID)
banner, err := r.cookieBanner.GetCookieBanner(ctx, bannerScope, pattern.CookieBannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateCookiePatternPayload{
CookiePattern: types.NewCookiePattern(pattern),
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// DeleteCookiePattern is the resolver for the deleteCookiePattern field.
func (r *mutationResolver) DeleteCookiePattern(ctx context.Context, input types.DeleteCookiePatternInput) (*types.DeleteCookiePatternPayload, error) {
if err := r.authorize(ctx, input.CookiePatternID, probo.ActionCookiePatternDelete); err != nil {
return nil, err
}
scope := coredata.NewScopeFromObjectID(input.CookiePatternID)
pattern, err := r.cookieBanner.GetCookiePattern(ctx, scope, input.CookiePatternID)
if err != nil {
if errors.Is(err, cookiebanner.ErrCookieNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get cookie pattern", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerID := pattern.CookieBannerID
err = r.cookieBanner.DeleteCookiePattern(ctx, scope, input.CookiePatternID)
if err != nil {
if errors.Is(err, cookiebanner.ErrCookiePatternNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot delete cookie pattern", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerScope := coredata.NewScopeFromObjectID(bannerID)
banner, err := r.cookieBanner.GetCookieBanner(ctx, bannerScope, bannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteCookiePatternPayload{
DeletedCookiePatternID: input.CookiePatternID,
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// MoveCookiePatternToCategory is the resolver for the moveCookiePatternToCategory field.
func (r *mutationResolver) MoveCookiePatternToCategory(ctx context.Context, input types.MoveCookiePatternToCategoryInput) (*types.MoveCookiePatternToCategoryPayload, error) {
if err := r.authorize(ctx, input.CookiePatternID, probo.ActionCookiePatternUpdate); err != nil {
return nil, err
}
if err := r.authorize(ctx, input.TargetCookieCategoryID, probo.ActionCookieCategoryUpdate); err != nil {
return nil, err
}
scope := coredata.NewScopeFromObjectID(input.CookiePatternID)
result, err := r.cookieBanner.MoveCookiePatternToCategory(
ctx,
scope,
cookiebanner.MoveCookiePatternToCategoryRequest{
CookiePatternID: input.CookiePatternID,
TargetCookieCategoryID: input.TargetCookieCategoryID,
},
)
if err != nil {
switch {
case errors.Is(err, cookiebanner.ErrCategoryNotFound):
return nil, gqlutils.NotFound(ctx, err)
case errors.Is(err, cookiebanner.ErrCookiePatternNotFound):
return nil, gqlutils.NotFound(ctx, err)
case errors.Is(err, cookiebanner.ErrCategoriesBannerMismatch):
return nil, gqlutils.NotFoundf(ctx, "cookie pattern or target category not found")
default:
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot move cookie pattern to category", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
return &types.MoveCookiePatternToCategoryPayload{
CookiePattern: types.NewCookiePattern(result.CookiePattern),
CookieBanner: types.NewCookieBanner(result.Banner),
}, nil
}
// UpsertCookieBannerTranslation is the resolver for the upsertCookieBannerTranslation field. // UpsertCookieBannerTranslation is the resolver for the upsertCookieBannerTranslation field.
func (r *mutationResolver) UpsertCookieBannerTranslation(ctx context.Context, input types.UpsertCookieBannerTranslationInput) (*types.UpsertCookieBannerTranslationPayload, error) { func (r *mutationResolver) UpsertCookieBannerTranslation(ctx context.Context, input types.UpsertCookieBannerTranslationInput) (*types.UpsertCookieBannerTranslationPayload, error) {
if err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerUpdate); err != nil { if err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerUpdate); err != nil {
@@ -1033,9 +755,64 @@ func (r *mutationResolver) UpsertCookieBannerTranslation(ctx context.Context, in
}, nil }, nil
} }
// CreateTrackerPattern is the resolver for the createTrackerPattern field.
func (r *mutationResolver) CreateTrackerPattern(ctx context.Context, input types.CreateTrackerPatternInput) (*types.CreateTrackerPatternPayload, error) {
if err := r.authorize(ctx, input.CookieCategoryID, probo.ActionTrackerPatternCreate); err != nil {
return nil, err
}
scope := coredata.NewScopeFromObjectID(input.CookieCategoryID)
trackerType := coredata.TrackerTypeCookie
if input.TrackerType != nil {
trackerType = *input.TrackerType
}
var description string
if input.Description != nil {
description = *input.Description
}
pattern, err := r.cookieBanner.CreateTrackerPattern(
ctx,
scope,
cookiebanner.CreateTrackerPatternRequest{
CookieCategoryID: input.CookieCategoryID,
TrackerType: trackerType,
Pattern: input.Pattern,
MatchType: input.MatchType,
DisplayName: input.DisplayName,
MaxAgeSeconds: input.MaxAgeSeconds,
Description: description,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrPatternAlreadyExists) {
return nil, gqlutils.Conflictf(ctx, "a pattern with this name already exists in this banner")
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create tracker pattern", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerScope := coredata.NewScopeFromObjectID(pattern.CookieBannerID)
banner, err := r.cookieBanner.GetCookieBanner(ctx, bannerScope, pattern.CookieBannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateTrackerPatternPayload{
TrackerPatternEdge: types.NewTrackerPatternEdge(pattern, coredata.TrackerPatternOrderFieldCreatedAt),
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// UpdateTrackerPattern is the resolver for the updateTrackerPattern field. // UpdateTrackerPattern is the resolver for the updateTrackerPattern field.
func (r *mutationResolver) UpdateTrackerPattern(ctx context.Context, input types.UpdateTrackerPatternInput) (*types.UpdateTrackerPatternPayload, error) { func (r *mutationResolver) UpdateTrackerPattern(ctx context.Context, input types.UpdateTrackerPatternInput) (*types.UpdateTrackerPatternPayload, error) {
if err := r.authorize(ctx, input.TrackerPatternID, probo.ActionCookiePatternUpdate); err != nil { if err := r.authorize(ctx, input.TrackerPatternID, probo.ActionTrackerPatternUpdate); err != nil {
return nil, err return nil, err
} }
@@ -1053,7 +830,7 @@ func (r *mutationResolver) UpdateTrackerPattern(ctx context.Context, input types
}, },
) )
if err != nil { if err != nil {
if errors.Is(err, cookiebanner.ErrCookiePatternNotFound) { if errors.Is(err, cookiebanner.ErrTrackerPatternNotFound) {
return nil, gqlutils.NotFound(ctx, err) return nil, gqlutils.NotFound(ctx, err)
} }
r.logger.ErrorCtx(ctx, "cannot update tracker pattern", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot update tracker pattern", log.Error(err))
@@ -1075,7 +852,7 @@ func (r *mutationResolver) UpdateTrackerPattern(ctx context.Context, input types
// DeleteTrackerPattern is the resolver for the deleteTrackerPattern field. // DeleteTrackerPattern is the resolver for the deleteTrackerPattern field.
func (r *mutationResolver) DeleteTrackerPattern(ctx context.Context, input types.DeleteTrackerPatternInput) (*types.DeleteTrackerPatternPayload, error) { func (r *mutationResolver) DeleteTrackerPattern(ctx context.Context, input types.DeleteTrackerPatternInput) (*types.DeleteTrackerPatternPayload, error) {
if err := r.authorize(ctx, input.TrackerPatternID, probo.ActionCookiePatternDelete); err != nil { if err := r.authorize(ctx, input.TrackerPatternID, probo.ActionTrackerPatternDelete); err != nil {
return nil, err return nil, err
} }
@@ -1083,7 +860,7 @@ func (r *mutationResolver) DeleteTrackerPattern(ctx context.Context, input types
pattern, err := r.cookieBanner.GetTrackerPattern(ctx, scope, input.TrackerPatternID) pattern, err := r.cookieBanner.GetTrackerPattern(ctx, scope, input.TrackerPatternID)
if err != nil { if err != nil {
if errors.Is(err, cookiebanner.ErrCookiePatternNotFound) { if errors.Is(err, cookiebanner.ErrTrackerPatternNotFound) {
return nil, gqlutils.NotFound(ctx, err) return nil, gqlutils.NotFound(ctx, err)
} }
r.logger.ErrorCtx(ctx, "cannot get tracker pattern", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot get tracker pattern", log.Error(err))
@@ -1093,7 +870,7 @@ func (r *mutationResolver) DeleteTrackerPattern(ctx context.Context, input types
bannerID := pattern.CookieBannerID bannerID := pattern.CookieBannerID
if err := r.cookieBanner.DeleteTrackerPattern(ctx, scope, input.TrackerPatternID); err != nil { if err := r.cookieBanner.DeleteTrackerPattern(ctx, scope, input.TrackerPatternID); err != nil {
if errors.Is(err, cookiebanner.ErrCookiePatternNotFound) { if errors.Is(err, cookiebanner.ErrTrackerPatternNotFound) {
return nil, gqlutils.NotFound(ctx, err) return nil, gqlutils.NotFound(ctx, err)
} }
r.logger.ErrorCtx(ctx, "cannot delete tracker pattern", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot delete tracker pattern", log.Error(err))
@@ -1115,7 +892,7 @@ func (r *mutationResolver) DeleteTrackerPattern(ctx context.Context, input types
// MoveTrackerPatternToCategory is the resolver for the moveTrackerPatternToCategory field. // MoveTrackerPatternToCategory is the resolver for the moveTrackerPatternToCategory field.
func (r *mutationResolver) MoveTrackerPatternToCategory(ctx context.Context, input types.MoveTrackerPatternToCategoryInput) (*types.MoveTrackerPatternToCategoryPayload, error) { func (r *mutationResolver) MoveTrackerPatternToCategory(ctx context.Context, input types.MoveTrackerPatternToCategoryInput) (*types.MoveTrackerPatternToCategoryPayload, error) {
if err := r.authorize(ctx, input.TrackerPatternID, probo.ActionCookiePatternUpdate); err != nil { if err := r.authorize(ctx, input.TrackerPatternID, probo.ActionTrackerPatternUpdate); err != nil {
return nil, err return nil, err
} }
@@ -1137,7 +914,7 @@ func (r *mutationResolver) MoveTrackerPatternToCategory(ctx context.Context, inp
switch { switch {
case errors.Is(err, cookiebanner.ErrCategoryNotFound): case errors.Is(err, cookiebanner.ErrCategoryNotFound):
return nil, gqlutils.NotFound(ctx, err) return nil, gqlutils.NotFound(ctx, err)
case errors.Is(err, cookiebanner.ErrCookiePatternNotFound): case errors.Is(err, cookiebanner.ErrTrackerPatternNotFound):
return nil, gqlutils.NotFound(ctx, err) return nil, gqlutils.NotFound(ctx, err)
case errors.Is(err, cookiebanner.ErrCategoriesBannerMismatch): case errors.Is(err, cookiebanner.ErrCategoriesBannerMismatch):
return nil, gqlutils.NotFoundf(ctx, "tracker pattern or target category not found") return nil, gqlutils.NotFoundf(ctx, "tracker pattern or target category not found")
@@ -1195,7 +972,7 @@ func (r *trackerPatternResolver) Permission(ctx context.Context, obj *types.Trac
func (r *trackerPatternConnectionResolver) TotalCount(ctx context.Context, obj *types.TrackerPatternConnection) (int, error) { func (r *trackerPatternConnectionResolver) TotalCount(ctx context.Context, obj *types.TrackerPatternConnection) (int, error) {
scope := coredata.NewScopeFromObjectID(obj.ParentID) scope := coredata.NewScopeFromObjectID(obj.ParentID)
filter := coredata.NewCookiePatternFilter(nil, nil, nil) filter := coredata.NewTrackerPatternFilter(nil, nil, nil)
if obj.Filter != nil { if obj.Filter != nil {
filter = filter.WithQuery(obj.Filter.Query).WithSource(obj.Filter.Source) filter = filter.WithQuery(obj.Filter.Query).WithSource(obj.Filter.Source)
} }
@@ -1230,14 +1007,6 @@ func (r *Resolver) CookieCategoryConnection() schema.CookieCategoryConnectionRes
return &cookieCategoryConnectionResolver{r} return &cookieCategoryConnectionResolver{r}
} }
// CookiePattern returns schema.CookiePatternResolver implementation.
func (r *Resolver) CookiePattern() schema.CookiePatternResolver { return &cookiePatternResolver{r} }
// CookiePatternConnection returns schema.CookiePatternConnectionResolver implementation.
func (r *Resolver) CookiePatternConnection() schema.CookiePatternConnectionResolver {
return &cookiePatternConnectionResolver{r}
}
// TrackerPattern returns schema.TrackerPatternResolver implementation. // TrackerPattern returns schema.TrackerPatternResolver implementation.
func (r *Resolver) TrackerPattern() schema.TrackerPatternResolver { return &trackerPatternResolver{r} } func (r *Resolver) TrackerPattern() schema.TrackerPatternResolver { return &trackerPatternResolver{r} }
@@ -1251,7 +1020,5 @@ type cookieBannerConnectionResolver struct{ *Resolver }
type cookieBannerVersionResolver struct{ *Resolver } type cookieBannerVersionResolver struct{ *Resolver }
type cookieCategoryResolver struct{ *Resolver } type cookieCategoryResolver struct{ *Resolver }
type cookieCategoryConnectionResolver struct{ *Resolver } type cookieCategoryConnectionResolver struct{ *Resolver }
type cookiePatternResolver struct{ *Resolver }
type cookiePatternConnectionResolver struct{ *Resolver }
type trackerPatternResolver struct{ *Resolver } type trackerPatternResolver struct{ *Resolver }
type trackerPatternConnectionResolver struct{ *Resolver } type trackerPatternConnectionResolver struct{ *Resolver }

View File

@@ -151,15 +151,6 @@ type CookieBanner implements Node {
filter: CookieConsentRecordFilter filter: CookieConsentRecordFilter
): CookieConsentRecordConnection @goField(forceResolver: true) ): CookieConsentRecordConnection @goField(forceResolver: true)
uncategorisedPatterns(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: CookiePatternOrder
filter: CookiePatternFilter
): CookiePatternConnection @goField(forceResolver: true)
uncategorisedTrackerPatterns( uncategorisedTrackerPatterns(
first: Int first: Int
after: CursorKey after: CursorKey
@@ -194,13 +185,13 @@ type CookieCategory implements Node {
gcmConsentTypes: [String!]! gcmConsentTypes: [String!]!
posthogConsent: Boolean! posthogConsent: Boolean!
cookiePatterns( trackerPatterns(
first: Int first: Int
after: CursorKey after: CursorKey
last: Int last: Int
before: CursorKey before: CursorKey
orderBy: CookiePatternOrder orderBy: TrackerPatternOrder
): CookiePatternConnection @goField(forceResolver: true) ): TrackerPatternConnection @goField(forceResolver: true)
createdAt: Datetime! createdAt: Datetime!
updatedAt: Datetime! updatedAt: Datetime!
@@ -208,98 +199,52 @@ type CookieCategory implements Node {
permission(action: String!): Boolean! @goField(forceResolver: true) permission(action: String!): Boolean! @goField(forceResolver: true)
} }
enum CookiePatternMatchType enum TrackerPatternMatchType
@goModel( @goModel(
model: "go.probo.inc/probo/pkg/coredata.CookiePatternMatchType" model: "go.probo.inc/probo/pkg/coredata.TrackerPatternMatchType"
) { ) {
EXACT EXACT
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternMatchTypeExact" value: "go.probo.inc/probo/pkg/coredata.TrackerPatternMatchTypeExact"
) )
PREFIX PREFIX
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternMatchTypePrefix" value: "go.probo.inc/probo/pkg/coredata.TrackerPatternMatchTypePrefix"
) )
} }
enum CookiePatternOrderField enum TrackerPatternOrderField
@goModel( @goModel(
model: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderField" model: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderField"
) { ) {
CREATED_AT CREATED_AT
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderFieldCreatedAt" value: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderFieldCreatedAt"
) )
NAME NAME
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderFieldName" value: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderFieldName"
) )
LAST_MATCHED_AT LAST_MATCHED_AT
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderFieldLastMatchedAt" value: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderFieldLastMatchedAt"
) )
UPDATED_AT UPDATED_AT
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderFieldUpdatedAt" value: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderFieldUpdatedAt"
) )
SOURCE SOURCE
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderFieldSource" value: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderFieldSource"
) )
} }
input CookiePatternOrder
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.CookiePatternOrderBy"
) {
direction: OrderDirection!
field: CookiePatternOrderField!
}
input CookiePatternFilter {
query: String
source: CookieSource
}
type CookiePattern implements Node {
id: ID!
cookieCategory: CookieCategory @goField(forceResolver: true)
trackerType: TrackerType!
pattern: String!
matchType: CookiePatternMatchType!
displayName: String!
maxAgeSeconds: Int
description: String!
source: CookieSource
excluded: Boolean!
cookieCount: Int! @goField(forceResolver: true)
lastMatchedAt: Datetime
createdAt: Datetime!
updatedAt: Datetime!
permission(action: String!): Boolean! @goField(forceResolver: true)
}
type CookiePatternConnection
@goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.CookiePatternConnection"
) {
totalCount: Int! @goField(forceResolver: true)
edges: [CookiePatternEdge!]!
pageInfo: PageInfo!
}
type CookiePatternEdge {
cursor: CursorKey!
node: CookiePattern!
}
type TrackerPattern implements Node { type TrackerPattern implements Node {
id: ID! id: ID!
cookieCategory: CookieCategory @goField(forceResolver: true) cookieCategory: CookieCategory @goField(forceResolver: true)
trackerType: TrackerType! trackerType: TrackerType!
pattern: String! pattern: String!
matchType: CookiePatternMatchType! matchType: TrackerPatternMatchType!
displayName: String! displayName: String!
maxAgeSeconds: Int maxAgeSeconds: Int
description: String! description: String!
@@ -332,7 +277,7 @@ input TrackerPatternOrder
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrackerPatternOrderBy" model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrackerPatternOrderBy"
) { ) {
direction: OrderDirection! direction: OrderDirection!
field: CookiePatternOrderField! field: TrackerPatternOrderField!
} }
input TrackerPatternFilter input TrackerPatternFilter
@@ -426,21 +371,12 @@ extend type Mutation {
reorderCookieCategory( reorderCookieCategory(
input: ReorderCookieCategoryInput! input: ReorderCookieCategoryInput!
): ReorderCookieCategoryPayload! ): ReorderCookieCategoryPayload!
createCookiePattern(
input: CreateCookiePatternInput!
): CreateCookiePatternPayload!
updateCookiePattern(
input: UpdateCookiePatternInput!
): UpdateCookiePatternPayload!
deleteCookiePattern(
input: DeleteCookiePatternInput!
): DeleteCookiePatternPayload!
moveCookiePatternToCategory(
input: MoveCookiePatternToCategoryInput!
): MoveCookiePatternToCategoryPayload!
upsertCookieBannerTranslation( upsertCookieBannerTranslation(
input: UpsertCookieBannerTranslationInput! input: UpsertCookieBannerTranslationInput!
): UpsertCookieBannerTranslationPayload! ): UpsertCookieBannerTranslationPayload!
createTrackerPattern(
input: CreateTrackerPatternInput!
): CreateTrackerPatternPayload!
updateTrackerPattern( updateTrackerPattern(
input: UpdateTrackerPatternInput! input: UpdateTrackerPatternInput!
): UpdateTrackerPatternPayload! ): UpdateTrackerPatternPayload!
@@ -558,53 +494,6 @@ type ReorderCookieCategoryPayload {
cookieBanner: CookieBanner! cookieBanner: CookieBanner!
} }
input CreateCookiePatternInput {
cookieCategoryId: ID!
trackerType: TrackerType
pattern: String!
matchType: CookiePatternMatchType!
displayName: String!
maxAgeSeconds: Int
description: String!
}
input UpdateCookiePatternInput {
cookiePatternId: ID!
displayName: String
maxAgeSeconds: Int @goField(omittable: true)
description: String
excluded: Boolean
}
input DeleteCookiePatternInput {
cookiePatternId: ID!
}
input MoveCookiePatternToCategoryInput {
cookiePatternId: ID!
targetCookieCategoryId: ID!
}
type CreateCookiePatternPayload {
cookiePatternEdge: CookiePatternEdge!
cookieBanner: CookieBanner!
}
type UpdateCookiePatternPayload {
cookiePattern: CookiePattern!
cookieBanner: CookieBanner!
}
type DeleteCookiePatternPayload {
deletedCookiePatternId: ID!
cookieBanner: CookieBanner!
}
type MoveCookiePatternToCategoryPayload {
cookiePattern: CookiePattern!
cookieBanner: CookieBanner!
}
input UpsertCookieBannerTranslationInput { input UpsertCookieBannerTranslationInput {
cookieBannerId: ID! cookieBannerId: ID!
language: String! language: String!
@@ -616,6 +505,16 @@ type UpsertCookieBannerTranslationPayload {
cookieBanner: CookieBanner! cookieBanner: CookieBanner!
} }
input CreateTrackerPatternInput {
cookieCategoryId: ID!
trackerType: TrackerType
pattern: String!
matchType: TrackerPatternMatchType!
displayName: String!
maxAgeSeconds: Int
description: String
}
input UpdateTrackerPatternInput { input UpdateTrackerPatternInput {
trackerPatternId: ID! trackerPatternId: ID!
displayName: String displayName: String
@@ -633,6 +532,11 @@ input MoveTrackerPatternToCategoryInput {
targetCookieCategoryId: ID! targetCookieCategoryId: ID!
} }
type CreateTrackerPatternPayload {
trackerPatternEdge: TrackerPatternEdge!
cookieBanner: CookieBanner!
}
type UpdateTrackerPatternPayload { type UpdateTrackerPatternPayload {
trackerPattern: TrackerPattern! trackerPattern: TrackerPattern!
cookieBanner: CookieBanner! cookieBanner: CookieBanner!

View File

@@ -1,119 +0,0 @@
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package types
import (
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
)
type (
CookiePatternOrderBy OrderBy[coredata.CookiePatternOrderField]
CookiePatternConnection struct {
TotalCount int
Edges []*CookiePatternEdge
PageInfo PageInfo
Resolver any
ParentID gid.GID
Filter *coredata.CookiePatternFilter
}
)
func NewCookiePatternConnection(
p *page.Page[*coredata.CookiePattern, coredata.CookiePatternOrderField],
parentType any,
parentID gid.GID,
) *CookiePatternConnection {
edges := make([]*CookiePatternEdge, len(p.Data))
for i := range edges {
edges[i] = NewCookiePatternEdge(p.Data[i], p.Cursor.OrderBy.Field)
}
return &CookiePatternConnection{
Edges: edges,
PageInfo: *NewPageInfo(p),
Resolver: parentType,
ParentID: parentID,
}
}
func NewCookiePatternConnectionWithFilter(
p *page.Page[*coredata.CookiePattern, coredata.CookiePatternOrderField],
parentType any,
parentID gid.GID,
filter *coredata.CookiePatternFilter,
) *CookiePatternConnection {
conn := NewCookiePatternConnection(p, parentType, parentID)
conn.Filter = filter
return conn
}
func NewCookiePatternEdge(cp *coredata.CookiePattern, orderBy coredata.CookiePatternOrderField) *CookiePatternEdge {
return &CookiePatternEdge{
Cursor: cp.CursorKey(orderBy),
Node: NewCookiePattern(cp),
}
}
func NewCookiePattern(cp *coredata.CookiePattern) *CookiePattern {
return &CookiePattern{
ID: cp.ID,
CookieCategory: &CookieCategory{
ID: cp.CookieCategoryID,
CookieBanner: &CookieBanner{
ID: cp.CookieBannerID,
},
},
TrackerType: coredata.TrackerTypeCookie,
Pattern: cp.Pattern,
MatchType: cp.MatchType,
DisplayName: cp.DisplayName,
MaxAgeSeconds: cp.MaxAgeSeconds,
Description: cp.Description,
Source: &cp.Source,
Excluded: cp.Excluded,
LastMatchedAt: cp.LastMatchedAt,
CreatedAt: cp.CreatedAt,
UpdatedAt: cp.UpdatedAt,
}
}
func NewTrackerPattern(tp *coredata.TrackerPattern) *CookiePattern {
return &CookiePattern{
ID: tp.ID,
CookieCategory: &CookieCategory{
ID: tp.CookieCategoryID,
CookieBanner: &CookieBanner{
ID: tp.CookieBannerID,
},
},
TrackerType: tp.TrackerType,
Pattern: tp.Pattern,
MatchType: tp.MatchType,
DisplayName: tp.DisplayName,
MaxAgeSeconds: tp.MaxAgeSeconds,
Description: tp.Description,
Source: tp.Source,
Excluded: tp.Excluded,
LastMatchedAt: tp.LastMatchedAt,
CreatedAt: tp.CreatedAt,
UpdatedAt: tp.UpdatedAt,
}
}

View File

@@ -21,7 +21,7 @@ import (
) )
type ( type (
TrackerPatternOrderBy OrderBy[coredata.CookiePatternOrderField] TrackerPatternOrderBy OrderBy[coredata.TrackerPatternOrderField]
TrackerPatternConnection struct { TrackerPatternConnection struct {
TotalCount int TotalCount int
@@ -41,7 +41,7 @@ type (
) )
func NewTrackerPatternConnection( func NewTrackerPatternConnection(
p *page.Page[*coredata.TrackerPattern, coredata.CookiePatternOrderField], p *page.Page[*coredata.TrackerPattern, coredata.TrackerPatternOrderField],
parentType any, parentType any,
parentID gid.GID, parentID gid.GID,
) *TrackerPatternConnection { ) *TrackerPatternConnection {
@@ -61,7 +61,7 @@ func NewTrackerPatternConnection(
} }
func NewTrackerPatternConnectionWithFilter( func NewTrackerPatternConnectionWithFilter(
p *page.Page[*coredata.TrackerPattern, coredata.CookiePatternOrderField], p *page.Page[*coredata.TrackerPattern, coredata.TrackerPatternOrderField],
parentType any, parentType any,
parentID gid.GID, parentID gid.GID,
filter *TrackerPatternFilter, filter *TrackerPatternFilter,
@@ -71,7 +71,7 @@ func NewTrackerPatternConnectionWithFilter(
return conn return conn
} }
func NewTrackerPatternEdge(tp *coredata.TrackerPattern, orderBy coredata.CookiePatternOrderField) *TrackerPatternEdge { func NewTrackerPatternEdge(tp *coredata.TrackerPattern, orderBy coredata.TrackerPatternOrderField) *TrackerPatternEdge {
return &TrackerPatternEdge{ return &TrackerPatternEdge{
Cursor: tp.CursorKey(orderBy), Cursor: tp.CursorKey(orderBy),
Node: NewTrackerPatternNode(tp), Node: NewTrackerPatternNode(tp),

View File

@@ -4941,49 +4941,50 @@ func (r *Resolver) ReorderCookieCategoryTool(ctx context.Context, req *mcp.CallT
return nil, types.ReorderCookieCategoryOutput{CookieCategory: types.NewCookieCategory(category)}, nil return nil, types.ReorderCookieCategoryOutput{CookieCategory: types.NewCookieCategory(category)}, nil
} }
func (r *Resolver) ListCookiePatternsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListCookiePatternsInput) (*mcp.CallToolResult, types.ListCookiePatternsOutput, error) { func (r *Resolver) ListTrackerPatternsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListTrackerPatternsInput) (*mcp.CallToolResult, types.ListTrackerPatternsOutput, error) {
r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionCookiePatternList) r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionTrackerPatternList)
scope := coredata.NewScopeFromObjectID(input.CookieCategoryID) scope := coredata.NewScopeFromObjectID(input.CookieCategoryID)
cursor := types.NewCursor(input.Size, input.Cursor, page.OrderBy[coredata.CookiePatternOrderField]{Field: coredata.CookiePatternOrderFieldCreatedAt, Direction: page.OrderDirectionAsc}) cursor := types.NewCursor(input.Size, input.Cursor, page.OrderBy[coredata.TrackerPatternOrderField]{Field: coredata.TrackerPatternOrderFieldCreatedAt, Direction: page.OrderDirectionAsc})
patterns, err := r.cookieBanner.ListCookiePatternsForCategory(ctx, scope, input.CookieCategoryID, cursor) patterns, err := r.cookieBanner.ListTrackerPatternsForCategory(ctx, scope, input.CookieCategoryID, cursor)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot list cookie patterns: %w", err)) panic(fmt.Errorf("cannot list tracker patterns: %w", err))
} }
p := page.NewPage(patterns, cursor) p := page.NewPage(patterns, cursor)
return nil, types.NewListCookiePatternsOutput(p), nil return nil, types.NewListTrackerPatternsOutput(p), nil
} }
func (r *Resolver) GetCookiePatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetCookiePatternInput) (*mcp.CallToolResult, types.GetCookiePatternOutput, error) { func (r *Resolver) GetTrackerPatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetTrackerPatternInput) (*mcp.CallToolResult, types.GetTrackerPatternOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionCookiePatternGet) r.MustAuthorize(ctx, input.ID, probo.ActionTrackerPatternGet)
scope := coredata.NewScopeFromObjectID(input.ID) scope := coredata.NewScopeFromObjectID(input.ID)
pattern, err := r.cookieBanner.GetCookiePattern(ctx, scope, input.ID) pattern, err := r.cookieBanner.GetTrackerPattern(ctx, scope, input.ID)
if err != nil { if err != nil {
return nil, types.GetCookiePatternOutput{}, fmt.Errorf("cannot get cookie pattern: %w", err) return nil, types.GetTrackerPatternOutput{}, fmt.Errorf("cannot get tracker pattern: %w", err)
} }
return nil, types.GetCookiePatternOutput{CookiePattern: types.NewCookiePattern(pattern)}, nil return nil, types.GetTrackerPatternOutput{TrackerPattern: types.NewTrackerPattern(pattern)}, nil
} }
func (r *Resolver) AddCookiePatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddCookiePatternInput) (*mcp.CallToolResult, types.AddCookiePatternOutput, error) { func (r *Resolver) AddTrackerPatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddTrackerPatternInput) (*mcp.CallToolResult, types.AddTrackerPatternOutput, error) {
r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionCookiePatternCreate) r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionTrackerPatternCreate)
scope := coredata.NewScopeFromObjectID(input.CookieCategoryID) scope := coredata.NewScopeFromObjectID(input.CookieCategoryID)
pattern, err := r.cookieBanner.CreateCookiePattern(ctx, scope, cookiebanner.CreateCookiePatternRequest{ pattern, err := r.cookieBanner.CreateTrackerPattern(ctx, scope, cookiebanner.CreateTrackerPatternRequest{
CookieCategoryID: input.CookieCategoryID, CookieCategoryID: input.CookieCategoryID,
TrackerType: coredata.TrackerType(input.TrackerType),
Pattern: input.Pattern, Pattern: input.Pattern,
MatchType: coredata.CookiePatternMatchType(input.MatchType), MatchType: coredata.TrackerPatternMatchType(input.MatchType),
DisplayName: input.DisplayName, DisplayName: input.DisplayName,
MaxAgeSeconds: input.MaxAgeSeconds, MaxAgeSeconds: input.MaxAgeSeconds,
Description: input.Description, Description: input.Description,
}) })
if err != nil { if err != nil {
return nil, types.AddCookiePatternOutput{}, fmt.Errorf("cannot create cookie pattern: %w", err) return nil, types.AddTrackerPatternOutput{}, fmt.Errorf("cannot create tracker pattern: %w", err)
} }
return nil, types.AddCookiePatternOutput{CookiePattern: types.NewCookiePattern(pattern)}, nil return nil, types.AddTrackerPatternOutput{TrackerPattern: types.NewTrackerPattern(pattern)}, nil
} }
func (r *Resolver) UpdateCookiePatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateCookiePatternInput) (*mcp.CallToolResult, types.UpdateCookiePatternOutput, error) { func (r *Resolver) UpdateTrackerPatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateTrackerPatternInput) (*mcp.CallToolResult, types.UpdateTrackerPatternOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionCookiePatternUpdate) r.MustAuthorize(ctx, input.ID, probo.ActionTrackerPatternUpdate)
scope := coredata.NewScopeFromObjectID(input.ID) scope := coredata.NewScopeFromObjectID(input.ID)
updateReq := cookiebanner.UpdateCookiePatternRequest{CookiePatternID: input.ID} updateReq := cookiebanner.UpdateTrackerPatternRequest{TrackerPatternID: input.ID}
if v := UnwrapOmittable(input.DisplayName); v != nil && *v != nil { if v := UnwrapOmittable(input.DisplayName); v != nil && *v != nil {
updateReq.DisplayName = *v updateReq.DisplayName = *v
} }
@@ -4997,33 +4998,33 @@ func (r *Resolver) UpdateCookiePatternTool(ctx context.Context, req *mcp.CallToo
if v := UnwrapOmittable(input.Excluded); v != nil && *v != nil { if v := UnwrapOmittable(input.Excluded); v != nil && *v != nil {
updateReq.Excluded = *v updateReq.Excluded = *v
} }
pattern, err := r.cookieBanner.UpdateCookiePattern(ctx, scope, updateReq) pattern, err := r.cookieBanner.UpdateTrackerPattern(ctx, scope, updateReq)
if err != nil { if err != nil {
return nil, types.UpdateCookiePatternOutput{}, fmt.Errorf("cannot update cookie pattern: %w", err) return nil, types.UpdateTrackerPatternOutput{}, fmt.Errorf("cannot update tracker pattern: %w", err)
} }
return nil, types.UpdateCookiePatternOutput{CookiePattern: types.NewCookiePattern(pattern)}, nil return nil, types.UpdateTrackerPatternOutput{TrackerPattern: types.NewTrackerPattern(pattern)}, nil
} }
func (r *Resolver) DeleteCookiePatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteCookiePatternInput) (*mcp.CallToolResult, types.DeleteCookiePatternOutput, error) { func (r *Resolver) DeleteTrackerPatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteTrackerPatternInput) (*mcp.CallToolResult, types.DeleteTrackerPatternOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionCookiePatternDelete) r.MustAuthorize(ctx, input.ID, probo.ActionTrackerPatternDelete)
scope := coredata.NewScopeFromObjectID(input.ID) scope := coredata.NewScopeFromObjectID(input.ID)
if err := r.cookieBanner.DeleteCookiePattern(ctx, scope, input.ID); err != nil { if err := r.cookieBanner.DeleteTrackerPattern(ctx, scope, input.ID); err != nil {
return nil, types.DeleteCookiePatternOutput{}, fmt.Errorf("cannot delete cookie pattern: %w", err) return nil, types.DeleteTrackerPatternOutput{}, fmt.Errorf("cannot delete tracker pattern: %w", err)
} }
return nil, types.DeleteCookiePatternOutput{DeletedID: input.ID}, nil return nil, types.DeleteTrackerPatternOutput{DeletedID: input.ID}, nil
} }
func (r *Resolver) MoveCookiePatternToCategoryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.MoveCookiePatternToCategoryInput) (*mcp.CallToolResult, types.MoveCookiePatternToCategoryOutput, error) { func (r *Resolver) MoveTrackerPatternToCategoryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.MoveTrackerPatternToCategoryInput) (*mcp.CallToolResult, types.MoveTrackerPatternToCategoryOutput, error) {
r.MustAuthorize(ctx, input.CookiePatternID, probo.ActionCookiePatternUpdate) r.MustAuthorize(ctx, input.TrackerPatternID, probo.ActionTrackerPatternUpdate)
scope := coredata.NewScopeFromObjectID(input.CookiePatternID) scope := coredata.NewScopeFromObjectID(input.TrackerPatternID)
result, err := r.cookieBanner.MoveCookiePatternToCategory(ctx, scope, cookiebanner.MoveCookiePatternToCategoryRequest{ result, err := r.cookieBanner.MoveTrackerPatternToCategory(ctx, scope, cookiebanner.MoveTrackerPatternToCategoryRequest{
CookiePatternID: input.CookiePatternID, TrackerPatternID: input.TrackerPatternID,
TargetCookieCategoryID: input.TargetCookieCategoryID, TargetCookieCategoryID: input.TargetCookieCategoryID,
}) })
if err != nil { if err != nil {
return nil, types.MoveCookiePatternToCategoryOutput{}, fmt.Errorf("cannot move cookie pattern: %w", err) return nil, types.MoveTrackerPatternToCategoryOutput{}, fmt.Errorf("cannot move tracker pattern: %w", err)
} }
return nil, types.MoveCookiePatternToCategoryOutput{CookiePattern: types.NewCookiePattern(result.CookiePattern)}, nil return nil, types.MoveTrackerPatternToCategoryOutput{TrackerPattern: types.NewTrackerPattern(result.TrackerPattern)}, nil
} }
func (r *Resolver) PublishCookieBannerVersionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.PublishCookieBannerVersionInput) (*mcp.CallToolResult, types.PublishCookieBannerVersionOutput, error) { func (r *Resolver) PublishCookieBannerVersionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.PublishCookieBannerVersionInput) (*mcp.CallToolResult, types.PublishCookieBannerVersionOutput, error) {

View File

@@ -9132,13 +9132,14 @@ components:
format: date-time format: date-time
description: Last update timestamp description: Last update timestamp
CookiePattern: TrackerPattern:
type: object type: object
required: required:
- id - id
- organization_id - organization_id
- cookie_banner_id - cookie_banner_id
- cookie_category_id - cookie_category_id
- tracker_type
- pattern - pattern
- match_type - match_type
- display_name - display_name
@@ -9150,7 +9151,7 @@ components:
properties: properties:
id: id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Cookie pattern ID description: Tracker pattern ID
organization_id: organization_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Organization ID description: Organization ID
@@ -9160,9 +9161,13 @@ components:
cookie_category_id: cookie_category_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Cookie category ID description: Cookie category ID
tracker_type:
type: string
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, PIXEL]
description: Type of tracker
pattern: pattern:
type: string type: string
description: Cookie name pattern description: Tracker name pattern
match_type: match_type:
type: string type: string
enum: [EXACT, PREFIX] enum: [EXACT, PREFIX]
@@ -9630,7 +9635,7 @@ components:
cookie_category: cookie_category:
$ref: "#/components/schemas/CookieCategory" $ref: "#/components/schemas/CookieCategory"
ListCookiePatternsInput: ListTrackerPatternsInput:
type: object type: object
required: required:
- cookie_category_id - cookie_category_id
@@ -9642,19 +9647,19 @@ components:
cursor: cursor:
$ref: "#/components/schemas/CursorKey" $ref: "#/components/schemas/CursorKey"
ListCookiePatternsOutput: ListTrackerPatternsOutput:
type: object type: object
required: required:
- cookie_patterns - tracker_patterns
properties: properties:
next_cursor: next_cursor:
$ref: "#/components/schemas/CursorKey" $ref: "#/components/schemas/CursorKey"
cookie_patterns: tracker_patterns:
type: array type: array
items: items:
$ref: "#/components/schemas/CookiePattern" $ref: "#/components/schemas/TrackerPattern"
GetCookiePatternInput: GetTrackerPatternInput:
type: object type: object
required: required:
- id - id
@@ -9662,18 +9667,19 @@ components:
id: id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
GetCookiePatternOutput: GetTrackerPatternOutput:
type: object type: object
required: required:
- cookie_pattern - tracker_pattern
properties: properties:
cookie_pattern: tracker_pattern:
$ref: "#/components/schemas/CookiePattern" $ref: "#/components/schemas/TrackerPattern"
AddCookiePatternInput: AddTrackerPatternInput:
type: object type: object
required: required:
- cookie_category_id - cookie_category_id
- tracker_type
- pattern - pattern
- match_type - match_type
- display_name - display_name
@@ -9681,6 +9687,9 @@ components:
properties: properties:
cookie_category_id: cookie_category_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
tracker_type:
type: string
enum: [COOKIE, LOCAL_STORAGE, SESSION_STORAGE, PIXEL]
pattern: pattern:
type: string type: string
match_type: match_type:
@@ -9693,15 +9702,15 @@ components:
description: description:
type: string type: string
AddCookiePatternOutput: AddTrackerPatternOutput:
type: object type: object
required: required:
- cookie_pattern - tracker_pattern
properties: properties:
cookie_pattern: tracker_pattern:
$ref: "#/components/schemas/CookiePattern" $ref: "#/components/schemas/TrackerPattern"
UpdateCookiePatternInput: UpdateTrackerPatternInput:
type: object type: object
required: required:
- id - id
@@ -9729,15 +9738,15 @@ components:
- "null" - "null"
go.probo.inc/mcpgen/omittable: true go.probo.inc/mcpgen/omittable: true
UpdateCookiePatternOutput: UpdateTrackerPatternOutput:
type: object type: object
required: required:
- cookie_pattern - tracker_pattern
properties: properties:
cookie_pattern: tracker_pattern:
$ref: "#/components/schemas/CookiePattern" $ref: "#/components/schemas/TrackerPattern"
DeleteCookiePatternInput: DeleteTrackerPatternInput:
type: object type: object
required: required:
- id - id
@@ -9745,7 +9754,7 @@ components:
id: id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
DeleteCookiePatternOutput: DeleteTrackerPatternOutput:
type: object type: object
required: required:
- deleted_id - deleted_id
@@ -9753,24 +9762,24 @@ components:
deleted_id: deleted_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
MoveCookiePatternToCategoryInput: MoveTrackerPatternToCategoryInput:
type: object type: object
required: required:
- cookie_pattern_id - tracker_pattern_id
- target_cookie_category_id - target_cookie_category_id
properties: properties:
cookie_pattern_id: tracker_pattern_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
target_cookie_category_id: target_cookie_category_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
MoveCookiePatternToCategoryOutput: MoveTrackerPatternToCategoryOutput:
type: object type: object
required: required:
- cookie_pattern - tracker_pattern
properties: properties:
cookie_pattern: tracker_pattern:
$ref: "#/components/schemas/CookiePattern" $ref: "#/components/schemas/TrackerPattern"
PublishCookieBannerVersionInput: PublishCookieBannerVersionInput:
type: object type: object
@@ -12019,57 +12028,57 @@ tools:
$ref: "#/components/schemas/ReorderCookieCategoryInput" $ref: "#/components/schemas/ReorderCookieCategoryInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/ReorderCookieCategoryOutput" $ref: "#/components/schemas/ReorderCookieCategoryOutput"
- name: listCookiePatterns - name: listTrackerPatterns
description: List all cookie patterns for a category description: List all tracker patterns for a category
hints: hints:
readonly: true readonly: true
idempotent: true idempotent: true
inputSchema: inputSchema:
$ref: "#/components/schemas/ListCookiePatternsInput" $ref: "#/components/schemas/ListTrackerPatternsInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/ListCookiePatternsOutput" $ref: "#/components/schemas/ListTrackerPatternsOutput"
- name: getCookiePattern - name: getTrackerPattern
description: Get a cookie pattern by ID description: Get a tracker pattern by ID
hints: hints:
readonly: true readonly: true
idempotent: true idempotent: true
inputSchema: inputSchema:
$ref: "#/components/schemas/GetCookiePatternInput" $ref: "#/components/schemas/GetTrackerPatternInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/GetCookiePatternOutput" $ref: "#/components/schemas/GetTrackerPatternOutput"
- name: addCookiePattern - name: addTrackerPattern
description: Create a new cookie pattern for a category description: Create a new tracker pattern for a category
hints: hints:
readonly: false readonly: false
inputSchema: inputSchema:
$ref: "#/components/schemas/AddCookiePatternInput" $ref: "#/components/schemas/AddTrackerPatternInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/AddCookiePatternOutput" $ref: "#/components/schemas/AddTrackerPatternOutput"
- name: updateCookiePattern - name: updateTrackerPattern
description: Update an existing cookie pattern description: Update an existing tracker pattern
hints: hints:
readonly: false readonly: false
inputSchema: inputSchema:
$ref: "#/components/schemas/UpdateCookiePatternInput" $ref: "#/components/schemas/UpdateTrackerPatternInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/UpdateCookiePatternOutput" $ref: "#/components/schemas/UpdateTrackerPatternOutput"
- name: deleteCookiePattern - name: deleteTrackerPattern
description: Delete a cookie pattern description: Delete a tracker pattern
hints: hints:
readonly: false readonly: false
destructive: true destructive: true
inputSchema: inputSchema:
$ref: "#/components/schemas/DeleteCookiePatternInput" $ref: "#/components/schemas/DeleteTrackerPatternInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/DeleteCookiePatternOutput" $ref: "#/components/schemas/DeleteTrackerPatternOutput"
- name: moveCookiePatternToCategory - name: moveTrackerPatternToCategory
description: Move a cookie pattern to a different category description: Move a tracker pattern to a different category
hints: hints:
readonly: false readonly: false
inputSchema: inputSchema:
$ref: "#/components/schemas/MoveCookiePatternToCategoryInput" $ref: "#/components/schemas/MoveTrackerPatternToCategoryInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/MoveCookiePatternToCategoryOutput" $ref: "#/components/schemas/MoveTrackerPatternToCategoryOutput"
- name: publishCookieBannerVersion - name: publishCookieBannerVersion
description: Publish the current draft version of a cookie banner description: Publish the current draft version of a cookie banner
hints: hints:

View File

@@ -19,18 +19,24 @@ import (
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
func NewCookiePattern(p *coredata.CookiePattern) *CookiePattern { func NewTrackerPattern(p *coredata.TrackerPattern) *TrackerPattern {
return &CookiePattern{ var source TrackerPatternSource
if p.Source != nil {
source = TrackerPatternSource(*p.Source)
}
return &TrackerPattern{
ID: p.ID, ID: p.ID,
OrganizationID: p.OrganizationID, OrganizationID: p.OrganizationID,
CookieBannerID: p.CookieBannerID, CookieBannerID: p.CookieBannerID,
CookieCategoryID: p.CookieCategoryID, CookieCategoryID: p.CookieCategoryID,
TrackerType: TrackerPatternTrackerType(p.TrackerType),
Pattern: p.Pattern, Pattern: p.Pattern,
MatchType: CookiePatternMatchType(p.MatchType), MatchType: TrackerPatternMatchType(p.MatchType),
DisplayName: p.DisplayName, DisplayName: p.DisplayName,
MaxAgeSeconds: p.MaxAgeSeconds, MaxAgeSeconds: p.MaxAgeSeconds,
Description: p.Description, Description: p.Description,
Source: CookiePatternSource(p.Source), Source: source,
Excluded: p.Excluded, Excluded: p.Excluded,
LastMatchedAt: p.LastMatchedAt, LastMatchedAt: p.LastMatchedAt,
CreatedAt: p.CreatedAt, CreatedAt: p.CreatedAt,
@@ -38,10 +44,10 @@ func NewCookiePattern(p *coredata.CookiePattern) *CookiePattern {
} }
} }
func NewListCookiePatternsOutput(pg *page.Page[*coredata.CookiePattern, coredata.CookiePatternOrderField]) ListCookiePatternsOutput { func NewListTrackerPatternsOutput(pg *page.Page[*coredata.TrackerPattern, coredata.TrackerPatternOrderField]) ListTrackerPatternsOutput {
patterns := make([]*CookiePattern, 0, len(pg.Data)) patterns := make([]*TrackerPattern, 0, len(pg.Data))
for _, p := range pg.Data { for _, p := range pg.Data {
patterns = append(patterns, NewCookiePattern(p)) patterns = append(patterns, NewTrackerPattern(p))
} }
var nextCursor *page.CursorKey var nextCursor *page.CursorKey
@@ -50,8 +56,8 @@ func NewListCookiePatternsOutput(pg *page.Page[*coredata.CookiePattern, coredata
nextCursor = &cursorKey nextCursor = &cursorKey
} }
return ListCookiePatternsOutput{ return ListTrackerPatternsOutput{
NextCursor: nextCursor, NextCursor: nextCursor,
CookiePatterns: patterns, TrackerPatterns: patterns,
} }
} }