Files
probo/pkg/server/api/console/v1/cookie_banner_resolvers.go
Émile Ré ed93301a1f Use subquery for common third party filter
Replace the two-step ID-materializing pattern (fetch IDs in Go, pass
as ANY(@ids)) with an IN-subquery that keeps the filtering entirely
in the database and eliminates an extra round trip. Remove the now
unused LoadIDsByCommonThirdPartyID and its service wrapper. Update
the coredata rule to clarify that subqueries for filtering are OK.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-29 10:07:31 +02:00

1636 lines
55 KiB
Go

package console_v1
// This file will be automatically regenerated based on the schema, any resolver
// implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.90
import (
"context"
"encoding/json"
"errors"
"github.com/vikstrous/dataloadgen"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/cookiebanner"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/probo"
"go.probo.inc/probo/pkg/server/api/authn"
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
"go.probo.inc/probo/pkg/server/api/console/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
"go.probo.inc/probo/pkg/validator"
)
// Organization is the resolver for the organization field.
func (r *cookieBannerResolver) Organization(ctx context.Context, obj *types.CookieBanner) (*types.Organization, error) {
if _, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
organization, err := loaders.Organization.Load(ctx, obj.Organization.ID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Categories is the resolver for the categories field.
func (r *cookieBannerResolver) Categories(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.CookieCategoryOrderBy, filter *types.CookieCategoryFilter) (*types.CookieCategoryConnection, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionCookieCategoryList)
if err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.CookieCategoryOrderField]{
Field: coredata.CookieCategoryOrderFieldRank,
Direction: page.OrderDirectionAsc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.CookieCategoryOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var excludeKind *coredata.CookieCategoryKind
if filter != nil {
excludeKind = filter.ExcludeKind
}
cdFilter := coredata.NewCookieCategoryFilter(excludeKind)
categories, err := r.cookieBanner.ListCategoriesForBanner(ctx, scope, obj.ID, cursor, cdFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list cookie categories", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
p := page.NewPage(categories, cursor)
return types.NewCookieCategoryConnectionWithFilter(p, r, obj.ID, cdFilter), nil
}
// Translations is the resolver for the translations field.
func (r *cookieBannerResolver) Translations(ctx context.Context, obj *types.CookieBanner) ([]*types.CookieBannerTranslation, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionCookieBannerGet)
if err != nil {
return nil, err
}
translations, err := r.cookieBanner.ListCookieBannerTranslations(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list cookie banner translations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
result := make([]*types.CookieBannerTranslation, len(translations))
for i, t := range translations {
result[i] = types.NewCookieBannerTranslation(t)
}
return result, nil
}
// LatestVersion is the resolver for the latestVersion field.
func (r *cookieBannerResolver) LatestVersion(ctx context.Context, obj *types.CookieBanner) (*types.CookieBannerVersion, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionCookieBannerVersionList)
if err != nil {
return nil, err
}
cursor := &page.Cursor[coredata.CookieBannerVersionOrderField]{
Size: 1,
Position: page.Head,
OrderBy: page.OrderBy[coredata.CookieBannerVersionOrderField]{
Field: coredata.CookieBannerVersionOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
},
}
versions, err := r.cookieBanner.ListCookieBannerVersionsForBanner(ctx, scope, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load latest cookie banner version", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if len(versions) == 0 {
return nil, nil
}
v := versions[0]
return &types.CookieBannerVersion{
ID: v.ID,
Version: v.Version,
State: string(v.State),
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
}, nil
}
// ConsentRecords is the resolver for the consentRecords field.
func (r *cookieBannerResolver) ConsentRecords(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.CookieConsentRecordOrderBy, filter *types.CookieConsentRecordFilter) (*types.CookieConsentRecordConnection, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionCookieConsentRecordList)
if err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.CookieConsentRecordOrderField]{
Field: coredata.CookieConsentRecordOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.CookieConsentRecordOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var (
action *coredata.CookieConsentAction
visitorID *string
version *int
)
if filter != nil {
action = filter.Action
visitorID = filter.VisitorID
version = filter.Version
}
coredataFilter := coredata.NewCookieConsentRecordFilter(action, visitorID, version)
records, err := r.cookieBanner.ListCookieConsentRecordsForBanner(ctx, scope, obj.ID, cursor, coredataFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list consent records", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
p := page.NewPage(records, cursor)
return types.NewCookieConsentRecordConnection(p, r, obj.ID, coredataFilter), nil
}
// TrackerPatterns is the resolver for the trackerPatterns field.
func (r *cookieBannerResolver) TrackerPatterns(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) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrackerPatternList)
if err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.TrackerPatternOrderField]{
Field: coredata.TrackerPatternOrderFieldName,
Direction: page.OrderDirectionAsc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.TrackerPatternOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
coredataFilter := coredata.NewTrackerPatternFilter(nil, nil, nil)
if filter != nil {
coredataFilter = coredata.NewTrackerPatternFilter(nil, filter.CookieCategoryID, nil)
coredataFilter = coredataFilter.WithQuery(filter.Query).WithSource(filter.Source).WithTrackerType(filter.TrackerType)
if filter.ThirdPartyID != nil {
switch filter.ThirdPartyID.EntityType() {
case coredata.ThirdPartyEntityType:
coredataFilter = coredataFilter.WithThirdPartyID(filter.ThirdPartyID)
case coredata.CommonThirdPartyEntityType:
coredataFilter = coredataFilter.WithCommonThirdPartyID(filter.ThirdPartyID)
default:
return nil, gqlutils.Invalidf(ctx, "thirdPartyId must reference a ThirdParty or CommonThirdParty")
}
}
}
patterns, err := r.cookieBanner.ListTrackerPatternsForBanner(ctx, scope, obj.ID, cursor, coredataFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list tracker patterns", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
p := page.NewPage(patterns, cursor)
return types.NewTrackerPatternConnectionWithFilter(p, r, obj.ID, filter), nil
}
// LinkedThirdParties is the resolver for the linkedThirdParties field.
//
// Aggregates the deduped union of third parties linked to the banner's
// tracker patterns: the org-scoped ThirdParty values reached through
// the direct foreign key, plus the global CommonThirdParty values
// reached indirectly through CommonTrackerPattern. The two sources are
// independent, so a tracker pattern that has both ThirdPartyID and
// CommonTrackerPatternID contributes the org-scoped link only — the
// commonThirdParty resolver follows the same priority and we want the
// banner-level filter to mirror it.
func (r *cookieBannerResolver) LinkedThirdParties(ctx context.Context, obj *types.CookieBanner) ([]types.TrackerPatternThirdPartyLink, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionThirdPartyList)
if err != nil {
return nil, err
}
thirdPartyIDs, err := r.cookieBanner.LoadDistinctThirdPartyIDsByCookieBannerID(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list banner third party links", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
commonPatternIDs, err := r.cookieBanner.LoadDistinctCommonTrackerPatternIDsByCookieBannerID(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list banner common tracker pattern links", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
out := make([]types.TrackerPatternThirdPartyLink, 0, len(thirdPartyIDs)+len(commonPatternIDs))
loaders := dataloader.FromContext(ctx)
if len(thirdPartyIDs) > 0 {
tps, loadErr := loaders.ThirdParty.LoadAll(ctx, thirdPartyIDs)
var loadErrs dataloadgen.ErrorSlice
if loadErr != nil && !errors.As(loadErr, &loadErrs) {
r.logger.ErrorCtx(ctx, "cannot get third parties", log.Error(loadErr))
return nil, gqlutils.Internal(ctx)
}
for i, tp := range tps {
if loadErrs != nil && loadErrs[i] != nil {
if errors.Is(loadErrs[i], coredata.ErrResourceNotFound) || errors.Is(loadErrs[i], dataloadgen.ErrNotFound) {
continue
}
r.logger.ErrorCtx(ctx, "cannot get third party", log.Error(loadErrs[i]))
return nil, gqlutils.Internal(ctx)
}
out = append(out, types.NewThirdParty(tp))
}
}
if len(commonPatternIDs) > 0 {
identity := authn.IdentityFromContext(ctx)
if _, err := r.authorize(ctx, identity.ID, probo.ActionCommonThirdPartyList); err != nil {
return nil, err
}
patterns, err := r.cookieBanner.GetCommonTrackerPatternsByIDs(ctx, commonPatternIDs...)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get common tracker patterns", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
seen := make(map[gid.GID]struct{}, len(patterns))
commonThirdPartyIDs := make([]gid.GID, 0, len(patterns))
for _, p := range patterns {
if p.CommonThirdPartyID == nil {
continue
}
if _, ok := seen[*p.CommonThirdPartyID]; ok {
continue
}
seen[*p.CommonThirdPartyID] = struct{}{}
commonThirdPartyIDs = append(commonThirdPartyIDs, *p.CommonThirdPartyID)
}
if len(commonThirdPartyIDs) > 0 {
parties, err := r.thirdParty.GetCommonThirdPartiesByIDs(ctx, commonThirdPartyIDs...)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get common third parties", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
for _, p := range parties {
out = append(out, types.NewCommonThirdParty(p))
}
}
}
return out, nil
}
// UncategorisedTrackerResources is the resolver for the uncategorisedTrackerResources field.
func (r *cookieBannerResolver) UncategorisedTrackerResources(ctx context.Context, obj *types.CookieBanner, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TrackerResourceOrderBy, filter *types.TrackerResourceFilter) (*types.TrackerResourceConnection, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrackerResourceList)
if err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.TrackerResourceOrderField]{
Field: coredata.TrackerResourceOrderFieldLastDetectedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.TrackerResourceOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
coredataFilter := coredata.NewTrackerResourceFilter(nil, nil)
if filter != nil {
coredataFilter = coredataFilter.WithQuery(filter.Query).WithResourceType(filter.Type)
}
resources, err := r.cookieBanner.ListUncategorisedTrackerResources(ctx, scope, obj.ID, cursor, coredataFilter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list uncategorised tracker resources", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
p := page.NewPage(resources, cursor)
return types.NewTrackerResourceConnectionWithFilter(p, r, obj.ID, filter), nil
}
// Permission is the resolver for the permission field.
func (r *cookieBannerResolver) Permission(ctx context.Context, obj *types.CookieBanner, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *cookieBannerConnectionResolver) TotalCount(ctx context.Context, obj *types.CookieBannerConnection) (int, error) {
scope, err := r.authorize(ctx, obj.ParentID, probo.ActionCookieBannerList)
if err != nil {
return 0, err
}
count, err := r.cookieBanner.CountCookieBannersForOrganization(ctx, scope, obj.ParentID, coredata.NewCookieBannerFilter(nil))
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count cookie banners", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// Categories is the resolver for the categories field.
func (r *cookieBannerVersionResolver) Categories(ctx context.Context, obj *types.CookieBannerVersion) ([]*types.CookieBannerVersionCategory, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionCookieBannerVersionGet)
if err != nil {
return nil, err
}
version, err := r.cookieBanner.GetCookieBannerVersion(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner version", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
snapshot, err := version.GetSnapshot()
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get version snapshot", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
categories := make([]*types.CookieBannerVersionCategory, len(snapshot.Categories))
for i, cat := range snapshot.Categories {
cookies := make([]*types.CookieBannerVersionCookie, len(cat.Cookies))
for j, c := range cat.Cookies {
cookies[j] = &types.CookieBannerVersionCookie{
Name: c.Name,
MaxAgeSeconds: c.MaxAgeSeconds,
Description: c.Description,
}
}
categories[i] = &types.CookieBannerVersionCategory{
Name: cat.Name,
Slug: cat.Slug,
Description: cat.Description,
Kind: cat.Kind,
Cookies: cookies,
}
}
return categories, nil
}
// CookieBanner is the resolver for the cookieBanner field.
func (r *cookieCategoryResolver) CookieBanner(ctx context.Context, obj *types.CookieCategory) (*types.CookieBanner, error) {
if obj.CookieBanner == nil {
return nil, nil
}
if _, err := r.authorize(ctx, obj.CookieBanner.ID, probo.ActionCookieBannerGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
banner, err := loaders.CookieBanner.Load(ctx, obj.CookieBanner.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 banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewCookieBanner(banner), 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) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrackerPatternList)
if err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.TrackerPatternOrderField]{
Field: coredata.TrackerPatternOrderFieldCreatedAt,
Direction: page.OrderDirectionAsc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.TrackerPatternOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
patterns, err := r.cookieBanner.ListTrackerPatternsForCategory(ctx, scope, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list tracker patterns", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
p := page.NewPage(patterns, cursor)
return types.NewTrackerPatternConnection(p, r, obj.ID), nil
}
// TrackerResources is the resolver for the trackerResources field.
func (r *cookieCategoryResolver) TrackerResources(ctx context.Context, obj *types.CookieCategory, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TrackerResourceOrderBy) (*types.TrackerResourceConnection, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrackerResourceList)
if err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.TrackerResourceOrderField]{
Field: coredata.TrackerResourceOrderFieldCreatedAt,
Direction: page.OrderDirectionAsc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.TrackerResourceOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
resources, err := r.cookieBanner.ListTrackerResourcesForCategory(ctx, scope, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list tracker resources", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
p := page.NewPage(resources, cursor)
return types.NewTrackerResourceConnection(p, r, obj.ID), nil
}
// Permission is the resolver for the permission field.
func (r *cookieCategoryResolver) Permission(ctx context.Context, obj *types.CookieCategory, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *cookieCategoryConnectionResolver) TotalCount(ctx context.Context, obj *types.CookieCategoryConnection) (int, error) {
scope, err := r.authorize(ctx, obj.ParentID, probo.ActionCookieCategoryList)
if err != nil {
return 0, err
}
count, err := r.cookieBanner.CountCategoriesForBanner(ctx, scope, obj.ParentID, obj.Filter)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count cookie categories", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// TotalCount is the resolver for the totalCount field.
func (r *detectedTrackerConnectionResolver) TotalCount(ctx context.Context, obj *types.DetectedTrackerConnection) (int, error) {
scope, err := r.authorize(ctx, obj.ParentID, probo.ActionTrackerPatternGet)
if err != nil {
return 0, err
}
count, err := r.cookieBanner.CountDetectedTrackersByPatternID(ctx, scope, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count detected trackers", 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) {
scope, err := r.authorize(ctx, input.OrganizationID, probo.ActionCookieBannerCreate)
if err != nil {
return nil, err
}
banner, err := r.cookieBanner.CreateCookieBanner(
ctx,
scope,
cookiebanner.CreateCookieBannerRequest{
OrganizationID: input.OrganizationID,
Name: input.Name,
Origin: input.Origin,
PrivacyPolicyURL: input.PrivacyPolicyURL,
CookiePolicyURL: input.CookiePolicyURL,
ConsentExpiryDays: input.ConsentExpiryDays,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrOriginAlreadyInUse) {
return nil, gqlutils.Conflict(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateCookieBannerPayload{
CookieBannerEdge: types.NewCookieBannerEdge(banner, coredata.CookieBannerOrderFieldCreatedAt),
}, nil
}
// UpdateCookieBanner is the resolver for the updateCookieBanner field.
func (r *mutationResolver) UpdateCookieBanner(ctx context.Context, input types.UpdateCookieBannerInput) (*types.UpdateCookieBannerPayload, error) {
scope, err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerUpdate)
if err != nil {
return nil, err
}
banner, err := r.cookieBanner.UpdateCookieBanner(
ctx,
scope,
cookiebanner.UpdateCookieBannerRequest{
CookieBannerID: input.CookieBannerID,
Name: input.Name,
PrivacyPolicyURL: input.PrivacyPolicyURL,
CookiePolicyURL: input.CookiePolicyURL,
ConsentExpiryDays: input.ConsentExpiryDays,
DefaultLanguage: input.DefaultLanguage,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrBannerNotFound) {
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 banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateCookieBannerPayload{
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// DeleteCookieBanner is the resolver for the deleteCookieBanner field.
func (r *mutationResolver) DeleteCookieBanner(ctx context.Context, input types.DeleteCookieBannerInput) (*types.DeleteCookieBannerPayload, error) {
scope, err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerDelete)
if err != nil {
return nil, err
}
if err := r.cookieBanner.DeleteCookieBanner(ctx, scope, input.CookieBannerID); err != nil {
if errors.Is(err, cookiebanner.ErrBannerNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot delete cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteCookieBannerPayload{
DeletedCookieBannerID: input.CookieBannerID,
}, nil
}
// ActivateCookieBanner is the resolver for the activateCookieBanner field.
func (r *mutationResolver) ActivateCookieBanner(ctx context.Context, input types.ActivateCookieBannerInput) (*types.ActivateCookieBannerPayload, error) {
scope, err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerActivate)
if err != nil {
return nil, err
}
banner, err := r.cookieBanner.ActivateCookieBanner(ctx, scope, input.CookieBannerID)
if err != nil {
if errors.Is(err, cookiebanner.ErrBannerNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if errors.Is(err, cookiebanner.ErrBannerAlreadyActive) {
return nil, gqlutils.Conflict(ctx, err)
}
if errors.Is(err, cookiebanner.ErrOriginAlreadyInUse) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot activate cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ActivateCookieBannerPayload{
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// DeactivateCookieBanner is the resolver for the deactivateCookieBanner field.
func (r *mutationResolver) DeactivateCookieBanner(ctx context.Context, input types.DeactivateCookieBannerInput) (*types.DeactivateCookieBannerPayload, error) {
scope, err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerDeactivate)
if err != nil {
return nil, err
}
banner, err := r.cookieBanner.DeactivateCookieBanner(ctx, scope, input.CookieBannerID)
if err != nil {
if errors.Is(err, cookiebanner.ErrBannerNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if errors.Is(err, cookiebanner.ErrBannerAlreadyInactive) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot deactivate cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeactivateCookieBannerPayload{
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// PublishCookieBannerVersion is the resolver for the publishCookieBannerVersion field.
func (r *mutationResolver) PublishCookieBannerVersion(ctx context.Context, input types.PublishCookieBannerVersionInput) (*types.PublishCookieBannerVersionPayload, error) {
scope, err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerVersionPublish)
if err != nil {
return nil, err
}
version, err := r.cookieBanner.PublishCookieBannerVersion(ctx, scope, input.CookieBannerID)
if err != nil {
if errors.Is(err, cookiebanner.ErrNoDraftVersion) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot publish cookie banner version", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
banner, err := r.cookieBanner.GetCookieBanner(ctx, scope, input.CookieBannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.PublishCookieBannerVersionPayload{
CookieBannerVersion: &types.CookieBannerVersion{
ID: version.ID,
Version: version.Version,
State: string(version.State),
CreatedAt: version.CreatedAt,
UpdatedAt: version.UpdatedAt,
},
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// CreateCookieCategory is the resolver for the createCookieCategory field.
func (r *mutationResolver) CreateCookieCategory(ctx context.Context, input types.CreateCookieCategoryInput) (*types.CreateCookieCategoryPayload, error) {
scope, err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieCategoryCreate)
if err != nil {
return nil, err
}
category, err := r.cookieBanner.CreateCookieCategory(
ctx,
scope,
cookiebanner.CreateCookieCategoryRequest{
CookieBannerID: input.CookieBannerID,
Name: input.Name,
Slug: input.Slug,
Description: input.Description,
Rank: input.Rank,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrBannerNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if errors.Is(err, cookiebanner.ErrCategorySlugAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot create cookie category", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
banner, err := r.cookieBanner.GetCookieBanner(ctx, scope, input.CookieBannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateCookieCategoryPayload{
CookieCategoryEdge: types.NewCookieCategoryEdge(category, coredata.CookieCategoryOrderFieldRank),
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// UpdateCookieCategory is the resolver for the updateCookieCategory field.
func (r *mutationResolver) UpdateCookieCategory(ctx context.Context, input types.UpdateCookieCategoryInput) (*types.UpdateCookieCategoryPayload, error) {
scope, err := r.authorize(ctx, input.CookieCategoryID, probo.ActionCookieCategoryUpdate)
if err != nil {
return nil, err
}
var gcmConsentTypes *[]string
if input.GcmConsentTypes != nil {
gcmConsentTypes = &input.GcmConsentTypes
}
category, err := r.cookieBanner.UpdateCookieCategory(
ctx,
scope,
cookiebanner.UpdateCookieCategoryRequest{
CookieCategoryID: input.CookieCategoryID,
Name: input.Name,
Slug: input.Slug,
Description: input.Description,
GCMConsentTypes: gcmConsentTypes,
PostHogConsent: input.PosthogConsent,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrCategoryNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if errors.Is(err, cookiebanner.ErrCategorySlugAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
if errors.Is(err, cookiebanner.ErrPostHogConsentKindInvalid) {
return nil, gqlutils.Invalid(ctx, err)
}
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update cookie category", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerScope := coredata.NewScopeFromObjectID(category.CookieBannerID)
banner, err := r.cookieBanner.GetCookieBanner(ctx, bannerScope, category.CookieBannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateCookieCategoryPayload{
CookieCategory: types.NewCookieCategory(category),
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// DeleteCookieCategory is the resolver for the deleteCookieCategory field.
func (r *mutationResolver) DeleteCookieCategory(ctx context.Context, input types.DeleteCookieCategoryInput) (*types.DeleteCookieCategoryPayload, error) {
scope, err := r.authorize(ctx, input.CookieCategoryID, probo.ActionCookieCategoryDelete)
if err != nil {
return nil, err
}
category, err := r.cookieBanner.GetCookieCategory(ctx, scope, input.CookieCategoryID)
if err != nil {
if errors.Is(err, cookiebanner.ErrCategoryNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get cookie category", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerID := category.CookieBannerID
err = r.cookieBanner.DeleteCookieCategory(ctx, scope, input.CookieCategoryID)
if err != nil {
if errors.Is(err, cookiebanner.ErrCategoryNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if errors.Is(err, cookiebanner.ErrCannotDeleteSystemCategory) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot delete cookie category", 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.DeleteCookieCategoryPayload{
DeletedCookieCategoryID: input.CookieCategoryID,
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// ReorderCookieCategory is the resolver for the reorderCookieCategory field.
func (r *mutationResolver) ReorderCookieCategory(ctx context.Context, input types.ReorderCookieCategoryInput) (*types.ReorderCookieCategoryPayload, error) {
scope, err := r.authorize(ctx, input.CookieCategoryID, probo.ActionCookieCategoryUpdate)
if err != nil {
return nil, err
}
banner, err := r.cookieBanner.ReorderCookieCategory(
ctx,
scope,
cookiebanner.ReorderCookieCategoryRequest{
CookieCategoryID: input.CookieCategoryID,
Rank: input.Rank,
},
)
if err != nil {
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 reorder cookie category", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ReorderCookieCategoryPayload{
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// UpsertCookieBannerTranslation is the resolver for the upsertCookieBannerTranslation field.
func (r *mutationResolver) UpsertCookieBannerTranslation(ctx context.Context, input types.UpsertCookieBannerTranslationInput) (*types.UpsertCookieBannerTranslationPayload, error) {
scope, err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerUpdate)
if err != nil {
return nil, err
}
translation, err := r.cookieBanner.UpsertCookieBannerTranslation(
ctx,
scope,
cookiebanner.UpsertCookieBannerTranslationRequest{
CookieBannerID: input.CookieBannerID,
Language: input.Language,
Translations: json.RawMessage(input.Translations),
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrBannerNotFound) {
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 upsert cookie banner translation", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
banner, err := r.cookieBanner.GetCookieBanner(ctx, scope, input.CookieBannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpsertCookieBannerTranslationPayload{
CookieBannerTranslation: types.NewCookieBannerTranslation(translation),
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// CreateTrackerPattern is the resolver for the createTrackerPattern field.
func (r *mutationResolver) CreateTrackerPattern(ctx context.Context, input types.CreateTrackerPatternInput) (*types.CreateTrackerPatternPayload, error) {
scope, err := r.authorize(ctx, input.CookieCategoryID, probo.ActionTrackerPatternCreate)
if err != nil {
return nil, err
}
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) {
scope, err := r.authorize(ctx, input.TrackerPatternID, probo.ActionTrackerPatternUpdate)
if err != nil {
return nil, err
}
pattern, err := r.cookieBanner.UpdateTrackerPattern(
ctx,
scope,
cookiebanner.UpdateTrackerPatternRequest{
TrackerPatternID: input.TrackerPatternID,
MaxAgeSeconds: gqlutils.UnwrapOmittable(input.MaxAgeSeconds),
Description: input.Description,
Excluded: input.Excluded,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrTrackerPatternNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot update 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.UpdateTrackerPatternPayload{
TrackerPattern: types.NewTrackerPatternNode(pattern),
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// DeleteTrackerPattern is the resolver for the deleteTrackerPattern field.
func (r *mutationResolver) DeleteTrackerPattern(ctx context.Context, input types.DeleteTrackerPatternInput) (*types.DeleteTrackerPatternPayload, error) {
scope, err := r.authorize(ctx, input.TrackerPatternID, probo.ActionTrackerPatternDelete)
if err != nil {
return nil, err
}
pattern, err := r.cookieBanner.GetTrackerPattern(ctx, scope, input.TrackerPatternID)
if err != nil {
if errors.Is(err, cookiebanner.ErrTrackerPatternNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get tracker pattern", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerID := pattern.CookieBannerID
if err := r.cookieBanner.DeleteTrackerPattern(ctx, scope, input.TrackerPatternID); err != nil {
if errors.Is(err, cookiebanner.ErrTrackerPatternNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot delete tracker 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.DeleteTrackerPatternPayload{
DeletedTrackerPatternID: input.TrackerPatternID,
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// MoveTrackerPatternToCategory is the resolver for the moveTrackerPatternToCategory field.
func (r *mutationResolver) MoveTrackerPatternToCategory(ctx context.Context, input types.MoveTrackerPatternToCategoryInput) (*types.MoveTrackerPatternToCategoryPayload, error) {
scope, err := r.authorize(ctx, input.TrackerPatternID, probo.ActionTrackerPatternUpdate)
if err != nil {
return nil, err
}
if _, err := r.authorize(ctx, input.TargetCookieCategoryID, probo.ActionCookieCategoryUpdate); err != nil {
return nil, err
}
result, err := r.cookieBanner.MoveTrackerPatternToCategory(
ctx,
scope,
cookiebanner.MoveTrackerPatternToCategoryRequest{
TrackerPatternID: input.TrackerPatternID,
TargetCookieCategoryID: input.TargetCookieCategoryID,
},
)
if err != nil {
switch {
case errors.Is(err, cookiebanner.ErrCategoryNotFound):
return nil, gqlutils.NotFound(ctx, err)
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")
default:
r.logger.ErrorCtx(ctx, "cannot move tracker pattern to category", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
return &types.MoveTrackerPatternToCategoryPayload{
TrackerPattern: types.NewTrackerPatternNode(result.TrackerPattern),
CookieBanner: types.NewCookieBanner(result.Banner),
}, nil
}
// CreateTrackerResource is the resolver for the createTrackerResource field.
func (r *mutationResolver) CreateTrackerResource(ctx context.Context, input types.CreateTrackerResourceInput) (*types.CreateTrackerResourcePayload, error) {
scope, err := r.authorize(ctx, input.CookieCategoryID, probo.ActionTrackerResourceCreate)
if err != nil {
return nil, err
}
var description string
if input.Description != nil {
description = *input.Description
}
resource, err := r.cookieBanner.CreateTrackerResource(
ctx,
scope,
cookiebanner.CreateTrackerResourceRequest{
CookieCategoryID: input.CookieCategoryID,
ResourceType: input.Type,
Origin: input.Origin,
Path: input.Path,
DisplayName: input.DisplayName,
Description: description,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrResourceAlreadyExists) {
return nil, gqlutils.Conflictf(ctx, "a resource with this origin and path 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 resource", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerScope := coredata.NewScopeFromObjectID(resource.CookieBannerID)
banner, err := r.cookieBanner.GetCookieBanner(ctx, bannerScope, resource.CookieBannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateTrackerResourcePayload{
TrackerResourceEdge: types.NewTrackerResourceEdge(resource, coredata.TrackerResourceOrderFieldCreatedAt),
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// UpdateTrackerResource is the resolver for the updateTrackerResource field.
func (r *mutationResolver) UpdateTrackerResource(ctx context.Context, input types.UpdateTrackerResourceInput) (*types.UpdateTrackerResourcePayload, error) {
scope, err := r.authorize(ctx, input.TrackerResourceID, probo.ActionTrackerResourceUpdate)
if err != nil {
return nil, err
}
resource, err := r.cookieBanner.UpdateTrackerResource(
ctx,
scope,
cookiebanner.UpdateTrackerResourceRequest{
TrackerResourceID: input.TrackerResourceID,
DisplayName: input.DisplayName,
Description: input.Description,
Excluded: input.Excluded,
},
)
if err != nil {
if errors.Is(err, cookiebanner.ErrTrackerResourceNotFound) {
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 tracker resource", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerScope := coredata.NewScopeFromObjectID(resource.CookieBannerID)
banner, err := r.cookieBanner.GetCookieBanner(ctx, bannerScope, resource.CookieBannerID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get cookie banner", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateTrackerResourcePayload{
TrackerResource: types.NewTrackerResourceNode(resource),
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// DeleteTrackerResource is the resolver for the deleteTrackerResource field.
func (r *mutationResolver) DeleteTrackerResource(ctx context.Context, input types.DeleteTrackerResourceInput) (*types.DeleteTrackerResourcePayload, error) {
scope, err := r.authorize(ctx, input.TrackerResourceID, probo.ActionTrackerResourceDelete)
if err != nil {
return nil, err
}
resource, err := r.cookieBanner.GetTrackerResource(ctx, scope, input.TrackerResourceID)
if err != nil {
if errors.Is(err, cookiebanner.ErrTrackerResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get tracker resource", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bannerID := resource.CookieBannerID
if err := r.cookieBanner.DeleteTrackerResource(ctx, scope, input.TrackerResourceID); err != nil {
if errors.Is(err, cookiebanner.ErrTrackerResourceNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot delete tracker resource", 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.DeleteTrackerResourcePayload{
DeletedTrackerResourceID: input.TrackerResourceID,
CookieBanner: types.NewCookieBanner(banner),
}, nil
}
// MoveTrackerResourceToCategory is the resolver for the moveTrackerResourceToCategory field.
func (r *mutationResolver) MoveTrackerResourceToCategory(ctx context.Context, input types.MoveTrackerResourceToCategoryInput) (*types.MoveTrackerResourceToCategoryPayload, error) {
scope, err := r.authorize(ctx, input.TrackerResourceID, probo.ActionTrackerResourceUpdate)
if err != nil {
return nil, err
}
if _, err := r.authorize(ctx, input.TargetCookieCategoryID, probo.ActionCookieCategoryUpdate); err != nil {
return nil, err
}
result, err := r.cookieBanner.MoveTrackerResourceToCategory(
ctx,
scope,
cookiebanner.MoveTrackerResourceToCategoryRequest{
TrackerResourceID: input.TrackerResourceID,
TargetCookieCategoryID: input.TargetCookieCategoryID,
},
)
if err != nil {
switch {
case errors.Is(err, cookiebanner.ErrCategoryNotFound):
return nil, gqlutils.NotFound(ctx, err)
case errors.Is(err, cookiebanner.ErrTrackerResourceNotFound):
return nil, gqlutils.NotFound(ctx, err)
case errors.Is(err, cookiebanner.ErrCategoriesBannerMismatch):
return nil, gqlutils.NotFoundf(ctx, "tracker resource or target category not found")
case errors.Is(err, cookiebanner.ErrSameResourceCategoryMove):
return nil, gqlutils.Invalidf(ctx, "tracker resource is already in the target category")
default:
r.logger.ErrorCtx(ctx, "cannot move tracker resource to category", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
return &types.MoveTrackerResourceToCategoryPayload{
TrackerResource: types.NewTrackerResourceNode(result.TrackerResource),
CookieBanner: types.NewCookieBanner(result.Banner),
}, nil
}
// CookieCategory is the resolver for the cookieCategory field.
func (r *trackerPatternResolver) CookieCategory(ctx context.Context, obj *types.TrackerPattern) (*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
}
// DetectedCount is the resolver for the detectedCount field.
func (r *trackerPatternResolver) DetectedCount(ctx context.Context, obj *types.TrackerPattern) (int, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrackerPatternGet)
if err != nil {
return 0, err
}
count, err := r.cookieBanner.CountDetectedTrackersByPatternID(ctx, scope, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count detected trackers", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// ThirdParty is the resolver for the thirdParty field.
func (r *trackerPatternResolver) ThirdParty(ctx context.Context, obj *types.TrackerPattern) (*types.ThirdParty, error) {
if obj.ThirdPartyID == nil {
return nil, nil
}
if _, err := r.authorize(ctx, *obj.ThirdPartyID, probo.ActionThirdPartyGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
tp, err := loaders.ThirdParty.Load(ctx, *obj.ThirdPartyID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get tracker pattern third party", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewThirdParty(tp), nil
}
// CommonThirdParty is the resolver for the commonThirdParty field.
//
// The org-scoped thirdParty takes priority: when ThirdPartyID is set we
// short-circuit to nil so the chained common-tracker-pattern lookup is
// never paid for.
func (r *trackerPatternResolver) CommonThirdParty(ctx context.Context, obj *types.TrackerPattern) (*types.CommonThirdParty, error) {
if obj.ThirdPartyID != nil || obj.CommonTrackerPatternID == nil {
return nil, nil
}
identity := authn.IdentityFromContext(ctx)
if _, err := r.authorize(ctx, identity.ID, probo.ActionCommonThirdPartyGet); err != nil {
return nil, err
}
loaders := dataloader.FromContext(ctx)
pattern, err := loaders.CommonTrackerPattern.Load(ctx, *obj.CommonTrackerPatternID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get common tracker pattern", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if pattern.CommonThirdPartyID == nil {
return nil, nil
}
party, err := loaders.CommonThirdParty.Load(ctx, *pattern.CommonThirdPartyID)
if err != nil {
if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get common third party", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewCommonThirdParty(party), nil
}
// DetectedTrackers is the resolver for the detectedTrackers field.
func (r *trackerPatternResolver) DetectedTrackers(ctx context.Context, obj *types.TrackerPattern, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DetectedTrackerOrderBy) (*types.DetectedTrackerConnection, error) {
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrackerPatternGet)
if err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.DetectedTrackerOrderField]{
Field: coredata.DetectedTrackerOrderFieldLastDetectedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.DetectedTrackerOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
trackers, err := r.cookieBanner.ListDetectedTrackersForPattern(ctx, scope, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list detected trackers", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
p := page.NewPage(trackers, cursor)
return types.NewDetectedTrackerConnection(p, r, obj.ID), nil
}
// Permission is the resolver for the permission field.
func (r *trackerPatternResolver) Permission(ctx context.Context, obj *types.TrackerPattern, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *trackerPatternConnectionResolver) TotalCount(ctx context.Context, obj *types.TrackerPatternConnection) (int, error) {
scope, err := r.authorize(ctx, obj.ParentID, probo.ActionTrackerPatternList)
if err != nil {
return 0, err
}
var count int
switch obj.Resolver.(type) {
case *cookieCategoryResolver:
count, err = r.cookieBanner.CountTrackerPatternsForCategory(ctx, scope, obj.ParentID)
default:
filter := coredata.NewTrackerPatternFilter(nil, nil, nil)
if obj.Filter != nil {
filter = coredata.NewTrackerPatternFilter(nil, obj.Filter.CookieCategoryID, nil)
filter = filter.WithQuery(obj.Filter.Query).WithSource(obj.Filter.Source).WithTrackerType(obj.Filter.TrackerType)
}
count, err = r.cookieBanner.CountTrackerPatternsForBanner(ctx, scope, obj.ParentID, filter)
}
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count tracker patterns", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// CookieCategory is the resolver for the cookieCategory field.
func (r *trackerResourceResolver) CookieCategory(ctx context.Context, obj *types.TrackerResource) (*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
}
// Permission is the resolver for the permission field.
func (r *trackerResourceResolver) Permission(ctx context.Context, obj *types.TrackerResource, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *trackerResourceConnectionResolver) TotalCount(ctx context.Context, obj *types.TrackerResourceConnection) (int, error) {
scope, err := r.authorize(ctx, obj.ParentID, probo.ActionTrackerResourceList)
if err != nil {
return 0, err
}
var count int
switch obj.Resolver.(type) {
case *cookieCategoryResolver:
count, err = r.cookieBanner.CountTrackerResourcesForCategory(ctx, scope, obj.ParentID)
default:
filter := coredata.NewTrackerResourceFilter(nil, nil)
if obj.Filter != nil {
filter = filter.WithQuery(obj.Filter.Query).WithResourceType(obj.Filter.Type)
}
count, err = r.cookieBanner.CountUncategorisedTrackerResources(ctx, scope, obj.ParentID, filter)
}
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count tracker resources", log.Error(err))
return 0, gqlutils.Internal(ctx)
}
return count, nil
}
// CookieBanner returns schema.CookieBannerResolver implementation.
func (r *Resolver) CookieBanner() schema.CookieBannerResolver { return &cookieBannerResolver{r} }
// CookieBannerConnection returns schema.CookieBannerConnectionResolver implementation.
func (r *Resolver) CookieBannerConnection() schema.CookieBannerConnectionResolver {
return &cookieBannerConnectionResolver{r}
}
// CookieBannerVersion returns schema.CookieBannerVersionResolver implementation.
func (r *Resolver) CookieBannerVersion() schema.CookieBannerVersionResolver {
return &cookieBannerVersionResolver{r}
}
// CookieCategory returns schema.CookieCategoryResolver implementation.
func (r *Resolver) CookieCategory() schema.CookieCategoryResolver { return &cookieCategoryResolver{r} }
// CookieCategoryConnection returns schema.CookieCategoryConnectionResolver implementation.
func (r *Resolver) CookieCategoryConnection() schema.CookieCategoryConnectionResolver {
return &cookieCategoryConnectionResolver{r}
}
// DetectedTrackerConnection returns schema.DetectedTrackerConnectionResolver implementation.
func (r *Resolver) DetectedTrackerConnection() schema.DetectedTrackerConnectionResolver {
return &detectedTrackerConnectionResolver{r}
}
// TrackerPattern returns schema.TrackerPatternResolver implementation.
func (r *Resolver) TrackerPattern() schema.TrackerPatternResolver { return &trackerPatternResolver{r} }
// TrackerPatternConnection returns schema.TrackerPatternConnectionResolver implementation.
func (r *Resolver) TrackerPatternConnection() schema.TrackerPatternConnectionResolver {
return &trackerPatternConnectionResolver{r}
}
// TrackerResource returns schema.TrackerResourceResolver implementation.
func (r *Resolver) TrackerResource() schema.TrackerResourceResolver {
return &trackerResourceResolver{r}
}
// TrackerResourceConnection returns schema.TrackerResourceConnectionResolver implementation.
func (r *Resolver) TrackerResourceConnection() schema.TrackerResourceConnectionResolver {
return &trackerResourceConnectionResolver{r}
}
type cookieBannerResolver struct{ *Resolver }
type cookieBannerConnectionResolver struct{ *Resolver }
type cookieBannerVersionResolver struct{ *Resolver }
type cookieCategoryResolver struct{ *Resolver }
type cookieCategoryConnectionResolver struct{ *Resolver }
type detectedTrackerConnectionResolver struct{ *Resolver }
type trackerPatternResolver struct{ *Resolver }
type trackerPatternConnectionResolver struct{ *Resolver }
type trackerResourceResolver struct{ *Resolver }
type trackerResourceConnectionResolver struct{ *Resolver }