graphql: add TrackerResource schema, resolvers, and IAM actions
Add the full GraphQL surface for the new tracker_resources table:
- TrackerResourceType enum (SCRIPT, IFRAME), TrackerResource node type
with connection/edge/order/filter, fields on CookieBanner
(uncategorisedTrackerResources) and CookieCategory (trackerResources).
- Mutations: createTrackerResource, updateTrackerResource,
deleteTrackerResource, moveTrackerResourceToCategory with
inputs and payloads.
- Resolvers for all mutations, connection fields, field resolvers
(cookieCategory, permission), and totalCount.
- IAM actions: core:tracker-resource:{get,list,create,update,delete}.
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -410,6 +410,13 @@ const (
|
|||||||
ActionTrackerPatternUpdate = "core:tracker-pattern:update"
|
ActionTrackerPatternUpdate = "core:tracker-pattern:update"
|
||||||
ActionTrackerPatternDelete = "core:tracker-pattern:delete"
|
ActionTrackerPatternDelete = "core:tracker-pattern:delete"
|
||||||
|
|
||||||
|
// TrackerResource actions
|
||||||
|
ActionTrackerResourceGet = "core:tracker-resource:get"
|
||||||
|
ActionTrackerResourceList = "core:tracker-resource:list"
|
||||||
|
ActionTrackerResourceCreate = "core:tracker-resource:create"
|
||||||
|
ActionTrackerResourceUpdate = "core:tracker-resource:update"
|
||||||
|
ActionTrackerResourceDelete = "core:tracker-resource:delete"
|
||||||
|
|
||||||
// CookieConsentRecord actions
|
// CookieConsentRecord actions
|
||||||
ActionCookieConsentRecordList = "core:cookie-consent-record:list"
|
ActionCookieConsentRecordList = "core:cookie-consent-record:list"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -213,6 +213,42 @@ func (r *cookieBannerResolver) UncategorisedTrackerPatterns(ctx context.Context,
|
|||||||
return types.NewTrackerPatternConnectionWithFilter(p, r, obj.ID, filter), nil
|
return types.NewTrackerPatternConnectionWithFilter(p, r, obj.ID, filter), 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) {
|
||||||
|
if err := r.authorize(ctx, obj.ID, probo.ActionTrackerResourceList); 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)
|
||||||
|
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||||
|
|
||||||
|
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.
|
// Permission is the resolver for the permission field.
|
||||||
func (r *cookieBannerResolver) Permission(ctx context.Context, obj *types.CookieBanner, action string) (bool, error) {
|
func (r *cookieBannerResolver) Permission(ctx context.Context, obj *types.CookieBanner, action string) (bool, error) {
|
||||||
return r.Resolver.Permission(ctx, obj, action)
|
return r.Resolver.Permission(ctx, obj, action)
|
||||||
@@ -333,6 +369,37 @@ func (r *cookieCategoryResolver) TrackerPatterns(ctx context.Context, obj *types
|
|||||||
return types.NewTrackerPatternConnection(p, r, obj.ID), nil
|
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) {
|
||||||
|
if err := r.authorize(ctx, obj.ID, probo.ActionTrackerResourceList); 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)
|
||||||
|
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||||
|
|
||||||
|
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.
|
// Permission is the resolver for the permission field.
|
||||||
func (r *cookieCategoryResolver) Permission(ctx context.Context, obj *types.CookieCategory, action string) (bool, error) {
|
func (r *cookieCategoryResolver) Permission(ctx context.Context, obj *types.CookieCategory, action string) (bool, error) {
|
||||||
return r.Resolver.Permission(ctx, obj, action)
|
return r.Resolver.Permission(ctx, obj, action)
|
||||||
@@ -929,6 +996,177 @@ func (r *mutationResolver) MoveTrackerPatternToCategory(ctx context.Context, inp
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateTrackerResource is the resolver for the createTrackerResource field.
|
||||||
|
func (r *mutationResolver) CreateTrackerResource(ctx context.Context, input types.CreateTrackerResourceInput) (*types.CreateTrackerResourcePayload, error) {
|
||||||
|
if err := r.authorize(ctx, input.CookieCategoryID, probo.ActionTrackerResourceCreate); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.CookieCategoryID)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if err := r.authorize(ctx, input.TrackerResourceID, probo.ActionTrackerResourceUpdate); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.TrackerResourceID)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if err := r.authorize(ctx, input.TrackerResourceID, probo.ActionTrackerResourceDelete); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.TrackerResourceID)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if err := r.authorize(ctx, input.TrackerResourceID, probo.ActionTrackerResourceUpdate); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.authorize(ctx, input.TargetCookieCategoryID, probo.ActionCookieCategoryUpdate); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := coredata.NewScopeFromObjectID(input.TrackerResourceID)
|
||||||
|
|
||||||
|
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")
|
||||||
|
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.
|
// CookieCategory is the resolver for the cookieCategory field.
|
||||||
func (r *trackerPatternResolver) CookieCategory(ctx context.Context, obj *types.TrackerPattern) (*types.CookieCategory, error) {
|
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 {
|
if err := r.authorize(ctx, obj.CookieCategory.ID, probo.ActionCookieCategoryGet); err != nil {
|
||||||
@@ -993,6 +1231,57 @@ func (r *trackerPatternConnectionResolver) TotalCount(ctx context.Context, obj *
|
|||||||
return count, nil
|
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 := coredata.NewScopeFromObjectID(obj.ParentID)
|
||||||
|
|
||||||
|
var count int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
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.
|
// CookieBanner returns schema.CookieBannerResolver implementation.
|
||||||
func (r *Resolver) CookieBanner() schema.CookieBannerResolver { return &cookieBannerResolver{r} }
|
func (r *Resolver) CookieBanner() schema.CookieBannerResolver { return &cookieBannerResolver{r} }
|
||||||
|
|
||||||
@@ -1022,6 +1311,16 @@ func (r *Resolver) TrackerPatternConnection() schema.TrackerPatternConnectionRes
|
|||||||
return &trackerPatternConnectionResolver{r}
|
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 cookieBannerResolver struct{ *Resolver }
|
||||||
type cookieBannerConnectionResolver struct{ *Resolver }
|
type cookieBannerConnectionResolver struct{ *Resolver }
|
||||||
type cookieBannerVersionResolver struct{ *Resolver }
|
type cookieBannerVersionResolver struct{ *Resolver }
|
||||||
@@ -1029,3 +1328,5 @@ type cookieCategoryResolver struct{ *Resolver }
|
|||||||
type cookieCategoryConnectionResolver struct{ *Resolver }
|
type cookieCategoryConnectionResolver struct{ *Resolver }
|
||||||
type trackerPatternResolver struct{ *Resolver }
|
type trackerPatternResolver struct{ *Resolver }
|
||||||
type trackerPatternConnectionResolver struct{ *Resolver }
|
type trackerPatternConnectionResolver struct{ *Resolver }
|
||||||
|
type trackerResourceResolver struct{ *Resolver }
|
||||||
|
type trackerResourceConnectionResolver struct{ *Resolver }
|
||||||
|
|||||||
@@ -152,6 +152,15 @@ type CookieBanner implements Node {
|
|||||||
filter: TrackerPatternFilter
|
filter: TrackerPatternFilter
|
||||||
): TrackerPatternConnection @goField(forceResolver: true)
|
): TrackerPatternConnection @goField(forceResolver: true)
|
||||||
|
|
||||||
|
uncategorisedTrackerResources(
|
||||||
|
first: Int
|
||||||
|
after: CursorKey
|
||||||
|
last: Int
|
||||||
|
before: CursorKey
|
||||||
|
orderBy: TrackerResourceOrder
|
||||||
|
filter: TrackerResourceFilter
|
||||||
|
): TrackerResourceConnection @goField(forceResolver: true)
|
||||||
|
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
|
|
||||||
@@ -185,6 +194,14 @@ type CookieCategory implements Node {
|
|||||||
orderBy: TrackerPatternOrder
|
orderBy: TrackerPatternOrder
|
||||||
): TrackerPatternConnection @goField(forceResolver: true)
|
): TrackerPatternConnection @goField(forceResolver: true)
|
||||||
|
|
||||||
|
trackerResources(
|
||||||
|
first: Int
|
||||||
|
after: CursorKey
|
||||||
|
last: Int
|
||||||
|
before: CursorKey
|
||||||
|
orderBy: TrackerResourceOrder
|
||||||
|
): TrackerResourceConnection @goField(forceResolver: true)
|
||||||
|
|
||||||
createdAt: Datetime!
|
createdAt: Datetime!
|
||||||
updatedAt: Datetime!
|
updatedAt: Datetime!
|
||||||
|
|
||||||
@@ -281,6 +298,86 @@ input TrackerPatternFilter
|
|||||||
trackerType: TrackerType
|
trackerType: TrackerType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum TrackerResourceType
|
||||||
|
@goModel(model: "go.probo.inc/probo/pkg/coredata.TrackerResourceType") {
|
||||||
|
SCRIPT
|
||||||
|
@goEnum(
|
||||||
|
value: "go.probo.inc/probo/pkg/coredata.TrackerResourceTypeScript"
|
||||||
|
)
|
||||||
|
IFRAME
|
||||||
|
@goEnum(
|
||||||
|
value: "go.probo.inc/probo/pkg/coredata.TrackerResourceTypeIframe"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TrackerResourceOrderField
|
||||||
|
@goModel(
|
||||||
|
model: "go.probo.inc/probo/pkg/coredata.TrackerResourceOrderField"
|
||||||
|
) {
|
||||||
|
CREATED_AT
|
||||||
|
@goEnum(
|
||||||
|
value: "go.probo.inc/probo/pkg/coredata.TrackerResourceOrderFieldCreatedAt"
|
||||||
|
)
|
||||||
|
LAST_DETECTED_AT
|
||||||
|
@goEnum(
|
||||||
|
value: "go.probo.inc/probo/pkg/coredata.TrackerResourceOrderFieldLastDetectedAt"
|
||||||
|
)
|
||||||
|
ORIGIN
|
||||||
|
@goEnum(
|
||||||
|
value: "go.probo.inc/probo/pkg/coredata.TrackerResourceOrderFieldOrigin"
|
||||||
|
)
|
||||||
|
UPDATED_AT
|
||||||
|
@goEnum(
|
||||||
|
value: "go.probo.inc/probo/pkg/coredata.TrackerResourceOrderFieldUpdatedAt"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrackerResource implements Node {
|
||||||
|
id: ID!
|
||||||
|
cookieCategory: CookieCategory @goField(forceResolver: true)
|
||||||
|
type: TrackerResourceType!
|
||||||
|
origin: String!
|
||||||
|
path: String!
|
||||||
|
displayName: String!
|
||||||
|
description: String!
|
||||||
|
excluded: Boolean!
|
||||||
|
lastDetectedAt: Datetime
|
||||||
|
createdAt: Datetime!
|
||||||
|
updatedAt: Datetime!
|
||||||
|
|
||||||
|
permission(action: String!): Boolean! @goField(forceResolver: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrackerResourceConnection
|
||||||
|
@goModel(
|
||||||
|
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrackerResourceConnection"
|
||||||
|
) {
|
||||||
|
totalCount: Int! @goField(forceResolver: true)
|
||||||
|
edges: [TrackerResourceEdge!]!
|
||||||
|
pageInfo: PageInfo!
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrackerResourceEdge {
|
||||||
|
cursor: CursorKey!
|
||||||
|
node: TrackerResource!
|
||||||
|
}
|
||||||
|
|
||||||
|
input TrackerResourceOrder
|
||||||
|
@goModel(
|
||||||
|
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrackerResourceOrderBy"
|
||||||
|
) {
|
||||||
|
direction: OrderDirection!
|
||||||
|
field: TrackerResourceOrderField!
|
||||||
|
}
|
||||||
|
|
||||||
|
input TrackerResourceFilter
|
||||||
|
@goModel(
|
||||||
|
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.TrackerResourceFilter"
|
||||||
|
) {
|
||||||
|
query: String
|
||||||
|
type: TrackerResourceType
|
||||||
|
}
|
||||||
|
|
||||||
type CookieBannerVersion implements Node {
|
type CookieBannerVersion implements Node {
|
||||||
id: ID!
|
id: ID!
|
||||||
version: Int!
|
version: Int!
|
||||||
@@ -378,6 +475,18 @@ extend type Mutation {
|
|||||||
moveTrackerPatternToCategory(
|
moveTrackerPatternToCategory(
|
||||||
input: MoveTrackerPatternToCategoryInput!
|
input: MoveTrackerPatternToCategoryInput!
|
||||||
): MoveTrackerPatternToCategoryPayload!
|
): MoveTrackerPatternToCategoryPayload!
|
||||||
|
createTrackerResource(
|
||||||
|
input: CreateTrackerResourceInput!
|
||||||
|
): CreateTrackerResourcePayload!
|
||||||
|
updateTrackerResource(
|
||||||
|
input: UpdateTrackerResourceInput!
|
||||||
|
): UpdateTrackerResourcePayload!
|
||||||
|
deleteTrackerResource(
|
||||||
|
input: DeleteTrackerResourceInput!
|
||||||
|
): DeleteTrackerResourcePayload!
|
||||||
|
moveTrackerResourceToCategory(
|
||||||
|
input: MoveTrackerResourceToCategoryInput!
|
||||||
|
): MoveTrackerResourceToCategoryPayload!
|
||||||
}
|
}
|
||||||
|
|
||||||
input CreateCookieBannerInput {
|
input CreateCookieBannerInput {
|
||||||
@@ -542,3 +651,48 @@ type MoveTrackerPatternToCategoryPayload {
|
|||||||
trackerPattern: TrackerPattern!
|
trackerPattern: TrackerPattern!
|
||||||
cookieBanner: CookieBanner!
|
cookieBanner: CookieBanner!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input CreateTrackerResourceInput {
|
||||||
|
cookieCategoryId: ID!
|
||||||
|
type: TrackerResourceType!
|
||||||
|
origin: String!
|
||||||
|
path: String!
|
||||||
|
displayName: String!
|
||||||
|
description: String
|
||||||
|
}
|
||||||
|
|
||||||
|
input UpdateTrackerResourceInput {
|
||||||
|
trackerResourceId: ID!
|
||||||
|
displayName: String
|
||||||
|
description: String
|
||||||
|
excluded: Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
input DeleteTrackerResourceInput {
|
||||||
|
trackerResourceId: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
input MoveTrackerResourceToCategoryInput {
|
||||||
|
trackerResourceId: ID!
|
||||||
|
targetCookieCategoryId: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateTrackerResourcePayload {
|
||||||
|
trackerResourceEdge: TrackerResourceEdge!
|
||||||
|
cookieBanner: CookieBanner!
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateTrackerResourcePayload {
|
||||||
|
trackerResource: TrackerResource!
|
||||||
|
cookieBanner: CookieBanner!
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteTrackerResourcePayload {
|
||||||
|
deletedTrackerResourceId: ID!
|
||||||
|
cookieBanner: CookieBanner!
|
||||||
|
}
|
||||||
|
|
||||||
|
type MoveTrackerResourceToCategoryPayload {
|
||||||
|
trackerResource: TrackerResource!
|
||||||
|
cookieBanner: CookieBanner!
|
||||||
|
}
|
||||||
|
|||||||
99
pkg/server/api/console/v1/types/tracker_resource.go
Normal file
99
pkg/server/api/console/v1/types/tracker_resource.go
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
|
"go.probo.inc/probo/pkg/gid"
|
||||||
|
"go.probo.inc/probo/pkg/page"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
TrackerResourceOrderBy OrderBy[coredata.TrackerResourceOrderField]
|
||||||
|
|
||||||
|
TrackerResourceConnection struct {
|
||||||
|
TotalCount int
|
||||||
|
Edges []*TrackerResourceEdge
|
||||||
|
PageInfo PageInfo
|
||||||
|
|
||||||
|
Resolver any
|
||||||
|
ParentID gid.GID
|
||||||
|
Filter *TrackerResourceFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackerResourceFilter struct {
|
||||||
|
Query *string
|
||||||
|
Type *coredata.TrackerResourceType
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewTrackerResourceConnection(
|
||||||
|
p *page.Page[*coredata.TrackerResource, coredata.TrackerResourceOrderField],
|
||||||
|
parentType any,
|
||||||
|
parentID gid.GID,
|
||||||
|
) *TrackerResourceConnection {
|
||||||
|
edges := make([]*TrackerResourceEdge, len(p.Data))
|
||||||
|
|
||||||
|
for i := range edges {
|
||||||
|
edges[i] = NewTrackerResourceEdge(p.Data[i], p.Cursor.OrderBy.Field)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TrackerResourceConnection{
|
||||||
|
Edges: edges,
|
||||||
|
PageInfo: *NewPageInfo(p),
|
||||||
|
|
||||||
|
Resolver: parentType,
|
||||||
|
ParentID: parentID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTrackerResourceConnectionWithFilter(
|
||||||
|
p *page.Page[*coredata.TrackerResource, coredata.TrackerResourceOrderField],
|
||||||
|
parentType any,
|
||||||
|
parentID gid.GID,
|
||||||
|
filter *TrackerResourceFilter,
|
||||||
|
) *TrackerResourceConnection {
|
||||||
|
conn := NewTrackerResourceConnection(p, parentType, parentID)
|
||||||
|
conn.Filter = filter
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTrackerResourceEdge(tr *coredata.TrackerResource, orderBy coredata.TrackerResourceOrderField) *TrackerResourceEdge {
|
||||||
|
return &TrackerResourceEdge{
|
||||||
|
Cursor: tr.CursorKey(orderBy),
|
||||||
|
Node: NewTrackerResourceNode(tr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTrackerResourceNode(tr *coredata.TrackerResource) *TrackerResource {
|
||||||
|
return &TrackerResource{
|
||||||
|
ID: tr.ID,
|
||||||
|
CookieCategory: &CookieCategory{
|
||||||
|
ID: tr.CookieCategoryID,
|
||||||
|
CookieBanner: &CookieBanner{
|
||||||
|
ID: tr.CookieBannerID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: tr.ResourceType,
|
||||||
|
Origin: tr.Origin,
|
||||||
|
Path: tr.Path,
|
||||||
|
DisplayName: tr.DisplayName,
|
||||||
|
Description: tr.Description,
|
||||||
|
Excluded: tr.Excluded,
|
||||||
|
LastDetectedAt: tr.LastDetectedAt,
|
||||||
|
CreatedAt: tr.CreatedAt,
|
||||||
|
UpdatedAt: tr.UpdatedAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user