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:
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
@@ -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 },
|
||||
};
|
||||
}
|
||||
@@ -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',
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
@@ -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))
|
||||
|
||||
@@ -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")
|
||||
@@ -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
|
||||
},
|
||||
@@ -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
|
||||
@@ -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
|
||||
},
|
||||
@@ -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))
|
||||
@@ -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
|
||||
},
|
||||
@@ -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 {
|
||||
@@ -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")
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -92,7 +92,7 @@ func TestFindMergeGroups(t *testing.T) {
|
||||
return &coredata.TrackerPattern{
|
||||
Pattern: name,
|
||||
TrackerType: coredata.TrackerTypeCookie,
|
||||
MatchType: coredata.CookiePatternMatchTypeExact,
|
||||
MatchType: coredata.TrackerPatternMatchTypeExact,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
71
pkg/coredata/tracker_pattern_order_field.go
Normal file
71
pkg/coredata/tracker_pattern_order_field.go
Normal 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
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user