Make tracker pattern displayName read-only

The displayName field was always predictable from pattern + matchType
and allowing edits added unnecessary complexity. Remove displayName
from UpdateTrackerPatternInput across all surfaces (GraphQL, MCP, CLI,
n8n) and make the frontend show it as non-editable text.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-05-06 19:31:23 +04:00
parent c0d0221be1
commit 31bfbefc45
13 changed files with 16 additions and 72 deletions

View File

@@ -251,12 +251,11 @@ export function DetectionPatternRow({ patternKey, connectionId }: DetectionPatte
});
};
const handleSaveEdit = (data: { displayName: string; description: string; maxAgeSeconds: number | null }) => {
const handleSaveEdit = (data: { description: string; maxAgeSeconds: number | null }) => {
updatePattern({
variables: {
input: {
trackerPatternId: pattern.id,
displayName: data.displayName,
description: data.description,
maxAgeSeconds: data.maxAgeSeconds,
},
@@ -278,7 +277,7 @@ export function DetectionPatternRow({ patternKey, connectionId }: DetectionPatte
if (isEditing) {
return (
<DetectionPatternRowEdit
displayName={pattern.displayName}
pattern={pattern.displayName}
description={pattern.description}
maxAgeSeconds={pattern.maxAgeSeconds ?? null}
isUpdating={isUpdating}

View File

@@ -18,22 +18,21 @@ import { Button, DurationInput, Input, Td, Tr } from "@probo/ui";
import { Controller, useForm } from "react-hook-form";
interface FormValues {
displayName: string;
duration: { value: string; unit: string };
description: string;
}
interface DetectionPatternRowEditProps {
displayName: string;
pattern: string;
description: string;
maxAgeSeconds: number | null;
isUpdating: boolean;
onSave: (data: { displayName: string; description: string; maxAgeSeconds: number | null }) => void;
onSave: (data: { description: string; maxAgeSeconds: number | null }) => void;
onCancel: () => void;
}
export function DetectionPatternRowEdit({
displayName,
pattern,
description,
maxAgeSeconds,
isUpdating,
@@ -45,7 +44,6 @@ export function DetectionPatternRowEdit({
const { register, handleSubmit, control } = useForm<FormValues>({
defaultValues: {
displayName,
duration: initial,
description,
},
@@ -53,7 +51,6 @@ export function DetectionPatternRowEdit({
const onSubmit = (data: FormValues) => {
onSave({
displayName: data.displayName,
description: data.description,
maxAgeSeconds: toMaxAgeSeconds(data.duration.value, data.duration.unit),
});
@@ -62,10 +59,7 @@ export function DetectionPatternRowEdit({
return (
<Tr>
<Td className="pr-3">
<Input
{...register("displayName")}
placeholder={__("Cookie name")}
/>
<span className="font-medium">{pattern}</span>
</Td>
<Td />
<Td className="pr-3">

View File

@@ -397,12 +397,10 @@ export function CategorySection({ categoryKey, connectionId }: CategorySectionPr
};
const handleSaveEditCookie = (patternId: string, cookie: CookieEntry) => {
if (!cookie.name.trim()) return;
updatePattern({
variables: {
input: {
trackerPatternId: patternId,
displayName: cookie.name,
maxAgeSeconds: cookie.maxAgeSeconds,
description: cookie.description,
excluded: cookie.excluded,

View File

@@ -33,7 +33,6 @@ export const editCookieRowFragment = graphql`
`;
interface CookieFormValues {
name: string;
duration: { value: string; unit: string };
description: string;
excluded: boolean;
@@ -58,7 +57,6 @@ export function EditCookieRow({
const { register, handleSubmit, control } = useForm<CookieFormValues>({
defaultValues: {
name: cookie.displayName,
duration: initial,
description: cookie.description,
excluded: cookie.excluded,
@@ -67,7 +65,7 @@ export function EditCookieRow({
const onSubmit = (data: CookieFormValues) => {
onSave({
name: data.name,
name: cookie.displayName,
maxAgeSeconds: toMaxAgeSeconds(data.duration.value, data.duration.unit),
description: data.description,
excluded: data.excluded,
@@ -90,10 +88,7 @@ export function EditCookieRow({
/>
)}
/>
<Input
{...register("name")}
placeholder={__("Cookie name")}
/>
<code className="text-sm font-mono">{cookie.displayName}</code>
</div>
</Td>
<Td />

View File

@@ -347,7 +347,6 @@ func TestCookieBannerVersioning_NoOpUpdates(t *testing.T) {
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"trackerPatternId": patternID,
"displayName": "GA Tracker",
"description": "Original description",
},
}, &result)
@@ -379,7 +378,7 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
const query = `
mutation UpdateTrackerPattern($input: UpdateTrackerPatternInput!) {
updateTrackerPattern(input: $input) {
trackerPattern { id displayName description }
trackerPattern { id description }
}
}
`
@@ -387,7 +386,6 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
var result struct {
UpdateTrackerPattern struct {
TrackerPattern struct {
DisplayName string `json:"displayName"`
Description string `json:"description"`
} `json:"trackerPattern"`
} `json:"updateTrackerPattern"`
@@ -396,12 +394,10 @@ func TestCookieBannerVersioning_ExcludedPattern(t *testing.T) {
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"trackerPatternId": patternID,
"displayName": "Renamed Excluded",
"description": "Now with notes",
},
}, &result)
require.NoError(t, err)
assert.Equal(t, "Renamed Excluded", result.UpdateTrackerPattern.TrackerPattern.DisplayName)
assert.Equal(t, "Now with notes", result.UpdateTrackerPattern.TrackerPattern.Description)
got := latestVersion(t, owner, bannerID)
@@ -604,7 +600,7 @@ func TestCookieBannerVersioning_RealChangesStillBumpVersion(t *testing.T) {
assert.Equal(t, "DRAFT", got.State)
})
t.Run("UpdateTrackerPattern displayName change on visible pattern creates a new draft", func(t *testing.T) {
t.Run("UpdateTrackerPattern description change on visible pattern creates a new draft", func(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
@@ -612,6 +608,7 @@ func TestCookieBannerVersioning_RealChangesStillBumpVersion(t *testing.T) {
categoryID := factory.CreateCookieCategory(owner, bannerID, factory.Attrs{"slug": "real-change"})
patternID := factory.CreateTrackerPattern(owner, categoryID, factory.Attrs{
"displayName": "Original",
"description": "Original desc",
})
published := publishBanner(t, owner, bannerID)
@@ -626,7 +623,7 @@ func TestCookieBannerVersioning_RealChangesStillBumpVersion(t *testing.T) {
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"trackerPatternId": patternID,
"displayName": "Renamed",
"description": "Updated desc",
},
}, &result)
require.NoError(t, err)

View File

@@ -192,7 +192,7 @@ func TestTrackerPattern_Create(t *testing.T) {
func TestTrackerPattern_Update(t *testing.T) {
t.Parallel()
t.Run("update displayName and description", func(t *testing.T) {
t.Run("update description", func(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
@@ -234,14 +234,13 @@ func TestTrackerPattern_Update(t *testing.T) {
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"trackerPatternId": patternID,
"displayName": "Updated Name",
"description": "Updated description",
},
}, &result)
require.NoError(t, err)
assert.Equal(t, patternID, result.UpdateTrackerPattern.TrackerPattern.ID)
assert.Equal(t, "Updated Name", result.UpdateTrackerPattern.TrackerPattern.DisplayName)
assert.Equal(t, "Original Name", result.UpdateTrackerPattern.TrackerPattern.DisplayName)
assert.Equal(t, "Updated description", result.UpdateTrackerPattern.TrackerPattern.Description)
assert.Equal(t, bannerID, result.UpdateTrackerPattern.CookieBanner.ID)
})
@@ -655,7 +654,7 @@ func TestTrackerPattern_RBAC(t *testing.T) {
`, map[string]any{
"input": map[string]any{
"trackerPatternId": patternID,
"displayName": "Updated by Viewer",
"description": "Updated by Viewer",
},
})
testutil.RequireForbiddenError(t, err, "viewer should not be able to update tracker pattern")

View File

@@ -30,19 +30,6 @@ export const description: INodeProperties[] = [
description: 'The ID of the tracker pattern to update',
required: true,
},
{
displayName: 'Display Name',
name: 'displayName',
type: 'string',
displayOptions: {
show: {
resource: ['trackerPattern'],
operation: ['update'],
},
},
default: '',
description: 'The display name for the tracker pattern',
},
{
displayName: 'Excluded',
name: 'excluded',
@@ -115,7 +102,6 @@ export async function execute(
itemIndex: number,
): Promise<INodeExecutionData> {
const trackerPatternId = this.getNodeParameter('trackerPatternId', itemIndex) as string;
const displayName = this.getNodeParameter('displayName', itemIndex, '') as string;
const excluded = this.getNodeParameter('excluded', itemIndex, '') as string;
const patternDescription = this.getNodeParameter('patternDescription', itemIndex, '') as string;
const additionalFields = this.getNodeParameter('additionalFields', itemIndex, {}) as {
@@ -146,7 +132,6 @@ export async function execute(
`;
const input: Record<string, unknown> = { trackerPatternId };
if (displayName) input.displayName = displayName;
if (excluded) input.excluded = excluded === 'true';
if (patternDescription) input.description = patternDescription;
if (additionalFields.maxAgeSeconds !== undefined) {

View File

@@ -48,7 +48,6 @@ type updateResponse struct {
func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
var (
flagDisplayName string
flagDescription string
flagMaxAge int
flagExcluded bool
@@ -79,9 +78,6 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
input := map[string]any{"trackerPatternId": args[0]}
if cmd.Flags().Changed("display-name") {
input["displayName"] = flagDisplayName
}
if cmd.Flags().Changed("description") {
input["description"] = flagDescription
}
@@ -113,7 +109,6 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
},
}
cmd.Flags().StringVar(&flagDisplayName, "display-name", "", "Display name")
cmd.Flags().StringVar(&flagDescription, "description", "", "Description")
cmd.Flags().IntVar(&flagMaxAge, "max-age-seconds", 0, "Maximum age in seconds")
cmd.Flags().BoolVar(&flagExcluded, "excluded", false, "Exclude pattern from consent banner")

View File

@@ -149,7 +149,6 @@ type (
UpdateTrackerPatternRequest struct {
TrackerPatternID gid.GID
DisplayName *string
MaxAgeSeconds **int
Description *string
Excluded *bool
@@ -357,9 +356,6 @@ func (r *UpdateTrackerPatternRequest) Validate() error {
v := validator.New()
v.Check(r.TrackerPatternID, "tracker_pattern_id", validator.Required(), validator.GID(coredata.TrackerPatternEntityType))
if r.DisplayName != nil {
v.Check(*r.DisplayName, "display_name", validator.Required(), validator.SafeTextNoNewLine(255))
}
if r.Description != nil {
v.Check(*r.Description, "description", validator.SafeText(1000))
}
@@ -2258,20 +2254,16 @@ func (s *Service) UpdateTrackerPattern(
return fmt.Errorf("cannot load tracker pattern: %w", err)
}
displayNameChanged := req.DisplayName != nil && *req.DisplayName != pattern.DisplayName
maxAgeChanged := req.MaxAgeSeconds != nil && !ptrEqual(*req.MaxAgeSeconds, pattern.MaxAgeSeconds)
descChanged := req.Description != nil && *req.Description != pattern.Description
excludedChanged := req.Excluded != nil && *req.Excluded != pattern.Excluded
if !displayNameChanged && !maxAgeChanged && !descChanged && !excludedChanged {
if !maxAgeChanged && !descChanged && !excludedChanged {
return nil
}
staysExcluded := pattern.Excluded && (req.Excluded == nil || *req.Excluded)
if req.DisplayName != nil {
pattern.DisplayName = *req.DisplayName
}
if req.MaxAgeSeconds != nil {
pattern.MaxAgeSeconds = *req.MaxAgeSeconds
}

View File

@@ -823,7 +823,6 @@ func (r *mutationResolver) UpdateTrackerPattern(ctx context.Context, input types
scope,
cookiebanner.UpdateTrackerPatternRequest{
TrackerPatternID: input.TrackerPatternID,
DisplayName: input.DisplayName,
MaxAgeSeconds: gqlutils.UnwrapOmittable(input.MaxAgeSeconds),
Description: input.Description,
Excluded: input.Excluded,

View File

@@ -517,7 +517,6 @@ input CreateTrackerPatternInput {
input UpdateTrackerPatternInput {
trackerPatternId: ID!
displayName: String
maxAgeSeconds: Int @goField(omittable: true)
description: String
excluded: Boolean

View File

@@ -4917,9 +4917,6 @@ func (r *Resolver) UpdateTrackerPatternTool(ctx context.Context, req *mcp.CallTo
r.MustAuthorize(ctx, input.ID, probo.ActionTrackerPatternUpdate)
scope := coredata.NewScopeFromObjectID(input.ID)
updateReq := cookiebanner.UpdateTrackerPatternRequest{TrackerPatternID: input.ID}
if v := UnwrapOmittable(input.DisplayName); v != nil && *v != nil {
updateReq.DisplayName = *v
}
if input.MaxAgeSeconds.IsSet() {
val, _ := input.MaxAgeSeconds.Value()
updateReq.MaxAgeSeconds = &val

View File

@@ -10012,11 +10012,6 @@ components:
properties:
id:
$ref: "#/components/schemas/GID"
display_name:
type:
- string
- "null"
go.probo.inc/mcpgen/omittable: true
max_age_seconds:
type:
- integer