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()
const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) {
updateCookiePattern(input: $input) {
cookiePattern { id excluded }
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) {
trackerPattern { id excluded }
}
}
`
@@ -114,10 +114,10 @@ func setPatternExcluded(t *testing.T, c *testutil.Client, patternID string, excl
var result struct{}
require.NoError(t, c.Execute(query, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"excluded": excluded,
"trackerPatternId": patternID,
"excluded": excluded,
},
}, &result), "updateCookiePattern excluded mutation failed")
}, &result), "updateTrackerPattern excluded mutation failed")
}
// 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)
})
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()
owner := testutil.NewClient(t, testutil.RoleOwner)
bannerID := factory.CreateCookieBanner(owner)
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",
"description": "Original description",
})
@@ -338,17 +338,17 @@ func TestCookieBannerVersioning_NoOpUpdates(t *testing.T) {
baseline := published.Version
const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) {
updateCookiePattern(input: $input) { cookiePattern { id } }
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) { trackerPattern { id } }
}
`
var result struct{}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"displayName": "GA Tracker",
"description": "Original description",
"trackerPatternId": patternID,
"displayName": "GA Tracker",
"description": "Original description",
},
}, &result)
require.NoError(t, err)
@@ -368,7 +368,7 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
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",
})
@@ -377,32 +377,32 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
baseline := published.Version
const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) {
updateCookiePattern(input: $input) {
cookiePattern { id displayName description }
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) {
trackerPattern { id displayName description }
}
}
`
var result struct {
UpdateCookiePattern struct {
CookiePattern struct {
UpdateTrackerPattern struct {
TrackerPattern struct {
DisplayName string `json:"displayName"`
Description string `json:"description"`
} `json:"cookiePattern"`
} `json:"updateCookiePattern"`
} `json:"trackerPattern"`
} `json:"updateTrackerPattern"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"displayName": "Renamed Excluded",
"description": "Now with notes",
"trackerPatternId": patternID,
"displayName": "Renamed Excluded",
"description": "Now with notes",
},
}, &result)
require.NoError(t, err)
assert.Equal(t, "Renamed Excluded", result.UpdateCookiePattern.CookiePattern.DisplayName)
assert.Equal(t, "Now with notes", result.UpdateCookiePattern.CookiePattern.Description)
assert.Equal(t, "Renamed Excluded", result.UpdateTrackerPattern.TrackerPattern.DisplayName)
assert.Equal(t, "Now with notes", result.UpdateTrackerPattern.TrackerPattern.Description)
got := latestVersion(t, owner, bannerID)
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)
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)
published := publishBanner(t, owner, bannerID)
baseline := published.Version
const query = `
mutation DeleteCookiePattern($input: DeleteCookiePatternInput!) {
deleteCookiePattern(input: $input) {
deletedCookiePatternId
mutation DeleteTrackerPattern($input: DeleteTrackerPatternInput!) {
deleteTrackerPattern(input: $input) {
deletedTrackerPatternId
}
}
`
var result struct{}
err := owner.Execute(query, map[string]any{
"input": map[string]any{"cookiePatternId": patternID},
"input": map[string]any{"trackerPatternId": patternID},
}, &result)
require.NoError(t, err)
@@ -447,16 +447,16 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryA := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "excl-move-a"})
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)
published := publishBanner(t, owner, bannerID)
baseline := published.Version
const query = `
mutation MoveCookiePatternToCategory($input: MoveCookiePatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) {
cookiePattern { id }
mutation MoveTrackerPatternToCategory($input: MoveTrackerPatternToCategoryInput!) {
moveTrackerPatternToCategory(input: $input) {
trackerPattern { id }
}
}
`
@@ -464,7 +464,7 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
var result struct{}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"trackerPatternId": patternID,
"targetCookieCategoryId": categoryB,
},
}, &result)
@@ -604,13 +604,13 @@ func TestCookieBannerVersioning_RealChangesStillBumpVersion(t *testing.T) {
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()
owner := testutil.NewClient(t, testutil.RoleOwner)
bannerID := factory.CreateCookieBanner(owner)
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",
})
@@ -618,15 +618,15 @@ func TestCookieBannerVersioning_RealChangesStillBumpVersion(t *testing.T) {
baseline := published.Version
const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) {
updateCookiePattern(input: $input) { cookiePattern { id } }
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) { trackerPattern { id } }
}
`
var result struct{}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"displayName": "Renamed",
"trackerPatternId": patternID,
"displayName": "Renamed",
},
}, &result)
require.NoError(t, err)

View File

@@ -23,7 +23,7 @@ import (
"go.probo.inc/probo/e2e/internal/testutil"
)
func TestCookiePattern_Create(t *testing.T) {
func TestTrackerPattern_Create(t *testing.T) {
t.Parallel()
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)
const query = `
mutation CreateCookiePattern($input: CreateCookiePatternInput!) {
createCookiePattern(input: $input) {
cookiePatternEdge {
mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createTrackerPattern(input: $input) {
trackerPatternEdge {
node {
id
pattern
@@ -57,8 +57,8 @@ func TestCookiePattern_Create(t *testing.T) {
`
var result struct {
CreateCookiePattern struct {
CookiePatternEdge struct {
CreateTrackerPattern struct {
TrackerPatternEdge struct {
Node struct {
ID string `json:"id"`
Pattern string `json:"pattern"`
@@ -70,11 +70,11 @@ func TestCookiePattern_Create(t *testing.T) {
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
} `json:"node"`
} `json:"cookiePatternEdge"`
} `json:"trackerPatternEdge"`
CookieBanner struct {
ID string `json:"id"`
} `json:"cookieBanner"`
} `json:"createCookiePattern"`
} `json:"createTrackerPattern"`
}
maxAge := 86400
@@ -90,7 +90,7 @@ func TestCookiePattern_Create(t *testing.T) {
}, &result)
require.NoError(t, err)
node := result.CreateCookiePattern.CookiePatternEdge.Node
node := result.CreateTrackerPattern.TrackerPatternEdge.Node
assert.NotEmpty(t, node.ID)
assert.Equal(t, "_ga", node.Pattern)
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, "Google Analytics tracking cookie", node.Description)
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) {
@@ -110,9 +110,9 @@ func TestCookiePattern_Create(t *testing.T) {
categoryID := factory.CreateCookieCategory(owner, bannerID)
const query = `
mutation CreateCookiePattern($input: CreateCookiePatternInput!) {
createCookiePattern(input: $input) {
cookiePatternEdge {
mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createTrackerPattern(input: $input) {
trackerPatternEdge {
node {
id
pattern
@@ -126,8 +126,8 @@ func TestCookiePattern_Create(t *testing.T) {
`
var result struct {
CreateCookiePattern struct {
CookiePatternEdge struct {
CreateTrackerPattern struct {
TrackerPatternEdge struct {
Node struct {
ID string `json:"id"`
Pattern string `json:"pattern"`
@@ -135,8 +135,8 @@ func TestCookiePattern_Create(t *testing.T) {
DisplayName string `json:"displayName"`
MaxAgeSeconds *int `json:"maxAgeSeconds"`
} `json:"node"`
} `json:"cookiePatternEdge"`
} `json:"createCookiePattern"`
} `json:"trackerPatternEdge"`
} `json:"createTrackerPattern"`
}
err := owner.Execute(query, map[string]any{
@@ -150,7 +150,7 @@ func TestCookiePattern_Create(t *testing.T) {
}, &result)
require.NoError(t, err)
node := result.CreateCookiePattern.CookiePatternEdge.Node
node := result.CreateTrackerPattern.TrackerPatternEdge.Node
assert.Equal(t, "_gat_", node.Pattern)
assert.Equal(t, "PREFIX", node.MatchType)
assert.Nil(t, node.MaxAgeSeconds)
@@ -163,15 +163,15 @@ func TestCookiePattern_Create(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID)
factory.CreateCookiePattern(owner, categoryID, factory.Attrs{
factory.CreateTrackerPattern(owner, categoryID, factory.Attrs{
"pattern": "duplicate_cookie",
"displayName": "First",
})
_, err := owner.Do(`
mutation CreateCookiePattern($input: CreateCookiePatternInput!) {
createCookiePattern(input: $input) {
cookiePatternEdge { node { id } }
mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createTrackerPattern(input: $input) {
trackerPatternEdge { node { 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.Run("update displayName and description", func(t *testing.T) {
@@ -197,15 +197,15 @@ func TestCookiePattern_Update(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID, factory.Attrs{
patternID := factory.CreateTrackerPattern(owner, categoryID, factory.Attrs{
"displayName": "Original Name",
"description": "Original description",
})
const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) {
updateCookiePattern(input: $input) {
cookiePattern {
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) {
trackerPattern {
id
displayName
description
@@ -218,31 +218,31 @@ func TestCookiePattern_Update(t *testing.T) {
`
var result struct {
UpdateCookiePattern struct {
CookiePattern struct {
UpdateTrackerPattern struct {
TrackerPattern struct {
ID string `json:"id"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
} `json:"cookiePattern"`
} `json:"trackerPattern"`
CookieBanner struct {
ID string `json:"id"`
} `json:"cookieBanner"`
} `json:"updateCookiePattern"`
} `json:"updateTrackerPattern"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"displayName": "Updated Name",
"description": "Updated description",
"trackerPatternId": patternID,
"displayName": "Updated Name",
"description": "Updated description",
},
}, &result)
require.NoError(t, err)
assert.Equal(t, patternID, result.UpdateCookiePattern.CookiePattern.ID)
assert.Equal(t, "Updated Name", result.UpdateCookiePattern.CookiePattern.DisplayName)
assert.Equal(t, "Updated description", result.UpdateCookiePattern.CookiePattern.Description)
assert.Equal(t, bannerID, result.UpdateCookiePattern.CookieBanner.ID)
assert.Equal(t, patternID, result.UpdateTrackerPattern.TrackerPattern.ID)
assert.Equal(t, "Updated Name", result.UpdateTrackerPattern.TrackerPattern.DisplayName)
assert.Equal(t, "Updated description", result.UpdateTrackerPattern.TrackerPattern.Description)
assert.Equal(t, bannerID, result.UpdateTrackerPattern.CookieBanner.ID)
})
t.Run("update maxAgeSeconds", func(t *testing.T) {
@@ -251,12 +251,12 @@ func TestCookiePattern_Update(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID)
patternID := factory.CreateTrackerPattern(owner, categoryID)
const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) {
updateCookiePattern(input: $input) {
cookiePattern {
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) {
trackerPattern {
id
maxAgeSeconds
}
@@ -265,28 +265,28 @@ func TestCookiePattern_Update(t *testing.T) {
`
var result struct {
UpdateCookiePattern struct {
CookiePattern struct {
UpdateTrackerPattern struct {
TrackerPattern struct {
ID string `json:"id"`
MaxAgeSeconds *int `json:"maxAgeSeconds"`
} `json:"cookiePattern"`
} `json:"updateCookiePattern"`
} `json:"trackerPattern"`
} `json:"updateTrackerPattern"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"maxAgeSeconds": 7200,
"trackerPatternId": patternID,
"maxAgeSeconds": 7200,
},
}, &result)
require.NoError(t, err)
require.NotNil(t, result.UpdateCookiePattern.CookiePattern.MaxAgeSeconds)
assert.Equal(t, 7200, *result.UpdateCookiePattern.CookiePattern.MaxAgeSeconds)
require.NotNil(t, result.UpdateTrackerPattern.TrackerPattern.MaxAgeSeconds)
assert.Equal(t, 7200, *result.UpdateTrackerPattern.TrackerPattern.MaxAgeSeconds)
})
}
func TestCookiePattern_Excluded(t *testing.T) {
func TestTrackerPattern_Excluded(t *testing.T) {
t.Parallel()
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)
const query = `
mutation CreateCookiePattern($input: CreateCookiePatternInput!) {
createCookiePattern(input: $input) {
cookiePatternEdge {
mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createTrackerPattern(input: $input) {
trackerPatternEdge {
node {
id
excluded
@@ -310,14 +310,14 @@ func TestCookiePattern_Excluded(t *testing.T) {
`
var result struct {
CreateCookiePattern struct {
CookiePatternEdge struct {
CreateTrackerPattern struct {
TrackerPatternEdge struct {
Node struct {
ID string `json:"id"`
Excluded bool `json:"excluded"`
} `json:"node"`
} `json:"cookiePatternEdge"`
} `json:"createCookiePattern"`
} `json:"trackerPatternEdge"`
} `json:"createTrackerPattern"`
}
err := owner.Execute(query, map[string]any{
@@ -331,7 +331,7 @@ func TestCookiePattern_Excluded(t *testing.T) {
}, &result)
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) {
@@ -340,12 +340,12 @@ func TestCookiePattern_Excluded(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID)
patternID := factory.CreateTrackerPattern(owner, categoryID)
const query = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) {
updateCookiePattern(input: $input) {
cookiePattern {
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) {
trackerPattern {
id
excluded
}
@@ -354,23 +354,23 @@ func TestCookiePattern_Excluded(t *testing.T) {
`
var result struct {
UpdateCookiePattern struct {
CookiePattern struct {
UpdateTrackerPattern struct {
TrackerPattern struct {
ID string `json:"id"`
Excluded bool `json:"excluded"`
} `json:"cookiePattern"`
} `json:"updateCookiePattern"`
} `json:"trackerPattern"`
} `json:"updateTrackerPattern"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"excluded": true,
"trackerPatternId": patternID,
"excluded": true,
},
}, &result)
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) {
@@ -379,12 +379,12 @@ func TestCookiePattern_Excluded(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID)
patternID := factory.CreateTrackerPattern(owner, categoryID)
const updateQuery = `
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) {
updateCookiePattern(input: $input) {
cookiePattern {
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) {
trackerPattern {
id
excluded
}
@@ -393,35 +393,35 @@ func TestCookiePattern_Excluded(t *testing.T) {
`
var result struct {
UpdateCookiePattern struct {
CookiePattern struct {
UpdateTrackerPattern struct {
TrackerPattern struct {
ID string `json:"id"`
Excluded bool `json:"excluded"`
} `json:"cookiePattern"`
} `json:"updateCookiePattern"`
} `json:"trackerPattern"`
} `json:"updateTrackerPattern"`
}
err := owner.Execute(updateQuery, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"excluded": true,
"trackerPatternId": patternID,
"excluded": true,
},
}, &result)
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{
"input": map[string]any{
"cookiePatternId": patternID,
"excluded": false,
"trackerPatternId": patternID,
"excluded": false,
},
}, &result)
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.Run("success", func(t *testing.T) {
@@ -430,12 +430,12 @@ func TestCookiePattern_Delete(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID)
patternID := factory.CreateTrackerPattern(owner, categoryID)
const query = `
mutation DeleteCookiePattern($input: DeleteCookiePatternInput!) {
deleteCookiePattern(input: $input) {
deletedCookiePatternId
mutation DeleteTrackerPattern($input: DeleteTrackerPatternInput!) {
deleteTrackerPattern(input: $input) {
deletedTrackerPatternId
cookieBanner {
id
}
@@ -444,25 +444,25 @@ func TestCookiePattern_Delete(t *testing.T) {
`
var result struct {
DeleteCookiePattern struct {
DeletedCookiePatternID string `json:"deletedCookiePatternId"`
CookieBanner struct {
DeleteTrackerPattern struct {
DeletedTrackerPatternID string `json:"deletedTrackerPatternId"`
CookieBanner struct {
ID string `json:"id"`
} `json:"cookieBanner"`
} `json:"deleteCookiePattern"`
} `json:"deleteTrackerPattern"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{"cookiePatternId": patternID},
"input": map[string]any{"trackerPatternId": patternID},
}, &result)
require.NoError(t, err)
assert.Equal(t, patternID, result.DeleteCookiePattern.DeletedCookiePatternID)
assert.Equal(t, bannerID, result.DeleteCookiePattern.CookieBanner.ID)
assert.Equal(t, patternID, result.DeleteTrackerPattern.DeletedTrackerPatternID)
assert.Equal(t, bannerID, result.DeleteTrackerPattern.CookieBanner.ID)
})
}
func TestCookiePattern_MoveToCategory(t *testing.T) {
func TestTrackerPattern_MoveToCategory(t *testing.T) {
t.Parallel()
t.Run("success", func(t *testing.T) {
@@ -472,12 +472,12 @@ func TestCookiePattern_MoveToCategory(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryA := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "cat-a-move"})
categoryB := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "cat-b-move"})
patternID := factory.CreateCookiePattern(owner, categoryA)
patternID := factory.CreateTrackerPattern(owner, categoryA)
const query = `
mutation MoveCookiePatternToCategory($input: MoveCookiePatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) {
cookiePattern {
mutation MoveTrackerPatternToCategory($input: MoveTrackerPatternToCategoryInput!) {
moveTrackerPatternToCategory(input: $input) {
trackerPattern {
id
cookieCategory {
id
@@ -491,30 +491,30 @@ func TestCookiePattern_MoveToCategory(t *testing.T) {
`
var result struct {
MoveCookiePatternToCategory struct {
CookiePattern struct {
MoveTrackerPatternToCategory struct {
TrackerPattern struct {
ID string `json:"id"`
CookieCategory struct {
ID string `json:"id"`
} `json:"cookieCategory"`
} `json:"cookiePattern"`
} `json:"trackerPattern"`
CookieBanner struct {
ID string `json:"id"`
} `json:"cookieBanner"`
} `json:"moveCookiePatternToCategory"`
} `json:"moveTrackerPatternToCategory"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"trackerPatternId": patternID,
"targetCookieCategoryId": categoryB,
},
}, &result)
require.NoError(t, err)
assert.Equal(t, patternID, result.MoveCookiePatternToCategory.CookiePattern.ID)
assert.Equal(t, categoryB, result.MoveCookiePatternToCategory.CookiePattern.CookieCategory.ID)
assert.Equal(t, bannerID, result.MoveCookiePatternToCategory.CookieBanner.ID)
assert.Equal(t, patternID, result.MoveTrackerPatternToCategory.TrackerPattern.ID)
assert.Equal(t, categoryB, result.MoveTrackerPatternToCategory.TrackerPattern.CookieCategory.ID)
assert.Equal(t, bannerID, result.MoveTrackerPatternToCategory.CookieBanner.ID)
})
t.Run("cross-banner mismatch error", func(t *testing.T) {
@@ -525,18 +525,18 @@ func TestCookiePattern_MoveToCategory(t *testing.T) {
banner2 := factory.CreateCookieBanner(owner)
category1 := factory.CreateCookieCategory(owner, banner1, factory.Attrs{"slug": "cat-x-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(`
mutation MoveCookiePatternToCategory($input: MoveCookiePatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) {
cookiePattern { id }
mutation MoveTrackerPatternToCategory($input: MoveTrackerPatternToCategoryInput!) {
moveTrackerPatternToCategory(input: $input) {
trackerPattern { id }
cookieBanner { id }
}
}
`, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"trackerPatternId": patternID,
"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.Run("via category cookiePatterns connection", func(t *testing.T) {
t.Run("via category trackerPatterns connection", func(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID)
factory.CreateCookiePattern(owner, categoryID)
factory.CreateCookiePattern(owner, categoryID)
factory.CreateTrackerPattern(owner, categoryID)
factory.CreateTrackerPattern(owner, categoryID)
const query = `
query($id: ID!) {
node(id: $id) {
... on CookieCategory {
cookiePatterns(first: 10) {
trackerPatterns(first: 10) {
totalCount
edges {
node {
@@ -581,7 +581,7 @@ func TestCookiePattern_List(t *testing.T) {
var result struct {
Node struct {
CookiePatterns struct {
TrackerPatterns struct {
TotalCount int `json:"totalCount"`
Edges []struct {
Node struct {
@@ -594,18 +594,18 @@ func TestCookiePattern_List(t *testing.T) {
HasNextPage bool `json:"hasNextPage"`
HasPreviousPage bool `json:"hasPreviousPage"`
} `json:"pageInfo"`
} `json:"cookiePatterns"`
} `json:"trackerPatterns"`
} `json:"node"`
}
err := owner.Execute(query, map[string]any{"id": categoryID}, &result)
require.NoError(t, err)
assert.Equal(t, 2, result.Node.CookiePatterns.TotalCount)
assert.Len(t, result.Node.CookiePatterns.Edges, 2)
assert.Equal(t, 2, result.Node.TrackerPatterns.TotalCount)
assert.Len(t, result.Node.TrackerPatterns.Edges, 2)
})
}
func TestCookiePattern_RBAC(t *testing.T) {
func TestTrackerPattern_RBAC(t *testing.T) {
t.Parallel()
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)
_, err := viewer.Do(`
mutation CreateCookiePattern($input: CreateCookiePatternInput!) {
createCookiePattern(input: $input) {
cookiePatternEdge { node { id } }
mutation CreateTrackerPattern($input: CreateTrackerPatternInput!) {
createTrackerPattern(input: $input) {
trackerPatternEdge { node { id } }
cookieBanner { id }
}
}
@@ -632,7 +632,7 @@ func TestCookiePattern_RBAC(t *testing.T) {
"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) {
@@ -642,22 +642,22 @@ func TestCookiePattern_RBAC(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID)
patternID := factory.CreateTrackerPattern(owner, categoryID)
_, err := viewer.Do(`
mutation UpdateCookiePattern($input: UpdateCookiePatternInput!) {
updateCookiePattern(input: $input) {
cookiePattern { id }
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) {
trackerPattern { id }
cookieBanner { id }
}
}
`, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"displayName": "Updated by Viewer",
"trackerPatternId": patternID,
"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) {
@@ -667,19 +667,19 @@ func TestCookiePattern_RBAC(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryID := factory.CreateCookieCategory(owner, bannerID)
patternID := factory.CreateCookiePattern(owner, categoryID)
patternID := factory.CreateTrackerPattern(owner, categoryID)
_, err := viewer.Do(`
mutation DeleteCookiePattern($input: DeleteCookiePatternInput!) {
deleteCookiePattern(input: $input) {
deletedCookiePatternId
mutation DeleteTrackerPattern($input: DeleteTrackerPatternInput!) {
deleteTrackerPattern(input: $input) {
deletedTrackerPatternId
cookieBanner { id }
}
}
`, 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) {
@@ -690,21 +690,21 @@ func TestCookiePattern_RBAC(t *testing.T) {
bannerID := factory.CreateCookieBanner(owner)
categoryA := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "rbac-move-a"})
categoryB := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "rbac-move-b"})
patternID := factory.CreateCookiePattern(owner, categoryA)
patternID := factory.CreateTrackerPattern(owner, categoryA)
_, err := viewer.Do(`
mutation MoveCookiePatternToCategory($input: MoveCookiePatternToCategoryInput!) {
moveCookiePatternToCategory(input: $input) {
cookiePattern { id }
mutation MoveTrackerPatternToCategory($input: MoveTrackerPatternToCategoryInput!) {
moveTrackerPatternToCategory(input: $input) {
trackerPattern { id }
cookieBanner { id }
}
}
`, map[string]any{
"input": map[string]any{
"cookiePatternId": patternID,
"trackerPatternId": patternID,
"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
}
func CreateCookiePattern(c *testutil.Client, categoryID string, attrs ...Attrs) string {
func CreateTrackerPattern(c *testutil.Client, categoryID string, attrs ...Attrs) string {
c.T.Helper()
var a Attrs
@@ -1352,9 +1352,9 @@ func CreateCookiePattern(c *testutil.Client, categoryID string, attrs ...Attrs)
}
const query = `
mutation($input: CreateCookiePatternInput!) {
createCookiePattern(input: $input) {
cookiePatternEdge {
mutation($input: CreateTrackerPatternInput!) {
createTrackerPattern(input: $input) {
trackerPatternEdge {
node { id }
}
}
@@ -1363,27 +1363,28 @@ func CreateCookiePattern(c *testutil.Client, categoryID string, attrs ...Attrs)
input := map[string]any{
"cookieCategoryId": categoryID,
"trackerType": a.getString("trackerType", "COOKIE"),
"pattern": a.getString("pattern", gofakeit.LetterN(8)+"_cookie"),
"matchType": a.getString("matchType", "EXACT"),
"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 {
input["maxAgeSeconds"] = a.getInt("maxAgeSeconds", 0)
}
var result struct {
CreateCookiePattern struct {
CookiePatternEdge struct {
CreateTrackerPattern struct {
TrackerPatternEdge struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"cookiePatternEdge"`
} `json:"createCookiePattern"`
} `json:"trackerPatternEdge"`
} `json:"createTrackerPattern"`
}
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',
description: 'View cookie consent records',
},
{
name: 'Cookie Pattern',
value: 'cookiePattern',
description: 'Manage cookie patterns',
},
{
name: 'Data',
value: 'datum',
@@ -206,6 +201,11 @@ export class Probo implements INodeType {
value: 'tia',
description: 'Manage transfer impact assessments',
},
{
name: 'Tracker Pattern',
value: 'trackerPattern',
description: 'Manage tracker patterns',
},
{
name: 'Trust Center',
value: 'trustCenter',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -31,7 +31,6 @@ import (
"go.probo.inc/probo/pkg/cmd/control"
cookiebanner "go.probo.inc/probo/pkg/cmd/cookie-banner"
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/document"
"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/task"
"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"
"go.probo.inc/probo/pkg/cmd/user"
"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(cookiebanner.NewCmdCookieBanner(f))
cmd.AddCommand(cookiecategory.NewCmdCookieCategory(f))
cmd.AddCommand(cookiepattern.NewCmdCookiePattern(f))
cmd.AddCommand(trackerpattern.NewCmdTrackerPattern(f))
cmd.AddCommand(datum.NewCmdDatum(f))
cmd.AddCommand(document.NewCmdDocument(f))
cmd.AddCommand(dpia.NewCmdDPIA(f))

View File

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

View File

@@ -24,9 +24,9 @@ import (
)
const deleteMutation = `
mutation($input: DeleteCookiePatternInput!) {
deleteCookiePattern(input: $input) {
deletedCookiePatternId
mutation($input: DeleteTrackerPatternInput!) {
deleteTrackerPattern(input: $input) {
deletedTrackerPatternId
cookieBanner {
id
}
@@ -39,15 +39,15 @@ func NewCmdDelete(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "delete <id>",
Short: "Delete a cookie pattern",
Short: "Delete a tracker pattern",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if !flagYes {
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
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
}
if !confirmed {
@@ -74,13 +74,13 @@ func NewCmdDelete(f *cmdutil.Factory) *cobra.Command {
)
_, err = client.Do(deleteMutation, map[string]any{
"input": map[string]any{"cookiePatternId": args[0]},
"input": map[string]any{"trackerPatternId": args[0]},
})
if err != nil {
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
},

View File

@@ -28,13 +28,14 @@ query($id: ID!, $first: Int, $after: CursorKey) {
node(id: $id) {
__typename
... on CookieCategory {
cookiePatterns(first: $first, after: $after) {
trackerPatterns(first: $first, after: $after) {
totalCount
edges {
node {
id
pattern
matchType
trackerType
displayName
source
excluded
@@ -51,10 +52,11 @@ query($id: ID!, $first: Int, $after: CursorKey) {
}
`
type cookiePattern struct {
type trackerPattern struct {
ID string `json:"id"`
Pattern string `json:"pattern"`
MatchType string `json:"matchType"`
TrackerType string `json:"trackerType"`
DisplayName string `json:"displayName"`
Source string `json:"source"`
Excluded bool `json:"excluded"`
@@ -70,7 +72,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List cookie patterns in a category",
Short: "List tracker patterns in a category",
Aliases: []string{"ls"},
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
@@ -103,11 +105,11 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
listQuery,
variables,
flagLimit,
func(data json.RawMessage) (*api.Connection[cookiePattern], error) {
func(data json.RawMessage) (*api.Connection[trackerPattern], error) {
var resp struct {
Node *struct {
Typename string `json:"__typename"`
CookiePatterns api.Connection[cookiePattern] `json:"cookiePatterns"`
Typename string `json:"__typename"`
TrackerPatterns api.Connection[trackerPattern] `json:"trackerPatterns"`
} `json:"node"`
}
if err := json.Unmarshal(data, &resp); err != nil {
@@ -119,7 +121,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
if resp.Node.Typename != "CookieCategory" {
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 {
@@ -131,7 +133,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
}
if len(patterns) == 0 {
_, _ = fmt.Fprintln(f.IOStreams.Out, "No cookie patterns found.")
_, _ = fmt.Fprintln(f.IOStreams.Out, "No tracker patterns found.")
return nil
}
@@ -145,14 +147,14 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
if p.LastMatchedAt != nil {
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)
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

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,7 +32,7 @@ var (
ErrCookieNotFound = errors.New("cookie not found")
ErrCategoriesBannerMismatch = errors.New("source and target categories belong to different banners")
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")
ErrSamePatternCategoryMove = errors.New("source and target cookie categories must be different")
)

View File

@@ -84,33 +84,6 @@ type (
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 {
CookieBannerID gid.GID
Version int
@@ -159,6 +132,17 @@ type (
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 {
TrackerPatternID gid.GID
DisplayName *string
@@ -266,46 +250,6 @@ func (r *ReorderCookieCategoryRequest) Validate() 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 {
v := validator.New()
@@ -373,6 +317,27 @@ func (r *UpsertCookieBannerTranslationRequest) Validate() 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 {
u, err := url.Parse(raw)
if err != nil {
@@ -473,7 +438,7 @@ func (s *Service) ensureDraftVersionForBanner(
tx,
scope,
bannerID,
coredata.NewCookiePatternFilter(nil, nil, new(false)),
coredata.NewTrackerPatternFilter(nil, nil, new(false)),
nil,
); err != nil {
return nil, fmt.Errorf("cannot load tracker patterns: %w", err)
@@ -550,17 +515,18 @@ func (s *Service) CreateCookieBanner(
if dc.Kind == coredata.CookieCategoryKindNecessary {
consentMaxAge := req.ConsentExpiryDays * 86400
consentPattern := &coredata.CookiePattern{
ID: gid.New(scope.GetTenantID(), coredata.CookiePatternEntityType),
consentPattern := &coredata.TrackerPattern{
ID: gid.New(scope.GetTenantID(), coredata.TrackerPatternEntityType),
OrganizationID: banner.OrganizationID,
CookieBannerID: banner.ID,
CookieCategoryID: category.ID,
TrackerType: coredata.TrackerTypeCookie,
Pattern: "probo_consent",
MatchType: coredata.CookiePatternMatchTypeExact,
MatchType: coredata.TrackerPatternMatchTypeExact,
DisplayName: "probo_consent",
MaxAgeSeconds: &consentMaxAge,
Description: "Stores your cookie consent preferences for this website.",
Source: coredata.CookieSourceScript,
Source: new(coredata.CookieSourceScript),
CreatedAt: now,
UpdatedAt: now,
}
@@ -1156,442 +1122,6 @@ func (s *Service) CountCookieCategoriesForBanner(
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(
ctx context.Context,
scope coredata.Scoper,
@@ -1777,9 +1307,9 @@ func (s *Service) DeleteCookieCategory(
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 {
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 {
@@ -2513,7 +2043,7 @@ func (s *Service) reportDetectedTracker(
CookieCategoryID: uncategorisedID,
TrackerType: info.TrackerType,
Pattern: info.Identifier,
MatchType: coredata.CookiePatternMatchTypeExact,
MatchType: coredata.TrackerPatternMatchTypeExact,
DisplayName: info.Identifier,
Description: "",
MaxAgeSeconds: info.MaxAgeSeconds,
@@ -2558,6 +2088,114 @@ func (s *Service) reportDetectedTracker(
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(
ctx context.Context,
scope coredata.Scoper,
@@ -2570,7 +2208,7 @@ func (s *Service) GetTrackerPattern(
func(ctx context.Context, conn pg.Querier) error {
if err := pattern.LoadByID(ctx, conn, scope, trackerPatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound
return ErrTrackerPatternNotFound
}
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 {
if err := pattern.LoadByID(ctx, tx, scope, req.TrackerPatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound
return ErrTrackerPatternNotFound
}
return fmt.Errorf("cannot load tracker pattern: %w", err)
}
@@ -2659,7 +2297,7 @@ func (s *Service) DeleteTrackerPattern(
var pattern coredata.TrackerPattern
if err := pattern.LoadByID(ctx, tx, scope, trackerPatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound
return ErrTrackerPatternNotFound
}
return fmt.Errorf("cannot load tracker pattern: %w", err)
}
@@ -2694,7 +2332,7 @@ func (s *Service) MoveTrackerPatternToCategory(
var pattern coredata.TrackerPattern
if err := pattern.LoadByID(ctx, tx, scope, req.TrackerPatternID); err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) {
return ErrCookiePatternNotFound
return ErrTrackerPatternNotFound
}
return fmt.Errorf("cannot load tracker pattern: %w", err)
}
@@ -2752,8 +2390,8 @@ func (s *Service) ListUncategorisedTrackerPatterns(
ctx context.Context,
scope coredata.Scoper,
bannerID gid.GID,
cursor *page.Cursor[coredata.CookiePatternOrderField],
filter *coredata.CookiePatternFilter,
cursor *page.Cursor[coredata.TrackerPatternOrderField],
filter *coredata.TrackerPatternFilter,
) (coredata.TrackerPatterns, error) {
var patterns coredata.TrackerPatterns
@@ -2778,7 +2416,7 @@ func (s *Service) CountUncategorisedTrackerPatterns(
ctx context.Context,
scope coredata.Scoper,
bannerID gid.GID,
filter *coredata.CookiePatternFilter,
filter *coredata.TrackerPatternFilter,
) (int, error) {
var count int

View File

@@ -100,7 +100,7 @@ func (h *patternAnalysisHandler) Process(ctx context.Context, banner coredata.Co
tx,
scope,
banner.ID,
coredata.NewCookiePatternFilter(new(coredata.CookiePatternMatchTypeExact), nil, new(false)),
coredata.NewTrackerPatternFilter(new(coredata.TrackerPatternMatchTypeExact), nil, new(false)),
nil,
); err != nil {
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,
TrackerType: key.trackerType,
Pattern: key.prefix,
MatchType: coredata.CookiePatternMatchTypePrefix,
MatchType: coredata.TrackerPatternMatchTypePrefix,
DisplayName: key.prefix + "*",
MaxAgeSeconds: maxAge,
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)
}
if prefixPattern.CookieCategoryID != key.categoryID || prefixPattern.MatchType != coredata.CookiePatternMatchTypePrefix {
if prefixPattern.CookieCategoryID != key.categoryID || prefixPattern.MatchType != coredata.TrackerPatternMatchTypePrefix {
continue
}
}
@@ -319,7 +319,7 @@ func (h *patternAnalysisHandler) adoptUncategorisedPatterns(
tx,
scope,
banner.ID,
coredata.NewCookiePatternFilter(new(coredata.CookiePatternMatchTypePrefix), nil, new(false)),
coredata.NewTrackerPatternFilter(new(coredata.TrackerPatternMatchTypePrefix), nil, new(false)),
nil,
); err != nil {
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)
})
exactMatchType := coredata.CookiePatternMatchTypeExact
exactMatchType := coredata.TrackerPatternMatchTypeExact
var uncategorisedExact coredata.TrackerPatterns
if err := uncategorisedExact.LoadAllByCookieBannerID(
ctx,
tx,
scope,
banner.ID,
coredata.NewCookiePatternFilter(&exactMatchType, &uncategorised.ID, new(false)),
coredata.NewTrackerPatternFilter(&exactMatchType, &uncategorised.ID, new(false)),
nil,
); err != nil {
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{
Pattern: name,
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
CookieBannerTranslationEntityType uint16 = 86
AgentRunEntityType uint16 = 87
CookiePatternEntityType uint16 = 88
_ uint16 = 88 // CookiePatternEntityType - removed
TrackerPatternEntityType uint16 = 89
DetectedTrackerEntityType uint16 = 90
)
@@ -282,8 +282,6 @@ func NewEntityFromID(id gid.GID) (any, bool) {
return &CookieBannerTranslation{ID: id}, true
case AgentRunEntityType:
return &AgentRun{ID: id}, true
case CookiePatternEntityType:
return &CookiePattern{ID: id}, true
case TrackerPatternEntityType:
return &TrackerPattern{ID: id}, true
case DetectedTrackerEntityType:

View File

@@ -30,40 +30,40 @@ import (
type (
TrackerPattern 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"`
TrackerType TrackerType `db:"tracker_type"`
Pattern string `db:"pattern"`
MatchType CookiePatternMatchType `db:"match_type"`
DisplayName string `db:"display_name"`
Description string `db:"description"`
Excluded bool `db:"excluded"`
MaxAgeSeconds *int `db:"max_age_seconds"`
Source *CookieSource `db:"source"`
LastMatchedAt *time.Time `db:"last_matched_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
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"`
TrackerType TrackerType `db:"tracker_type"`
Pattern string `db:"pattern"`
MatchType TrackerPatternMatchType `db:"match_type"`
DisplayName string `db:"display_name"`
Description string `db:"description"`
Excluded bool `db:"excluded"`
MaxAgeSeconds *int `db:"max_age_seconds"`
Source *CookieSource `db:"source"`
LastMatchedAt *time.Time `db:"last_matched_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
TrackerPatterns []*TrackerPattern
)
func (tp *TrackerPattern) CursorKey(field CookiePatternOrderField) page.CursorKey {
func (tp *TrackerPattern) CursorKey(field TrackerPatternOrderField) page.CursorKey {
switch field {
case CookiePatternOrderFieldCreatedAt:
case TrackerPatternOrderFieldCreatedAt:
return page.NewCursorKey(tp.ID, tp.CreatedAt)
case CookiePatternOrderFieldName:
case TrackerPatternOrderFieldName:
return page.NewCursorKey(tp.ID, tp.DisplayName)
case CookiePatternOrderFieldLastMatchedAt:
case TrackerPatternOrderFieldLastMatchedAt:
if tp.LastMatchedAt == nil {
return page.NewCursorKey(tp.ID, time.Time{})
}
return page.NewCursorKey(tp.ID, *tp.LastMatchedAt)
case CookiePatternOrderFieldUpdatedAt:
case TrackerPatternOrderFieldUpdatedAt:
return page.NewCursorKey(tp.ID, tp.UpdatedAt)
case CookiePatternOrderFieldSource:
case TrackerPatternOrderFieldSource:
if tp.Source == nil {
return page.NewCursorKey(tp.ID, "")
}
@@ -254,8 +254,8 @@ LIMIT 1;
"cookie_banner_id": cookieBannerID,
"tracker_type": trackerType,
"identifier": identifier,
"match_type_prefix": CookiePatternMatchTypePrefix,
"match_type_exact": CookiePatternMatchTypeExact,
"match_type_prefix": TrackerPatternMatchTypePrefix,
"match_type_exact": TrackerPatternMatchTypeExact,
}
maps.Copy(args, scope.SQLArguments())
@@ -504,7 +504,7 @@ func (tps *TrackerPatterns) LoadAllByCookieBannerID(
conn pg.Querier,
scope Scoper,
cookieBannerID gid.GID,
filter *CookiePatternFilter,
filter *TrackerPatternFilter,
trackerType *TrackerType,
) error {
trackerTypeFragment := "TRUE"
@@ -605,8 +605,8 @@ func (tps *TrackerPatterns) LoadUncategorisedByCookieBannerID(
conn pg.Querier,
scope Scoper,
cookieBannerID gid.GID,
cursor *page.Cursor[CookiePatternOrderField],
filter *CookiePatternFilter,
cursor *page.Cursor[TrackerPatternOrderField],
filter *TrackerPatternFilter,
) error {
q := `
SELECT
@@ -671,7 +671,7 @@ func (tps *TrackerPatterns) CountUncategorisedByCookieBannerID(
conn pg.Querier,
scope Scoper,
cookieBannerID gid.GID,
filter *CookiePatternFilter,
filter *TrackerPatternFilter,
) (int, error) {
q := `
SELECT
@@ -709,3 +709,120 @@ WHERE
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"
)
type CookiePatternFilter struct {
matchType *CookiePatternMatchType
type TrackerPatternFilter struct {
matchType *TrackerPatternMatchType
cookieCategoryID *gid.GID
excluded *bool
query *string
source *CookieSource
}
func NewCookiePatternFilter(
matchType *CookiePatternMatchType,
func NewTrackerPatternFilter(
matchType *TrackerPatternMatchType,
cookieCategoryID *gid.GID,
excluded *bool,
) *CookiePatternFilter {
return &CookiePatternFilter{
) *TrackerPatternFilter {
return &TrackerPatternFilter{
matchType: matchType,
cookieCategoryID: cookieCategoryID,
excluded: excluded,
}
}
func (f *CookiePatternFilter) WithQuery(query *string) *CookiePatternFilter {
func (f *TrackerPatternFilter) WithQuery(query *string) *TrackerPatternFilter {
f.query = query
return f
}
func (f *CookiePatternFilter) WithSource(source *CookieSource) *CookiePatternFilter {
func (f *TrackerPatternFilter) WithSource(source *CookieSource) *TrackerPatternFilter {
f.source = source
return f
}
func (f *CookiePatternFilter) SQLFragment() string {
func (f *TrackerPatternFilter) SQLFragment() string {
if f == nil {
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 {
return pgx.StrictNamedArgs{}
}

View File

@@ -19,25 +19,25 @@ import (
"fmt"
)
type CookiePatternMatchType string
type TrackerPatternMatchType string
const (
CookiePatternMatchTypeExact CookiePatternMatchType = "EXACT"
CookiePatternMatchTypePrefix CookiePatternMatchType = "PREFIX"
TrackerPatternMatchTypeExact TrackerPatternMatchType = "EXACT"
TrackerPatternMatchTypePrefix TrackerPatternMatchType = "PREFIX"
)
func CookiePatternMatchTypes() []CookiePatternMatchType {
return []CookiePatternMatchType{
CookiePatternMatchTypeExact,
CookiePatternMatchTypePrefix,
func TrackerPatternMatchTypes() []TrackerPatternMatchType {
return []TrackerPatternMatchType{
TrackerPatternMatchTypeExact,
TrackerPatternMatchTypePrefix,
}
}
func (m CookiePatternMatchType) String() string {
func (m TrackerPatternMatchType) String() string {
return string(m)
}
func (m *CookiePatternMatchType) Scan(value any) error {
func (m *TrackerPatternMatchType) Scan(value any) error {
var v string
switch val := value.(type) {
case string:
@@ -45,26 +45,26 @@ func (m *CookiePatternMatchType) Scan(value any) error {
case []byte:
v = string(val)
default:
return fmt.Errorf("unsupported type for CookiePatternMatchType: %T", value)
return fmt.Errorf("unsupported type for TrackerPatternMatchType: %T", value)
}
switch CookiePatternMatchType(v) {
case CookiePatternMatchTypeExact:
*m = CookiePatternMatchTypeExact
case CookiePatternMatchTypePrefix:
*m = CookiePatternMatchTypePrefix
switch TrackerPatternMatchType(v) {
case TrackerPatternMatchTypeExact:
*m = TrackerPatternMatchTypeExact
case TrackerPatternMatchTypePrefix:
*m = TrackerPatternMatchTypePrefix
default:
return fmt.Errorf("invalid CookiePatternMatchType value: %q", v)
return fmt.Errorf("invalid TrackerPatternMatchType value: %q", v)
}
return nil
}
func (m CookiePatternMatchType) Value() (driver.Value, error) {
func (m TrackerPatternMatchType) Value() (driver.Value, error) {
switch m {
case CookiePatternMatchTypeExact,
CookiePatternMatchTypePrefix:
case TrackerPatternMatchTypeExact,
TrackerPatternMatchTypePrefix:
return string(m), nil
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"
ActionCookieDelete = "core:cookie:delete"
// CookiePattern actions
ActionCookiePatternGet = "core:cookie-pattern:get"
ActionCookiePatternList = "core:cookie-pattern:list"
ActionCookiePatternCreate = "core:cookie-pattern:create"
ActionCookiePatternUpdate = "core:cookie-pattern:update"
ActionCookiePatternDelete = "core:cookie-pattern:delete"
// TrackerPattern actions
ActionTrackerPatternGet = "core:tracker-pattern:get"
ActionTrackerPatternList = "core:tracker-pattern:list"
ActionTrackerPatternCreate = "core:tracker-pattern:create"
ActionTrackerPatternUpdate = "core:tracker-pattern:update"
ActionTrackerPatternDelete = "core:tracker-pattern:delete"
// CookieConsentRecord actions
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
}
// 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.
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
}
pageOrderBy := page.OrderBy[coredata.CookiePatternOrderField]{
Field: coredata.CookiePatternOrderFieldName,
pageOrderBy := page.OrderBy[coredata.TrackerPatternOrderField]{
Field: coredata.TrackerPatternOrderFieldName,
Direction: page.OrderDirectionAsc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.CookiePatternOrderField]{
pageOrderBy = page.OrderBy[coredata.TrackerPatternOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
@@ -233,7 +197,7 @@ func (r *cookieBannerResolver) UncategorisedTrackerPatterns(ctx context.Context,
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
scope := coredata.NewScopeFromObjectID(obj.ID)
coredataFilter := coredata.NewCookiePatternFilter(nil, nil, nil)
coredataFilter := coredata.NewTrackerPatternFilter(nil, nil, nil)
if filter != nil {
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
}
// CookiePatterns is the resolver for the cookiePatterns 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) {
if err := r.authorize(ctx, obj.ID, probo.ActionCookiePatternList); err != nil {
// TrackerPatterns is the resolver for the trackerPatterns field.
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.ActionTrackerPatternList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.CookiePatternOrderField]{
Field: coredata.CookiePatternOrderFieldCreatedAt,
pageOrderBy := page.OrderBy[coredata.TrackerPatternOrderField]{
Field: coredata.TrackerPatternOrderFieldCreatedAt,
Direction: page.OrderDirectionAsc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.CookiePatternOrderField]{
pageOrderBy = page.OrderBy[coredata.TrackerPatternOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
@@ -358,15 +322,15 @@ func (r *cookieCategoryResolver) CookiePatterns(ctx context.Context, obj *types.
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
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 {
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)
}
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.
@@ -391,74 +355,6 @@ func (r *cookieCategoryConnectionResolver) TotalCount(ctx context.Context, obj *
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.
func (r *mutationResolver) CreateCookieBanner(ctx context.Context, input types.CreateCookieBannerInput) (*types.CreateCookieBannerPayload, error) {
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
}
// 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.
func (r *mutationResolver) UpsertCookieBannerTranslation(ctx context.Context, input types.UpsertCookieBannerTranslationInput) (*types.UpsertCookieBannerTranslationPayload, error) {
if err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerUpdate); err != nil {
@@ -1033,9 +755,64 @@ func (r *mutationResolver) UpsertCookieBannerTranslation(ctx context.Context, in
}, 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.
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
}
@@ -1053,7 +830,7 @@ func (r *mutationResolver) UpdateTrackerPattern(ctx context.Context, input types
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrCookiePatternNotFound) {
if errors.Is(err, cookiebanner.ErrTrackerPatternNotFound) {
return nil, gqlutils.NotFound(ctx, 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.
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
}
@@ -1083,7 +860,7 @@ func (r *mutationResolver) DeleteTrackerPattern(ctx context.Context, input types
pattern, err := r.cookieBanner.GetTrackerPattern(ctx, scope, input.TrackerPatternID)
if err != nil {
if errors.Is(err, cookiebanner.ErrCookiePatternNotFound) {
if errors.Is(err, cookiebanner.ErrTrackerPatternNotFound) {
return nil, gqlutils.NotFound(ctx, 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
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)
}
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.
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
}
@@ -1137,7 +914,7 @@ func (r *mutationResolver) MoveTrackerPatternToCategory(ctx context.Context, inp
switch {
case errors.Is(err, cookiebanner.ErrCategoryNotFound):
return nil, gqlutils.NotFound(ctx, err)
case errors.Is(err, cookiebanner.ErrCookiePatternNotFound):
case errors.Is(err, cookiebanner.ErrTrackerPatternNotFound):
return nil, gqlutils.NotFound(ctx, err)
case errors.Is(err, cookiebanner.ErrCategoriesBannerMismatch):
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) {
scope := coredata.NewScopeFromObjectID(obj.ParentID)
filter := coredata.NewCookiePatternFilter(nil, nil, nil)
filter := coredata.NewTrackerPatternFilter(nil, nil, nil)
if obj.Filter != nil {
filter = filter.WithQuery(obj.Filter.Query).WithSource(obj.Filter.Source)
}
@@ -1230,14 +1007,6 @@ func (r *Resolver) CookieCategoryConnection() schema.CookieCategoryConnectionRes
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.
func (r *Resolver) TrackerPattern() schema.TrackerPatternResolver { return &trackerPatternResolver{r} }
@@ -1251,7 +1020,5 @@ type cookieBannerConnectionResolver struct{ *Resolver }
type cookieBannerVersionResolver struct{ *Resolver }
type cookieCategoryResolver struct{ *Resolver }
type cookieCategoryConnectionResolver struct{ *Resolver }
type cookiePatternResolver struct{ *Resolver }
type cookiePatternConnectionResolver struct{ *Resolver }
type trackerPatternResolver struct{ *Resolver }
type trackerPatternConnectionResolver struct{ *Resolver }

View File

@@ -151,15 +151,6 @@ type CookieBanner implements Node {
filter: CookieConsentRecordFilter
): CookieConsentRecordConnection @goField(forceResolver: true)
uncategorisedPatterns(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: CookiePatternOrder
filter: CookiePatternFilter
): CookiePatternConnection @goField(forceResolver: true)
uncategorisedTrackerPatterns(
first: Int
after: CursorKey
@@ -194,13 +185,13 @@ type CookieCategory implements Node {
gcmConsentTypes: [String!]!
posthogConsent: Boolean!
cookiePatterns(
trackerPatterns(
first: Int
after: CursorKey
last: Int
before: CursorKey
orderBy: CookiePatternOrder
): CookiePatternConnection @goField(forceResolver: true)
orderBy: TrackerPatternOrder
): TrackerPatternConnection @goField(forceResolver: true)
createdAt: Datetime!
updatedAt: Datetime!
@@ -208,98 +199,52 @@ type CookieCategory implements Node {
permission(action: String!): Boolean! @goField(forceResolver: true)
}
enum CookiePatternMatchType
enum TrackerPatternMatchType
@goModel(
model: "go.probo.inc/probo/pkg/coredata.CookiePatternMatchType"
model: "go.probo.inc/probo/pkg/coredata.TrackerPatternMatchType"
) {
EXACT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternMatchTypeExact"
value: "go.probo.inc/probo/pkg/coredata.TrackerPatternMatchTypeExact"
)
PREFIX
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternMatchTypePrefix"
value: "go.probo.inc/probo/pkg/coredata.TrackerPatternMatchTypePrefix"
)
}
enum CookiePatternOrderField
enum TrackerPatternOrderField
@goModel(
model: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderField"
model: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderField"
) {
CREATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderFieldCreatedAt"
value: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderFieldCreatedAt"
)
NAME
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderFieldName"
value: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderFieldName"
)
LAST_MATCHED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderFieldLastMatchedAt"
value: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderFieldLastMatchedAt"
)
UPDATED_AT
@goEnum(
value: "go.probo.inc/probo/pkg/coredata.CookiePatternOrderFieldUpdatedAt"
value: "go.probo.inc/probo/pkg/coredata.TrackerPatternOrderFieldUpdatedAt"
)
SOURCE
@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 {
id: ID!
cookieCategory: CookieCategory @goField(forceResolver: true)
trackerType: TrackerType!
pattern: String!
matchType: CookiePatternMatchType!
matchType: TrackerPatternMatchType!
displayName: String!
maxAgeSeconds: Int
description: String!
@@ -332,7 +277,7 @@ input TrackerPatternOrder
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrackerPatternOrderBy"
) {
direction: OrderDirection!
field: CookiePatternOrderField!
field: TrackerPatternOrderField!
}
input TrackerPatternFilter
@@ -426,21 +371,12 @@ extend type Mutation {
reorderCookieCategory(
input: ReorderCookieCategoryInput!
): ReorderCookieCategoryPayload!
createCookiePattern(
input: CreateCookiePatternInput!
): CreateCookiePatternPayload!
updateCookiePattern(
input: UpdateCookiePatternInput!
): UpdateCookiePatternPayload!
deleteCookiePattern(
input: DeleteCookiePatternInput!
): DeleteCookiePatternPayload!
moveCookiePatternToCategory(
input: MoveCookiePatternToCategoryInput!
): MoveCookiePatternToCategoryPayload!
upsertCookieBannerTranslation(
input: UpsertCookieBannerTranslationInput!
): UpsertCookieBannerTranslationPayload!
createTrackerPattern(
input: CreateTrackerPatternInput!
): CreateTrackerPatternPayload!
updateTrackerPattern(
input: UpdateTrackerPatternInput!
): UpdateTrackerPatternPayload!
@@ -558,53 +494,6 @@ type ReorderCookieCategoryPayload {
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 {
cookieBannerId: ID!
language: String!
@@ -616,6 +505,16 @@ type UpsertCookieBannerTranslationPayload {
cookieBanner: CookieBanner!
}
input CreateTrackerPatternInput {
cookieCategoryId: ID!
trackerType: TrackerType
pattern: String!
matchType: TrackerPatternMatchType!
displayName: String!
maxAgeSeconds: Int
description: String
}
input UpdateTrackerPatternInput {
trackerPatternId: ID!
displayName: String
@@ -633,6 +532,11 @@ input MoveTrackerPatternToCategoryInput {
targetCookieCategoryId: ID!
}
type CreateTrackerPatternPayload {
trackerPatternEdge: TrackerPatternEdge!
cookieBanner: CookieBanner!
}
type UpdateTrackerPatternPayload {
trackerPattern: TrackerPattern!
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 (
TrackerPatternOrderBy OrderBy[coredata.CookiePatternOrderField]
TrackerPatternOrderBy OrderBy[coredata.TrackerPatternOrderField]
TrackerPatternConnection struct {
TotalCount int
@@ -41,7 +41,7 @@ type (
)
func NewTrackerPatternConnection(
p *page.Page[*coredata.TrackerPattern, coredata.CookiePatternOrderField],
p *page.Page[*coredata.TrackerPattern, coredata.TrackerPatternOrderField],
parentType any,
parentID gid.GID,
) *TrackerPatternConnection {
@@ -61,7 +61,7 @@ func NewTrackerPatternConnection(
}
func NewTrackerPatternConnectionWithFilter(
p *page.Page[*coredata.TrackerPattern, coredata.CookiePatternOrderField],
p *page.Page[*coredata.TrackerPattern, coredata.TrackerPatternOrderField],
parentType any,
parentID gid.GID,
filter *TrackerPatternFilter,
@@ -71,7 +71,7 @@ func NewTrackerPatternConnectionWithFilter(
return conn
}
func NewTrackerPatternEdge(tp *coredata.TrackerPattern, orderBy coredata.CookiePatternOrderField) *TrackerPatternEdge {
func NewTrackerPatternEdge(tp *coredata.TrackerPattern, orderBy coredata.TrackerPatternOrderField) *TrackerPatternEdge {
return &TrackerPatternEdge{
Cursor: tp.CursorKey(orderBy),
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
}
func (r *Resolver) ListCookiePatternsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListCookiePatternsInput) (*mcp.CallToolResult, types.ListCookiePatternsOutput, error) {
r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionCookiePatternList)
func (r *Resolver) ListTrackerPatternsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListTrackerPatternsInput) (*mcp.CallToolResult, types.ListTrackerPatternsOutput, error) {
r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionTrackerPatternList)
scope := coredata.NewScopeFromObjectID(input.CookieCategoryID)
cursor := types.NewCursor(input.Size, input.Cursor, page.OrderBy[coredata.CookiePatternOrderField]{Field: coredata.CookiePatternOrderFieldCreatedAt, Direction: page.OrderDirectionAsc})
patterns, err := r.cookieBanner.ListCookiePatternsForCategory(ctx, scope, input.CookieCategoryID, cursor)
cursor := types.NewCursor(input.Size, input.Cursor, page.OrderBy[coredata.TrackerPatternOrderField]{Field: coredata.TrackerPatternOrderFieldCreatedAt, Direction: page.OrderDirectionAsc})
patterns, err := r.cookieBanner.ListTrackerPatternsForCategory(ctx, scope, input.CookieCategoryID, cursor)
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)
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) {
r.MustAuthorize(ctx, input.ID, probo.ActionCookiePatternGet)
func (r *Resolver) GetTrackerPatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetTrackerPatternInput) (*mcp.CallToolResult, types.GetTrackerPatternOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionTrackerPatternGet)
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 {
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) {
r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionCookiePatternCreate)
func (r *Resolver) AddTrackerPatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddTrackerPatternInput) (*mcp.CallToolResult, types.AddTrackerPatternOutput, error) {
r.MustAuthorize(ctx, input.CookieCategoryID, probo.ActionTrackerPatternCreate)
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,
TrackerType: coredata.TrackerType(input.TrackerType),
Pattern: input.Pattern,
MatchType: coredata.CookiePatternMatchType(input.MatchType),
MatchType: coredata.TrackerPatternMatchType(input.MatchType),
DisplayName: input.DisplayName,
MaxAgeSeconds: input.MaxAgeSeconds,
Description: input.Description,
})
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) {
r.MustAuthorize(ctx, input.ID, probo.ActionCookiePatternUpdate)
func (r *Resolver) UpdateTrackerPatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateTrackerPatternInput) (*mcp.CallToolResult, types.UpdateTrackerPatternOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionTrackerPatternUpdate)
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 {
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 {
updateReq.Excluded = *v
}
pattern, err := r.cookieBanner.UpdateCookiePattern(ctx, scope, updateReq)
pattern, err := r.cookieBanner.UpdateTrackerPattern(ctx, scope, updateReq)
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) {
r.MustAuthorize(ctx, input.ID, probo.ActionCookiePatternDelete)
func (r *Resolver) DeleteTrackerPatternTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteTrackerPatternInput) (*mcp.CallToolResult, types.DeleteTrackerPatternOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionTrackerPatternDelete)
scope := coredata.NewScopeFromObjectID(input.ID)
if err := r.cookieBanner.DeleteCookiePattern(ctx, scope, input.ID); err != nil {
return nil, types.DeleteCookiePatternOutput{}, fmt.Errorf("cannot delete cookie pattern: %w", err)
if err := r.cookieBanner.DeleteTrackerPattern(ctx, scope, input.ID); err != nil {
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) {
r.MustAuthorize(ctx, input.CookiePatternID, probo.ActionCookiePatternUpdate)
scope := coredata.NewScopeFromObjectID(input.CookiePatternID)
result, err := r.cookieBanner.MoveCookiePatternToCategory(ctx, scope, cookiebanner.MoveCookiePatternToCategoryRequest{
CookiePatternID: input.CookiePatternID,
func (r *Resolver) MoveTrackerPatternToCategoryTool(ctx context.Context, req *mcp.CallToolRequest, input *types.MoveTrackerPatternToCategoryInput) (*mcp.CallToolResult, types.MoveTrackerPatternToCategoryOutput, error) {
r.MustAuthorize(ctx, input.TrackerPatternID, probo.ActionTrackerPatternUpdate)
scope := coredata.NewScopeFromObjectID(input.TrackerPatternID)
result, err := r.cookieBanner.MoveTrackerPatternToCategory(ctx, scope, cookiebanner.MoveTrackerPatternToCategoryRequest{
TrackerPatternID: input.TrackerPatternID,
TargetCookieCategoryID: input.TargetCookieCategoryID,
})
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) {

View File

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

View File

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