mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Limit workflow ID conditions to equality
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
@@ -11,8 +11,10 @@ import { FieldLabel, FormSelect } from "./workflowFormPrimitives";
|
||||
import {
|
||||
buildConditionExpression,
|
||||
conditionFieldsForTrigger,
|
||||
conditionOperatorsForField,
|
||||
conditionOperatorNeedsValue,
|
||||
CUSTOM_CONDITION_FIELD,
|
||||
defaultConditionOperatorForField,
|
||||
normalizeWebhookField,
|
||||
parseConditionExpression,
|
||||
} from "./workflowConditionExpression";
|
||||
@@ -38,7 +40,15 @@ function initialEditorState(
|
||||
triggerType: TriggerType,
|
||||
): ParsedConditionExpression {
|
||||
const parsed = parseConditionExpression(value, triggerType);
|
||||
if (parsed) return parsed;
|
||||
if (parsed) {
|
||||
const supportedOperators = conditionOperatorsForField(parsed.field);
|
||||
return supportedOperators.includes(parsed.operator)
|
||||
? parsed
|
||||
: {
|
||||
...parsed,
|
||||
operator: defaultConditionOperatorForField(parsed.field),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
field: value.trim() ? CUSTOM_CONDITION_FIELD : "",
|
||||
@@ -154,6 +164,8 @@ export function WorkflowConditionBuilder({
|
||||
};
|
||||
|
||||
const needsValue = conditionOperatorNeedsValue(editor.operator);
|
||||
const operatorOptions = conditionOperatorsForField(editor.field);
|
||||
const usesExactMatchOperators = operatorOptions.length === 2;
|
||||
const webhookFieldInvalid =
|
||||
editor.field === "webhook_field" &&
|
||||
editor.webhookField.length > 0 &&
|
||||
@@ -216,7 +228,7 @@ export function WorkflowConditionBuilder({
|
||||
}
|
||||
emitEditor({
|
||||
field: field.value,
|
||||
operator: "contains",
|
||||
operator: defaultConditionOperatorForField(field.value),
|
||||
value: "",
|
||||
webhookField: "",
|
||||
});
|
||||
@@ -264,9 +276,11 @@ export function WorkflowConditionBuilder({
|
||||
}
|
||||
value={editor.operator}
|
||||
>
|
||||
{Object.entries(OPERATOR_LABELS).map(([operator, label]) => (
|
||||
{operatorOptions.map((operator) => (
|
||||
<option key={operator} value={operator}>
|
||||
{label}
|
||||
{usesExactMatchOperators && operator === "equals"
|
||||
? "is"
|
||||
: OPERATOR_LABELS[operator]}
|
||||
</option>
|
||||
))}
|
||||
</FormSelect>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { test } from "node:test";
|
||||
import {
|
||||
buildConditionExpression,
|
||||
conditionFieldsForTrigger,
|
||||
conditionOperatorsForField,
|
||||
normalizeWebhookField,
|
||||
parseConditionExpression,
|
||||
} from "./workflowConditionExpression.ts";
|
||||
@@ -77,6 +78,21 @@ test("shows trigger-relevant fields", () => {
|
||||
assert.equal(conditionFieldsForTrigger("webhook")[0].value, "webhook_field");
|
||||
});
|
||||
|
||||
test("limits opaque identifiers to equality operators", () => {
|
||||
for (const field of [
|
||||
"trigger_author",
|
||||
"trigger_channel_id",
|
||||
"trigger_message_id",
|
||||
"future_resource_id",
|
||||
]) {
|
||||
assert.deepEqual(conditionOperatorsForField(field), [
|
||||
"equals",
|
||||
"not_equals",
|
||||
]);
|
||||
}
|
||||
assert.equal(conditionOperatorsForField("trigger_text").length, 8);
|
||||
});
|
||||
|
||||
test("parses generated conditions back into editor fields", () => {
|
||||
assert.deepEqual(
|
||||
parseConditionExpression(
|
||||
|
||||
@@ -13,6 +13,26 @@ export const CONDITION_OPERATORS = [
|
||||
|
||||
export type ConditionOperator = (typeof CONDITION_OPERATORS)[number];
|
||||
|
||||
const EXACT_MATCH_OPERATORS = [
|
||||
"equals",
|
||||
"not_equals",
|
||||
] as const satisfies readonly ConditionOperator[];
|
||||
|
||||
/** IDs and pubkeys are opaque identifiers, so partial string matching is not meaningful. */
|
||||
export function conditionOperatorsForField(
|
||||
field: string,
|
||||
): readonly ConditionOperator[] {
|
||||
return field === "trigger_author" || field.endsWith("_id")
|
||||
? EXACT_MATCH_OPERATORS
|
||||
: CONDITION_OPERATORS;
|
||||
}
|
||||
|
||||
export function defaultConditionOperatorForField(
|
||||
field: string,
|
||||
): ConditionOperator {
|
||||
return conditionOperatorsForField(field)[0];
|
||||
}
|
||||
|
||||
export type ConditionField = {
|
||||
label: string;
|
||||
value: string;
|
||||
|
||||
@@ -336,6 +336,9 @@ test("chooses a trigger channel condition from the live channel list", async ({
|
||||
const inspector = dialog.getByTestId("workflow-node-inspector");
|
||||
|
||||
await inspector.getByRole("button", { name: "Channel ID" }).click();
|
||||
const matchOperator = inspector.getByLabel("Match");
|
||||
await expect(matchOperator).toHaveValue("equals");
|
||||
await expect(matchOperator.locator("option")).toHaveText(["is", "is not"]);
|
||||
const channelCondition = inspector.getByRole("combobox", {
|
||||
name: "Channel ID",
|
||||
});
|
||||
@@ -356,7 +359,7 @@ test("chooses a trigger channel condition from the live channel list", async ({
|
||||
|
||||
await dialog.getByRole("tab", { name: "YAML" }).click();
|
||||
await expect(dialog.getByLabel("Workflow YAML")).toHaveValue(
|
||||
/str_contains\(trigger_channel_id, "[0-9a-f-]{36}"\)/,
|
||||
/trigger_channel_id == "[0-9a-f-]{36}"/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -448,7 +451,7 @@ test("chooses a trigger author condition from live user search", async ({
|
||||
|
||||
await dialog.getByRole("tab", { name: "YAML" }).click();
|
||||
await expect(dialog.getByLabel("Workflow YAML")).toHaveValue(
|
||||
/str_contains\(trigger_author,\s+"[0-9a-f]{64}"\)/,
|
||||
/trigger_author\s+==\s+"[0-9a-f]{64}"/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user