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.94 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 } // PolicyDocument is the resolver for the policyDocument field. func (r *cookieBannerResolver) PolicyDocument(ctx context.Context, obj *types.CookieBanner) (*types.Document, error) { if obj.PolicyDocument == nil { return nil, nil } if _, err := r.authorize(ctx, obj.PolicyDocument.ID, probo.ActionDocumentGet); err != nil { return nil, err } loaders := dataloader.FromContext(ctx) document, err := loaders.Document.Load(ctx, obj.PolicyDocument.ID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { return nil, nil } r.logger.ErrorCtx(ctx, "cannot get policy document", log.Error(err)) return nil, gqlutils.Internal(ctx) } return types.NewDocument(document), 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, TrackerType: c.TrackerType, 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 } // RegenerateCookieBannerTrackerPolicy is the resolver for the regenerateCookieBannerTrackerPolicy field. func (r *mutationResolver) RegenerateCookieBannerTrackerPolicy(ctx context.Context, input types.RegenerateCookieBannerTrackerPolicyInput) (*types.RegenerateCookieBannerTrackerPolicyPayload, error) { scope, err := r.authorize(ctx, input.CookieBannerID, probo.ActionCookieBannerRegeneratePolicy) if err != nil { return nil, err } banner, err := r.cookieBanner.RegenerateTrackerPolicy(ctx, scope, input.CookieBannerID) if err != nil { if errors.Is(err, cookiebanner.ErrBannerNotFound) { return nil, gqlutils.NotFound(ctx, err) } if errors.Is(err, cookiebanner.ErrNoPublishedVersion) { return nil, gqlutils.Conflict(ctx, err) } r.logger.ErrorCtx(ctx, "cannot regenerate cookie banner tracker policy", log.Error(err)) return nil, gqlutils.Internal(ctx) } return &types.RegenerateCookieBannerTrackerPolicyPayload{ 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 } cookieBannerConnectionResolver struct{ *Resolver } cookieBannerVersionResolver struct{ *Resolver } cookieCategoryResolver struct{ *Resolver } cookieCategoryConnectionResolver struct{ *Resolver } detectedTrackerConnectionResolver struct{ *Resolver } trackerPatternResolver struct{ *Resolver } trackerPatternConnectionResolver struct{ *Resolver } trackerResourceResolver struct{ *Resolver } trackerResourceConnectionResolver struct{ *Resolver } )