From b350839b1f189f02ffb7c52f249120886d41a97e Mon Sep 17 00:00:00 2001 From: "jared-outpost[bot]" Date: Sun, 2 Aug 2026 12:53:30 +0000 Subject: [PATCH] fix(alerts): seed logicType/conditions when issue alert edit adds first action filter A partial edit that introduces an action filter on a rule that had none (e.g. `edit --action`) built the filter from an empty object, emitting a DataConditionGroup with no logicType or conditions. The workflows API rejects a group without a logic type. Seed the same defaults the create path uses (`logicType: all`, `conditions: []`); existing filters keep their own values. Fixes #1335 --- .../cli/src/commands/alert/issues/edit.ts | 13 +++++- .../test/commands/alert/issues/edit.test.ts | 42 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/alert/issues/edit.ts b/packages/cli/src/commands/alert/issues/edit.ts index 94e32508e..c635ab2b5 100644 --- a/packages/cli/src/commands/alert/issues/edit.ts +++ b/packages/cli/src/commands/alert/issues/edit.ts @@ -123,7 +123,18 @@ function applyIssueEdits( const actionFilters = Array.isArray(body.actionFilters) ? (body.actionFilters as Record[]) : []; - const filter = (actionFilters[0] as Record) ?? {}; + const existingFilter = actionFilters[0] as + | Record + | undefined; + // When the rule has no action filter yet, a partial edit (e.g. only + // --action) must still emit a valid DataConditionGroup. Seed the same + // defaults the create path uses so the group carries a logicType and a + // conditions array; the workflows API rejects a group without a logic + // type. An existing filter keeps its own logicType/conditions. + const filter: Record = existingFilter ?? { + logicType: matchToLogicType(undefined), + conditions: [], + }; if (actions !== undefined) { filter.actions = actions; } diff --git a/packages/cli/test/commands/alert/issues/edit.test.ts b/packages/cli/test/commands/alert/issues/edit.test.ts index 8f0a2615a..e0cbd5cba 100644 --- a/packages/cli/test/commands/alert/issues/edit.test.ts +++ b/packages/cli/test/commands/alert/issues/edit.test.ts @@ -138,6 +138,48 @@ describe("alert issues edit", () => { }); }); + test("seeds logicType and conditions when the rule has no action filter yet", async () => { + const context = createContext(); + resolveSpy.mockResolvedValue({ targets: [sampleTarget] }); + getRuleSpy.mockResolvedValue(sampleRule); + getDocSpy.mockResolvedValue({ + id: "42", + name: "Rule Alpha", + enabled: true, + config: { frequency: 30 }, + triggers: { + logicType: "any-short", + conditions: [{ id: "old-condition" }], + }, + }); + putSpy.mockResolvedValue({ id: "42", name: "Rule Alpha", enabled: true }); + const func = (await editCommand.loader()) as unknown as ( + this: unknown, + flags: EditFlags, + arg: string + ) => Promise; + + await func.call( + context, + { action: ['{"id":"new-action"}'], json: true }, + "test-org/test-project/42" + ); + + expect(putSpy).toHaveBeenCalledWith("test-org", "42", { + id: "42", + name: "Rule Alpha", + enabled: true, + config: { frequency: 30 }, + triggers: { + logicType: "any-short", + conditions: [{ id: "old-condition" }], + }, + actionFilters: [ + { logicType: "all", conditions: [], actions: [{ id: "new-action" }] }, + ], + }); + }); + test("maps the workflow enabled field to a status label in output", async () => { const context = createContext(); resolveSpy.mockResolvedValue({ targets: [sampleTarget] });