From c594b10e9fadf1433a242cb6cf46bc83e4286e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Fri, 10 Apr 2026 17:31:59 +0400 Subject: [PATCH] Add consent record operations and rename ConsentRecord to CookieConsentRecord MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- pkg/cookiebanner/client.go | 114 ++++++++++++++++++++++++++ pkg/coredata/cookie_consent_record.go | 16 ++-- pkg/coredata/entity_type_reg.go | 6 +- 3 files changed, 125 insertions(+), 11 deletions(-) diff --git a/pkg/cookiebanner/client.go b/pkg/cookiebanner/client.go index a8333c8b1..efcb0e65c 100644 --- a/pkg/cookiebanner/client.go +++ b/pkg/cookiebanner/client.go @@ -16,6 +16,7 @@ package cookiebanner import ( "context" + "encoding/json" "errors" "fmt" "time" @@ -82,6 +83,15 @@ type ( Rank *int Cookies *coredata.CookieItems } + + CreateCookieConsentRecordRequest struct { + CookieBannerID gid.GID + VisitorID string + IPAddress *string + UserAgent *string + ConsentData json.RawMessage + Action coredata.CookieConsentAction + } ) func (r *CreateCookieBannerRequest) Validate() error { @@ -132,6 +142,16 @@ func (r *UpdateCookieCategoryRequest) Validate() error { return v.Error() } +func (r *CreateCookieConsentRecordRequest) Validate() error { + v := validator.New() + + v.Check(r.CookieBannerID, "cookie_banner_id", validator.Required(), validator.GID(coredata.CookieBannerEntityType)) + v.Check(r.VisitorID, "visitor_id", validator.Required(), validator.NotEmpty()) + v.Check(r.Action, "action", validator.Required(), validator.OneOfSlice(coredata.CookieConsentActions())) + + return v.Error() +} + func (c *Client) CreateCookieBanner( ctx context.Context, scope coredata.Scoper, @@ -643,3 +663,97 @@ func (c *Client) DeleteCookieCategory( }, ) } + +func (c *Client) CreateCookieConsentRecord( + ctx context.Context, + scope coredata.Scoper, + req CreateCookieConsentRecordRequest, +) (*coredata.CookieConsentRecord, error) { + if err := req.Validate(); err != nil { + return nil, fmt.Errorf("invalid request: %w", err) + } + + var record *coredata.CookieConsentRecord + + err := c.pg.WithTx( + ctx, + func(ctx context.Context, tx pg.Tx) error { + record = &coredata.CookieConsentRecord{ + ID: gid.New(scope.GetTenantID(), coredata.CookieConsentRecordEntityType), + CookieBannerID: req.CookieBannerID, + VisitorID: req.VisitorID, + IPAddress: req.IPAddress, + UserAgent: req.UserAgent, + ConsentData: req.ConsentData, + Action: req.Action, + CreatedAt: time.Now(), + } + + if err := record.Insert(ctx, tx, scope); err != nil { + return fmt.Errorf("cannot insert consent record: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, err + } + + return record, nil +} + +func (c *Client) ListCookieConsentRecordsForBanner( + ctx context.Context, + scope coredata.Scoper, + bannerID gid.GID, + cursor *page.Cursor[coredata.CookieConsentRecordOrderField], + filter *coredata.CookieConsentRecordFilter, +) (coredata.CookieConsentRecords, error) { + var records coredata.CookieConsentRecords + + err := c.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + if err := records.LoadByCookieBannerID(ctx, conn, scope, bannerID, cursor, filter); err != nil { + return fmt.Errorf("cannot list consent records: %w", err) + } + + return nil + }, + ) + if err != nil { + return nil, err + } + + return records, nil +} + +func (c *Client) CountCookieConsentRecordsForBanner( + ctx context.Context, + scope coredata.Scoper, + bannerID gid.GID, + filter *coredata.CookieConsentRecordFilter, +) (int, error) { + var count int + + err := c.pg.WithConn( + ctx, + func(ctx context.Context, conn pg.Querier) error { + var records coredata.CookieConsentRecords + var err error + + count, err = records.CountByCookieBannerID(ctx, conn, scope, bannerID, filter) + if err != nil { + return fmt.Errorf("cannot count consent records: %w", err) + } + + return nil + }, + ) + if err != nil { + return 0, err + } + + return count, nil +} diff --git a/pkg/coredata/cookie_consent_record.go b/pkg/coredata/cookie_consent_record.go index be46379c9..219511b8a 100644 --- a/pkg/coredata/cookie_consent_record.go +++ b/pkg/coredata/cookie_consent_record.go @@ -29,7 +29,7 @@ import ( ) type ( - ConsentRecord struct { + CookieConsentRecord struct { ID gid.GID `db:"id"` CookieBannerID gid.GID `db:"cookie_banner_id"` VisitorID string `db:"visitor_id"` @@ -40,10 +40,10 @@ type ( CreatedAt time.Time `db:"created_at"` } - ConsentRecords []*ConsentRecord + CookieConsentRecords []*CookieConsentRecord ) -func (r *ConsentRecord) CursorKey(field CookieConsentRecordOrderField) page.CursorKey { +func (r *CookieConsentRecord) CursorKey(field CookieConsentRecordOrderField) page.CursorKey { switch field { case CookieConsentRecordOrderFieldCreatedAt: return page.NewCursorKey(r.ID, r.CreatedAt) @@ -52,7 +52,7 @@ func (r *ConsentRecord) CursorKey(field CookieConsentRecordOrderField) page.Curs panic(fmt.Sprintf("unsupported order by: %s", field)) } -func (r *ConsentRecord) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { +func (r *CookieConsentRecord) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { q := ` SELECT cb.organization_id FROM cookie_consent_records cr @@ -72,7 +72,7 @@ LIMIT 1; return map[string]string{"organization_id": organizationID.String()}, nil } -func (r *ConsentRecords) LoadByCookieBannerID( +func (r *CookieConsentRecords) LoadByCookieBannerID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -111,7 +111,7 @@ WHERE return fmt.Errorf("cannot query consent records: %w", err) } - records, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ConsentRecord]) + records, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[CookieConsentRecord]) if err != nil { return fmt.Errorf("cannot collect consent records: %w", err) } @@ -121,7 +121,7 @@ WHERE return nil } -func (r *ConsentRecords) CountByCookieBannerID( +func (r *CookieConsentRecords) CountByCookieBannerID( ctx context.Context, conn pg.Querier, scope Scoper, @@ -155,7 +155,7 @@ WHERE return count, nil } -func (r *ConsentRecord) Insert( +func (r *CookieConsentRecord) Insert( ctx context.Context, tx pg.Tx, scope Scoper, diff --git a/pkg/coredata/entity_type_reg.go b/pkg/coredata/entity_type_reg.go index 011df7acb..e29f27dcf 100644 --- a/pkg/coredata/entity_type_reg.go +++ b/pkg/coredata/entity_type_reg.go @@ -100,7 +100,7 @@ const ( AccessEntryDecisionHistoryEntityType uint16 = 74 CookieBannerEntityType uint16 = 75 CookieCategoryEntityType uint16 = 76 - ConsentRecordEntityType uint16 = 77 + CookieConsentRecordEntityType uint16 = 77 ) func NewEntityFromID(id gid.GID) (any, bool) { @@ -251,8 +251,8 @@ func NewEntityFromID(id gid.GID) (any, bool) { return &CookieBanner{ID: id}, true case CookieCategoryEntityType: return &CookieCategory{ID: id}, true - case ConsentRecordEntityType: - return &ConsentRecord{ID: id}, true + case CookieConsentRecordEntityType: + return &CookieConsentRecord{ID: id}, true default: return nil, false }