mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 00:02:13 +00:00
fix: persist ws_error_handler_muted for flows in create/update (#7797)
This commit is contained in:
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO flow (\n workspace_id, path, summary, description,\n dependency_job, lock_error_logs, draft_only, tag,\n dedicated_worker, visible_to_runner_only, on_behalf_of_email,\n value, schema, edited_by, edited_at\n ) VALUES (\n $1, $2, $3, $4,\n NULL, '', $5, $6,\n $7, $8, $9,\n $10, $11::text::json, $12, now()\n )",
|
||||
"query": "INSERT INTO flow (\n workspace_id, path, summary, description,\n dependency_job, lock_error_logs, draft_only, tag,\n dedicated_worker, visible_to_runner_only, on_behalf_of_email,\n ws_error_handler_muted,\n value, schema, edited_by, edited_at\n ) VALUES (\n $1, $2, $3, $4,\n NULL, '', $5, $6,\n $7, $8, $9,\n $10,\n $11, $12::text::json, $13, now()\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -14,6 +14,7 @@
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Text",
|
||||
"Bool",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar"
|
||||
@@ -21,5 +22,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "081dc94a7d0fdaade77cfb593a025d8c48d7eab3dbb30ca0b43fb1ef45d8d8bd"
|
||||
"hash": "6bde827da007b470b9d0acccfc3e00ce6aac650b9138a236f34c614eed753849"
|
||||
}
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n flow\n SET\n path = $1,\n summary = $2,\n description = $3,\n dependency_job = NULL,\n lock_error_logs = '',\n draft_only = NULL,\n tag = $4,\n dedicated_worker = $5,\n visible_to_runner_only = $6,\n on_behalf_of_email = $7,\n value = $8,\n schema = $9::text::json,\n edited_by = $10,\n edited_at = now()\n WHERE\n path = $11 AND workspace_id = $12",
|
||||
"query": "\n UPDATE\n flow\n SET\n path = $1,\n summary = $2,\n description = $3,\n dependency_job = NULL,\n lock_error_logs = '',\n draft_only = NULL,\n tag = $4,\n dedicated_worker = $5,\n visible_to_runner_only = $6,\n on_behalf_of_email = $7,\n ws_error_handler_muted = $8,\n value = $9,\n schema = $10::text::json,\n edited_by = $11,\n edited_at = now()\n WHERE\n path = $12 AND workspace_id = $13",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -12,6 +12,7 @@
|
||||
"Bool",
|
||||
"Bool",
|
||||
"Text",
|
||||
"Bool",
|
||||
"Jsonb",
|
||||
"Text",
|
||||
"Varchar",
|
||||
@@ -21,5 +22,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "207a0721b6f0b8b6ddd4120343eba524a2bc1e9047bdde5f568af4d993dbb74c"
|
||||
"hash": "77ac7257be02fb04c4b3213e2221e6f60621b4b2909d770de744ef5671e12ed9"
|
||||
}
|
||||
@@ -473,12 +473,14 @@ async fn create_flow(
|
||||
workspace_id, path, summary, description,
|
||||
dependency_job, lock_error_logs, draft_only, tag,
|
||||
dedicated_worker, visible_to_runner_only, on_behalf_of_email,
|
||||
ws_error_handler_muted,
|
||||
value, schema, edited_by, edited_at
|
||||
) VALUES (
|
||||
$1, $2, $3, $4,
|
||||
NULL, '', $5, $6,
|
||||
$7, $8, $9,
|
||||
$10, $11::text::json, $12, now()
|
||||
$10,
|
||||
$11, $12::text::json, $13, now()
|
||||
)"#,
|
||||
w_id,
|
||||
nf.path,
|
||||
@@ -489,6 +491,7 @@ async fn create_flow(
|
||||
nf.dedicated_worker,
|
||||
nf.visible_to_runner_only.unwrap_or(false),
|
||||
nf.on_behalf_of_email.and(Some(&authed.email)),
|
||||
nf.ws_error_handler_muted.unwrap_or(false),
|
||||
sqlx::types::Json(&nf.value) as _,
|
||||
schema_str,
|
||||
&authed.username,
|
||||
@@ -897,12 +900,13 @@ async fn update_flow(
|
||||
dedicated_worker = $5,
|
||||
visible_to_runner_only = $6,
|
||||
on_behalf_of_email = $7,
|
||||
value = $8,
|
||||
schema = $9::text::json,
|
||||
edited_by = $10,
|
||||
ws_error_handler_muted = $8,
|
||||
value = $9,
|
||||
schema = $10::text::json,
|
||||
edited_by = $11,
|
||||
edited_at = now()
|
||||
WHERE
|
||||
path = $11 AND workspace_id = $12",
|
||||
path = $12 AND workspace_id = $13",
|
||||
if is_new_path { flow_path } else { &nf.path },
|
||||
nf.summary,
|
||||
nf.description.as_deref().unwrap_or(""),
|
||||
@@ -910,6 +914,7 @@ async fn update_flow(
|
||||
nf.dedicated_worker,
|
||||
nf.visible_to_runner_only.unwrap_or(false),
|
||||
nf.on_behalf_of_email.and(Some(&authed.email)),
|
||||
nf.ws_error_handler_muted.unwrap_or(false),
|
||||
sqlx::types::Json(&nf.value) as _,
|
||||
schema_str,
|
||||
authed.username,
|
||||
|
||||
@@ -906,7 +906,9 @@ Deno.test("readDirRecursive reads all files correctly", async () => {
|
||||
// Integration Tests (use withTestBackend for automated backend setup)
|
||||
// =============================================================================
|
||||
|
||||
import { yamlParseFile } from "../deps.ts";
|
||||
import { withTestBackend } from "./test_backend.ts";
|
||||
import { shouldSkipOnCI } from "./cargo_backend.ts";
|
||||
|
||||
Deno.test({
|
||||
name: "Integration: Pull creates correct local structure",
|
||||
@@ -1950,3 +1952,212 @@ excludes: []
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// ws_error_handler_muted Persistence Tests
|
||||
// =============================================================================
|
||||
|
||||
Deno.test({
|
||||
name: "Integration: Script ws_error_handler_muted is persisted through push/pull",
|
||||
ignore: shouldSkipOnCI(), // Requires EE features
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
async fn() {
|
||||
await withTestBackend(async (backend, tempDir) => {
|
||||
await Deno.writeTextFile(
|
||||
`${tempDir}/wmill.yaml`,
|
||||
`defaultTs: bun
|
||||
includes:
|
||||
- "**"
|
||||
excludes: []
|
||||
`,
|
||||
);
|
||||
|
||||
const uniqueId = Date.now();
|
||||
await ensureDir(`${tempDir}/f/test`);
|
||||
|
||||
// Create a script with ws_error_handler_muted: true
|
||||
const scriptName = `f/test/muted_script_${uniqueId}`;
|
||||
const script = createScriptFixture(scriptName, "deno");
|
||||
await Deno.writeTextFile(`${tempDir}/${script.contentFile.path}`, script.contentFile.content);
|
||||
// Add ws_error_handler_muted to the metadata
|
||||
const metadataWithMuted = script.metadataFile.content + `ws_error_handler_muted: true\n`;
|
||||
await Deno.writeTextFile(`${tempDir}/${script.metadataFile.path}`, metadataWithMuted);
|
||||
|
||||
// Push
|
||||
const pushResult = await backend.runCLICommand(
|
||||
["sync", "push", "--yes", "--includes", `f/test/muted_script_${uniqueId}**`],
|
||||
tempDir,
|
||||
);
|
||||
assertEquals(
|
||||
pushResult.code,
|
||||
0,
|
||||
`Push should succeed.\nstdout: ${pushResult.stdout}\nstderr: ${pushResult.stderr}`,
|
||||
);
|
||||
|
||||
// Verify via API that ws_error_handler_muted was persisted
|
||||
const apiResp = await backend.apiRequest!(
|
||||
`/api/w/${backend.workspace}/scripts/get/p/${scriptName}`,
|
||||
);
|
||||
assertEquals(apiResp.status, 200, "API should return the script");
|
||||
const scriptData = await apiResp.json();
|
||||
assertEquals(
|
||||
scriptData.ws_error_handler_muted,
|
||||
true,
|
||||
"API should return ws_error_handler_muted: true for the pushed script",
|
||||
);
|
||||
|
||||
// Pull into a fresh directory and verify the field round-trips
|
||||
const pullDir = await Deno.makeTempDir({ prefix: "wmill_muted_script_pull_" });
|
||||
try {
|
||||
await Deno.writeTextFile(
|
||||
`${pullDir}/wmill.yaml`,
|
||||
`defaultTs: bun
|
||||
includes:
|
||||
- "f/test/muted_script_${uniqueId}**"
|
||||
excludes: []
|
||||
`,
|
||||
);
|
||||
|
||||
const pullResult = await backend.runCLICommand(["sync", "pull", "--yes"], pullDir);
|
||||
assertEquals(
|
||||
pullResult.code,
|
||||
0,
|
||||
`Pull should succeed.\nstdout: ${pullResult.stdout}\nstderr: ${pullResult.stderr}`,
|
||||
);
|
||||
|
||||
// Verify ws_error_handler_muted is in the pulled metadata
|
||||
const pulledMetadata = await Deno.readTextFile(`${pullDir}/${script.metadataFile.path}`);
|
||||
assertStringIncludes(
|
||||
pulledMetadata,
|
||||
"ws_error_handler_muted: true",
|
||||
"Pulled script metadata should contain ws_error_handler_muted: true",
|
||||
);
|
||||
|
||||
// Verify push from pulled dir is idempotent (no changes)
|
||||
const push2 = await backend.runCLICommand(
|
||||
["sync", "push", "--dry-run", "--includes", `f/test/muted_script_${uniqueId}**`],
|
||||
pullDir,
|
||||
);
|
||||
assertEquals(push2.code, 0, `Second push dry-run should succeed: ${push2.stderr}`);
|
||||
const output = (push2.stdout + push2.stderr).toLowerCase();
|
||||
assert(
|
||||
output.includes("0 change") || output.includes("no change") || output.includes("nothing"),
|
||||
`Should have no changes after push-pull cycle for script with ws_error_handler_muted. Output: ${output}`,
|
||||
);
|
||||
} finally {
|
||||
await Deno.remove(pullDir, { recursive: true }).catch(() => {});
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "Integration: Flow ws_error_handler_muted is persisted through push/pull",
|
||||
ignore: shouldSkipOnCI(), // Requires EE features
|
||||
sanitizeResources: false,
|
||||
sanitizeOps: false,
|
||||
async fn() {
|
||||
await withTestBackend(async (backend, tempDir) => {
|
||||
await Deno.writeTextFile(
|
||||
`${tempDir}/wmill.yaml`,
|
||||
`defaultTs: bun
|
||||
includes:
|
||||
- "**"
|
||||
excludes: []
|
||||
`,
|
||||
);
|
||||
|
||||
const uniqueId = Date.now();
|
||||
const flowName = `f/test/muted_flow_${uniqueId}`;
|
||||
const flowFixture = createFlowFixture(flowName);
|
||||
|
||||
// Create flow directory and files
|
||||
await ensureDir(`${tempDir}/f/test/muted_flow_${uniqueId}${getFolderSuffix("flow")}`);
|
||||
for (const [key, file] of Object.entries(flowFixture)) {
|
||||
if (key === "metadata") {
|
||||
// Add ws_error_handler_muted to flow metadata
|
||||
const contentWithMuted = file.content + `ws_error_handler_muted: true\n`;
|
||||
await Deno.writeTextFile(`${tempDir}/${file.path}`, contentWithMuted);
|
||||
} else {
|
||||
await Deno.writeTextFile(`${tempDir}/${file.path}`, file.content);
|
||||
}
|
||||
}
|
||||
|
||||
// Push
|
||||
const pushResult = await backend.runCLICommand(
|
||||
["sync", "push", "--yes", "--includes", `f/test/muted_flow_${uniqueId}*/**`],
|
||||
tempDir,
|
||||
);
|
||||
assertEquals(
|
||||
pushResult.code,
|
||||
0,
|
||||
`Push should succeed.\nstdout: ${pushResult.stdout}\nstderr: ${pushResult.stderr}`,
|
||||
);
|
||||
|
||||
// Verify via API that ws_error_handler_muted was persisted
|
||||
const apiResp = await backend.apiRequest!(
|
||||
`/api/w/${backend.workspace}/flows/get/${flowName}`,
|
||||
);
|
||||
assertEquals(apiResp.status, 200, "API should return the flow");
|
||||
const flowData = await apiResp.json();
|
||||
assertEquals(
|
||||
flowData.ws_error_handler_muted,
|
||||
true,
|
||||
"API should return ws_error_handler_muted: true for the pushed flow",
|
||||
);
|
||||
|
||||
// Pull into a fresh directory and verify the field round-trips
|
||||
const pullDir = await Deno.makeTempDir({ prefix: "wmill_muted_flow_pull_" });
|
||||
try {
|
||||
await Deno.writeTextFile(
|
||||
`${pullDir}/wmill.yaml`,
|
||||
`defaultTs: bun
|
||||
includes:
|
||||
- "f/test/muted_flow_${uniqueId}*/**"
|
||||
excludes: []
|
||||
`,
|
||||
);
|
||||
|
||||
const pullResult = await backend.runCLICommand(["sync", "pull", "--yes"], pullDir);
|
||||
assertEquals(
|
||||
pullResult.code,
|
||||
0,
|
||||
`Pull should succeed.\nstdout: ${pullResult.stdout}\nstderr: ${pullResult.stderr}`,
|
||||
);
|
||||
|
||||
// Verify ws_error_handler_muted is in the pulled flow.yaml
|
||||
const flowYamlPath = `${pullDir}/${flowFixture.metadata.path}`;
|
||||
const pulledFlowYaml = await Deno.readTextFile(flowYamlPath);
|
||||
assertStringIncludes(
|
||||
pulledFlowYaml,
|
||||
"ws_error_handler_muted: true",
|
||||
"Pulled flow.yaml should contain ws_error_handler_muted: true",
|
||||
);
|
||||
|
||||
// Parse the YAML to confirm it's a proper boolean value
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const parsed = await yamlParseFile(flowYamlPath) as any;
|
||||
assertEquals(
|
||||
parsed.ws_error_handler_muted,
|
||||
true,
|
||||
"ws_error_handler_muted should be boolean true in parsed flow YAML",
|
||||
);
|
||||
|
||||
// Verify push from pulled dir is idempotent (no changes)
|
||||
const push2 = await backend.runCLICommand(
|
||||
["sync", "push", "--dry-run", "--includes", `f/test/muted_flow_${uniqueId}*/**`],
|
||||
pullDir,
|
||||
);
|
||||
assertEquals(push2.code, 0, `Second push dry-run should succeed: ${push2.stderr}`);
|
||||
const output = (push2.stdout + push2.stderr).toLowerCase();
|
||||
assert(
|
||||
output.includes("0 change") || output.includes("no change") || output.includes("nothing"),
|
||||
`Should have no changes after push-pull cycle for flow with ws_error_handler_muted. Output: ${output}`,
|
||||
);
|
||||
} finally {
|
||||
await Deno.remove(pullDir, { recursive: true }).catch(() => {});
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user