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:
@@ -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}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -517,7 +517,6 @@ input CreateTrackerPatternInput {
|
||||
|
||||
input UpdateTrackerPatternInput {
|
||||
trackerPatternId: ID!
|
||||
displayName: String
|
||||
maxAgeSeconds: Int @goField(omittable: true)
|
||||
description: String
|
||||
excluded: Boolean
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user