mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
feat: Mute workspace error handler for flows and scripts (#2458)
* feat: Mute workspace error handler for flows and scripts * implement BE muting * Add tooltip to bell icon * change field name to ws_error_handler_muted * revert new field for queue table * Error when error handler not set * extract svelte component and use flow table column * Fix flow error handler result arg
This commit is contained in:
committed by
GitHub
parent
61ac5e45f5
commit
09e29d6b3c
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE script SET ws_error_handler_muted = $3 WHERE workspace_id = $2 AND path = $1 AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1182fe055306d7ea435d76b74d781e066915c8397e6bbc9e408ff3dda9fec27f"
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select hash, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2 AND\n deleted = false AND archived = false)",
|
||||
"query": "select hash, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where path = $1 AND workspace_id = $2 AND\n created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2 AND\n deleted = false AND archived = false)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -75,5 +75,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5cd89ab614d3cac80fb81627267ee85b191263989b0c78b2bfce77e796e96825"
|
||||
"hash": "251f18ccdd89255023258a33285757801cabe4e86385e2dea10b666d1c6f2b61"
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select path, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where hash = $1 AND workspace_id = $2",
|
||||
"query": "select path, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where hash = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -75,5 +75,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "65835f2e5ad38f7cc6b147dadfef6f580f15bca96d9746c9359e98ca793f8f1f"
|
||||
"hash": "27ab808d7e05235180449df043ef57c6fbe7d3e261e5a52313d01782739c0e77"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE flow SET ws_error_handler_muted = $3 WHERE path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "899b48109ce20a8fbcf9c8e9339713dcdf4173564d388f1927dea06653c718d5"
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "select tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where hash = $1 AND workspace_id = $2",
|
||||
"query": "select tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where hash = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -69,5 +69,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "123c0608e229c29187009b7961355ddd99c4ad1f46b876dd86e372b84d806ecd"
|
||||
"hash": "901c430cba96e5cab1a186207ea708f3d34a04c8ab03b385f8f2e9c662d3e724"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT ws_error_handler_muted FROM script WHERE workspace_id = $1 AND hash = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "ws_error_handler_muted",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "97b6bbcd655001a99f0cd9b127dcddf6d45ad87308c1294e52f1ae65529271d1"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT ws_error_handler_muted FROM flow WHERE workspace_id = $1 AND path = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "ws_error_handler_muted",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "9f545c9c03d550ba0468dd7b4be39b76a7754a53f4508dce05e3f36300db483b"
|
||||
}
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)",
|
||||
"query": "INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -58,10 +58,11 @@
|
||||
"Int4",
|
||||
"Int4",
|
||||
"Int4",
|
||||
"Bool",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "438b5b5d29b05846c2e074cad2404e797527841cd97dba80c271cbefafae65cc"
|
||||
"hash": "b5646d44502b289e5d92f53f74bf0a52b8bf76775d87a1f698ed2a3c3446c86e"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT error_handler FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "error_handler",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "f8b34e09453d51d3df5be20652938a890ee353fec64ee73c312a3352da7f7515"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE script DROP COLUMN ws_error_handler_muted;
|
||||
ALTER TABLE flow DROP COLUMN ws_error_handler_muted;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE script ADD COLUMN ws_error_handler_muted BOOLEAN NOT NULL DEFAULT false;
|
||||
ALTER TABLE flow ADD COLUMN ws_error_handler_muted BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -2564,6 +2564,7 @@ async fn test_flow_lock_all(db: Pool<Postgres>) {
|
||||
open_flow: flow,
|
||||
path: "g/all/flow_lock_all".to_owned(),
|
||||
tag: None,
|
||||
ws_error_handler_muted: None,
|
||||
},
|
||||
draft_only: None,
|
||||
},
|
||||
@@ -2917,6 +2918,7 @@ async fn run_deployed_relative_imports(db: &Pool<Postgres>, script_content: Stri
|
||||
summary: "".to_string(),
|
||||
tag: None,
|
||||
schema: std::collections::HashMap::new(),
|
||||
ws_error_handler_muted: Some(false),
|
||||
},
|
||||
).await.unwrap();
|
||||
|
||||
|
||||
@@ -3931,6 +3931,8 @@ paths:
|
||||
type: number
|
||||
dedicated_worker:
|
||||
type: boolean
|
||||
ws_error_handler_muted:
|
||||
type: boolean
|
||||
required: &ref_29
|
||||
- hash
|
||||
- path
|
||||
@@ -4115,6 +4117,8 @@ paths:
|
||||
type: number
|
||||
dedicated_worker:
|
||||
type: boolean
|
||||
ws_error_handler_muted:
|
||||
type: boolean
|
||||
required: &ref_32
|
||||
- path
|
||||
- summary
|
||||
@@ -4128,6 +4132,38 @@ paths:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
/w/{workspace}/scripts/toggle_workspace_error_handler/p/{path}:
|
||||
post:
|
||||
summary: Toggle ON and OFF the workspace error handler for a given script
|
||||
operationId: toggleWorkspaceErrorHandlerForScript
|
||||
tags:
|
||||
- script
|
||||
parameters:
|
||||
- name: workspace
|
||||
in: path
|
||||
required: true
|
||||
schema: *ref_0
|
||||
- name: path
|
||||
in: path
|
||||
required: true
|
||||
schema: *ref_27
|
||||
requestBody:
|
||||
description: Workspace error handler enabled
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
muted:
|
||||
type: boolean
|
||||
responses:
|
||||
'200':
|
||||
description: error handler toggled
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
/workers/custom_tags:
|
||||
get:
|
||||
summary: >-
|
||||
@@ -4955,6 +4991,8 @@ paths:
|
||||
type: boolean
|
||||
tag:
|
||||
type: string
|
||||
ws_error_handler_muted:
|
||||
type: boolean
|
||||
required: &ref_141
|
||||
- path
|
||||
- edited_by
|
||||
@@ -4989,6 +5027,38 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
allOf: *ref_41
|
||||
/w/{workspace}/flows/toggle_workspace_error_handler/{path}:
|
||||
post:
|
||||
summary: Toggle ON and OFF the workspace error handler for a given flow
|
||||
operationId: toggleWorkspaceErrorHandlerForFlow
|
||||
tags:
|
||||
- flow
|
||||
parameters:
|
||||
- name: workspace
|
||||
in: path
|
||||
required: true
|
||||
schema: *ref_0
|
||||
- name: path
|
||||
in: path
|
||||
required: true
|
||||
schema: *ref_27
|
||||
requestBody:
|
||||
description: Workspace error handler enabled
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
muted:
|
||||
type: boolean
|
||||
responses:
|
||||
'200':
|
||||
description: error handler toggled
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
/w/{workspace}/flows/get/draft/{path}:
|
||||
get:
|
||||
summary: get flow by path with draft
|
||||
@@ -5066,6 +5136,8 @@ paths:
|
||||
type: string
|
||||
tag:
|
||||
type: string
|
||||
ws_error_handler_muted:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
- type: object
|
||||
|
||||
@@ -2898,6 +2898,33 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/scripts/toggle_workspace_error_handler/p/{path}:
|
||||
post:
|
||||
summary: Toggle ON and OFF the workspace error handler for a given script
|
||||
operationId: toggleWorkspaceErrorHandlerForScript
|
||||
tags:
|
||||
- script
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
requestBody:
|
||||
description: Workspace error handler enabled
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
muted:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: error handler toggled
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/workers/custom_tags:
|
||||
get:
|
||||
summary: get all instance custom tags (tags are used to dispatch jobs to
|
||||
@@ -3446,6 +3473,33 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Flow"
|
||||
|
||||
/w/{workspace}/flows/toggle_workspace_error_handler/{path}:
|
||||
post:
|
||||
summary: Toggle ON and OFF the workspace error handler for a given flow
|
||||
operationId: toggleWorkspaceErrorHandlerForFlow
|
||||
tags:
|
||||
- flow
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
requestBody:
|
||||
description: Workspace error handler enabled
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
muted:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: error handler toggled
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/flows/get/draft/{path}:
|
||||
get:
|
||||
summary: get flow by path with draft
|
||||
@@ -6390,6 +6444,8 @@ components:
|
||||
type: number
|
||||
dedicated_worker:
|
||||
type: boolean
|
||||
ws_error_handler_muted:
|
||||
type: boolean
|
||||
required:
|
||||
- hash
|
||||
- path
|
||||
@@ -6467,6 +6523,8 @@ components:
|
||||
type: number
|
||||
dedicated_worker:
|
||||
type: boolean
|
||||
ws_error_handler_muted:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
- summary
|
||||
@@ -7712,6 +7770,8 @@ components:
|
||||
type: boolean
|
||||
tag:
|
||||
type: string
|
||||
ws_error_handler_muted:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
- edited_by
|
||||
@@ -7728,6 +7788,8 @@ components:
|
||||
type: string
|
||||
tag:
|
||||
type: string
|
||||
ws_error_handler_muted:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
|
||||
|
||||
@@ -53,6 +53,10 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/get/draft/*path", get(get_flow_by_path_w_draft))
|
||||
.route("/exists/*path", get(exists_flow_by_path))
|
||||
.route("/list_paths", get(list_paths))
|
||||
.route(
|
||||
"/toggle_workspace_error_handler/*path",
|
||||
post(toggle_workspace_error_handler),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn global_service() -> Router {
|
||||
@@ -114,7 +118,8 @@ async fn list_flows(
|
||||
"extra_perms",
|
||||
"favorite.path IS NOT NULL as starred",
|
||||
"draft.path IS NOT NULL as has_draft",
|
||||
"draft_only"
|
||||
"draft_only",
|
||||
"ws_error_handler_muted"
|
||||
])
|
||||
.left()
|
||||
.join("favorite")
|
||||
@@ -209,6 +214,56 @@ pub async fn get_hub_flow_by_id(
|
||||
Ok(Json(value))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ToggleWorkspaceErrorHandler {
|
||||
pub muted: Option<bool>,
|
||||
}
|
||||
async fn toggle_workspace_error_handler(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Json(req): Json<ToggleWorkspaceErrorHandler>,
|
||||
) -> Result<String> {
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
if true {
|
||||
return Err(Error::BadRequest(
|
||||
"Muting the error handler for certain flow is only available in enterprise version"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let error_handler_maybe: Option<String> = sqlx::query_scalar!(
|
||||
"SELECT error_handler FROM workspace_settings WHERE workspace_id = $1",
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?
|
||||
.unwrap_or(None);
|
||||
|
||||
return match error_handler_maybe {
|
||||
Some(_) => {
|
||||
sqlx::query_scalar!(
|
||||
"UPDATE flow SET ws_error_handler_muted = $3 WHERE path = $1 AND workspace_id = $2",
|
||||
path.to_path(),
|
||||
w_id,
|
||||
req.muted,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok("".to_string())
|
||||
}
|
||||
None => {
|
||||
tx.commit().await?;
|
||||
Err(Error::ExecutionErr(
|
||||
"Workspace error handler needs to be defined".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async fn check_path_conflict<'c>(
|
||||
tx: &mut Transaction<'c, Postgres>,
|
||||
w_id: &str,
|
||||
@@ -237,6 +292,19 @@ async fn create_flow(
|
||||
Path(w_id): Path<String>,
|
||||
Json(nf): Json<NewFlow>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
if nf
|
||||
.value
|
||||
.get("ws_error_handler_muted")
|
||||
.map(|val| val.as_bool().unwrap_or(false))
|
||||
.is_some_and(|val| val)
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
"Muting the error handler for certain flow is only available in enterprise version"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// cron::Schedule::from_str(&ns.schedule).map_err(|e| error::Error::BadRequest(e.to_string()))?;
|
||||
let authed = maybe_refresh_folders(&nf.path, &w_id, authed, &db).await;
|
||||
|
||||
@@ -372,6 +440,19 @@ async fn update_flow(
|
||||
Path((w_id, flow_path)): Path<(String, StripPath)>,
|
||||
Json(nf): Json<NewFlow>,
|
||||
) -> Result<String> {
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
if nf
|
||||
.value
|
||||
.get("ws_error_handler_muted")
|
||||
.map(|val| val.as_bool().unwrap_or(false))
|
||||
.is_some_and(|val| val)
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
"Muting the error handler for certain flow is only available in enterprise version"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let flow_path = flow_path.to_path();
|
||||
let authed = maybe_refresh_folders(&flow_path, &w_id, authed, &db).await;
|
||||
|
||||
@@ -554,6 +635,7 @@ pub struct FlowWDraft {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub draft_only: Option<bool>,
|
||||
pub tag: Option<String>,
|
||||
pub ws_error_handler_muted: Option<bool>,
|
||||
}
|
||||
|
||||
async fn get_flow_by_path_w_draft(
|
||||
@@ -565,7 +647,7 @@ async fn get_flow_by_path_w_draft(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let flow_o = sqlx::query_as::<_, FlowWDraft>(
|
||||
"SELECT flow.path, flow.summary, flow,description, flow.schema, flow.value, flow.extra_perms, flow.draft_only, draft.value as draft, flow.tag FROM flow
|
||||
"SELECT flow.path, flow.summary, flow,description, flow.schema, flow.value, flow.extra_perms, flow.draft_only, flow.ws_error_handler_muted, draft.value as draft, flow.tag FROM flow
|
||||
LEFT JOIN draft ON
|
||||
flow.path = draft.path AND draft.workspace_id = $2 AND draft.typ = 'flow'
|
||||
WHERE flow.path = $1 AND flow.workspace_id = $2",
|
||||
@@ -794,6 +876,7 @@ mod tests {
|
||||
concurrency_time_window_s: None,
|
||||
skip_expr: None,
|
||||
cache_ttl: None,
|
||||
ws_error_handler_muted: None,
|
||||
};
|
||||
let expect = serde_json::json!({
|
||||
"modules": [
|
||||
|
||||
@@ -316,7 +316,7 @@ pub async fn get_path_tag_limits_cache_for_hash(
|
||||
Option<bool>,
|
||||
)> {
|
||||
let script = sqlx::query!(
|
||||
"select path, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where hash = $1 AND workspace_id = $2",
|
||||
"select path, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where hash = $1 AND workspace_id = $2",
|
||||
hash,
|
||||
w_id
|
||||
)
|
||||
|
||||
@@ -77,6 +77,7 @@ pub struct ScriptWDraft {
|
||||
pub cache_ttl: Option<i32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub dedicated_worker: Option<bool>,
|
||||
pub ws_error_handler_muted: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn global_service() -> Router {
|
||||
@@ -113,6 +114,10 @@ pub fn workspaced_service() -> Router {
|
||||
.route("/raw/h/:hash", get(raw_script_by_hash))
|
||||
.route("/deployment_status/h/:hash", get(get_deployment_status))
|
||||
.route("/list_paths", get(list_paths))
|
||||
.route(
|
||||
"/toggle_workspace_error_handler/p/*path",
|
||||
post(toggle_workspace_error_handler),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Serialize, FromRow)]
|
||||
@@ -168,7 +173,8 @@ async fn list_scripts(
|
||||
"favorite.path IS NOT NULL as starred",
|
||||
"tag",
|
||||
"draft.path IS NOT NULL as has_draft",
|
||||
"draft_only"
|
||||
"draft_only",
|
||||
"ws_error_handler_muted"
|
||||
])
|
||||
.left()
|
||||
.join("favorite")
|
||||
@@ -304,6 +310,14 @@ async fn create_script(
|
||||
Path(w_id): Path<String>,
|
||||
Json(ns): Json<NewScript>,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
if ns.ws_error_handler_muted.is_some_and(|val| val) {
|
||||
return Err(Error::BadRequest(
|
||||
"Muting the error handler for certain script is only available in enterprise version"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let hash = ScriptHash(hash_script(&ns));
|
||||
let authed = maybe_refresh_folders(&ns.path, &w_id, authed, &db).await;
|
||||
let mut tx: QueueTransaction<'_, _> = (rsmq, user_db.begin(&authed).await?).into();
|
||||
@@ -456,8 +470,8 @@ async fn create_script(
|
||||
sqlx::query!(
|
||||
"INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, \
|
||||
content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, \
|
||||
draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker) \
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)",
|
||||
draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted) \
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::text::json, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22)",
|
||||
&w_id,
|
||||
&hash.0,
|
||||
ns.path,
|
||||
@@ -478,7 +492,8 @@ async fn create_script(
|
||||
ns.concurrent_limit,
|
||||
ns.concurrency_time_window_s,
|
||||
ns.cache_ttl,
|
||||
ns.dedicated_worker
|
||||
ns.dedicated_worker,
|
||||
ns.ws_error_handler_muted.unwrap_or(false),
|
||||
)
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
@@ -675,7 +690,7 @@ async fn get_script_by_path_w_draft(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let script_o = sqlx::query_as::<_, ScriptWDraft>(
|
||||
"SELECT hash, script.path, summary, description, content, language, kind, tag, schema, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, draft.value as draft, dedicated_worker FROM script LEFT JOIN draft ON
|
||||
"SELECT hash, script.path, summary, description, content, language, kind, tag, schema, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, ws_error_handler_muted, draft.value as draft, dedicated_worker FROM script LEFT JOIN draft ON
|
||||
script.path = draft.path AND script.workspace_id = draft.workspace_id AND draft.typ = 'script'
|
||||
WHERE script.path = $1 AND script.workspace_id = $2 \
|
||||
AND script.created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \
|
||||
@@ -709,6 +724,56 @@ async fn list_paths(
|
||||
Ok(Json(scripts))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ToggleWorkspaceErrorHandler {
|
||||
pub muted: Option<bool>,
|
||||
}
|
||||
async fn toggle_workspace_error_handler(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Json(req): Json<ToggleWorkspaceErrorHandler>,
|
||||
) -> Result<String> {
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
if true {
|
||||
return Err(Error::BadRequest(
|
||||
"Muting the error handler for certain script is only available in enterprise version"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let error_handler_maybe: Option<String> = sqlx::query_scalar!(
|
||||
"SELECT error_handler FROM workspace_settings WHERE workspace_id = $1",
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?
|
||||
.unwrap_or(None);
|
||||
|
||||
match error_handler_maybe {
|
||||
Some(_) => {
|
||||
sqlx::query_scalar!(
|
||||
"UPDATE script SET ws_error_handler_muted = $3 WHERE workspace_id = $2 AND path = $1 AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)",
|
||||
path.to_path(),
|
||||
w_id,
|
||||
req.muted,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok("".to_string())
|
||||
}
|
||||
None => {
|
||||
tx.commit().await?;
|
||||
Err(Error::ExecutionErr(
|
||||
"Workspace error handler needs to be defined".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_tokened_raw_script_by_path(
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, token, path)): Path<(String, String, StripPath)>,
|
||||
|
||||
@@ -37,6 +37,7 @@ pub struct Flow {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub draft_only: Option<bool>,
|
||||
pub tag: Option<String>,
|
||||
pub ws_error_handler_muted: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -54,6 +55,7 @@ pub struct ListableFlow {
|
||||
pub has_draft: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub draft_only: Option<bool>,
|
||||
pub ws_error_handler_muted: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -86,6 +88,8 @@ pub struct FlowValue {
|
||||
pub skip_expr: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub cache_ttl: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ws_error_handler_muted: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
|
||||
@@ -281,7 +281,7 @@ pub async fn script_hash_to_tag_and_limits<'c>(
|
||||
Option<bool>,
|
||||
)> {
|
||||
let script = sqlx::query!(
|
||||
"select tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where hash = $1 AND workspace_id = $2",
|
||||
"select tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where hash = $1 AND workspace_id = $2",
|
||||
script_hash.0,
|
||||
w_id
|
||||
)
|
||||
|
||||
@@ -222,7 +222,7 @@ pub async fn get_latest_hash_for_path<'c>(
|
||||
Option<bool>,
|
||||
)> {
|
||||
let r_o = sqlx::query!(
|
||||
"select hash, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where path = $1 AND workspace_id = $2 AND
|
||||
"select hash, tag, concurrent_limit, concurrency_time_window_s, cache_ttl, language as \"language: ScriptLang\", dedicated_worker from script where path = $1 AND workspace_id = $2 AND
|
||||
created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2 AND
|
||||
deleted = false AND archived = false)",
|
||||
script_path,
|
||||
|
||||
@@ -162,6 +162,7 @@ pub struct Script {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub concurrency_time_window_s: Option<i32>,
|
||||
pub dedicated_worker: Option<bool>,
|
||||
pub ws_error_handler_muted: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -181,6 +182,7 @@ pub struct ListableScript {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub draft_only: Option<bool>,
|
||||
pub has_deploy_errors: bool,
|
||||
pub ws_error_handler_muted: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@@ -217,6 +219,7 @@ pub struct NewScript {
|
||||
pub concurrency_time_window_s: Option<i32>,
|
||||
pub cache_ttl: Option<i32>,
|
||||
pub dedicated_worker: Option<bool>,
|
||||
pub ws_error_handler_muted: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
||||
@@ -591,16 +591,40 @@ pub async fn send_error_to_workspace_handler<
|
||||
.ok_or_else(|| Error::InternalErr(format!("no workspace settings for id {w_id}")))?;
|
||||
|
||||
if let Some(error_handler) = error_handler {
|
||||
run_error_handler(
|
||||
rsmq,
|
||||
queued_job,
|
||||
db,
|
||||
result,
|
||||
&error_handler.strip_prefix("script/").unwrap(),
|
||||
error_handler_extra_args,
|
||||
false,
|
||||
)
|
||||
.await?
|
||||
let ws_error_handler_muted: Option<bool> = match queued_job.job_kind {
|
||||
JobKind::Script => {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT ws_error_handler_muted FROM script WHERE workspace_id = $1 AND hash = $2",
|
||||
queued_job.workspace_id,
|
||||
queued_job.script_hash.unwrap().0,
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
}
|
||||
JobKind::Flow => {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT ws_error_handler_muted FROM flow WHERE workspace_id = $1 AND path = $2",
|
||||
queued_job.workspace_id,
|
||||
queued_job.script_path.as_ref().unwrap(),
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if !ws_error_handler_muted.unwrap_or(false) {
|
||||
run_error_handler(
|
||||
rsmq,
|
||||
queued_job,
|
||||
db,
|
||||
result,
|
||||
&error_handler.strip_prefix("script/").unwrap(),
|
||||
error_handler_extra_args,
|
||||
false,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -2066,7 +2090,9 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection
|
||||
"INSERT INTO queue
|
||||
(workspace_id, id, running, parent_job, created_by, permissioned_as, scheduled_for,
|
||||
script_hash, script_path, raw_code, raw_lock, args, job_kind, schedule_path, raw_flow, \
|
||||
flow_status, is_flow_step, language, started_at, same_worker, pre_run_error, email, visible_to_owner, root_job, tag, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id, cache_ttl)
|
||||
flow_status, is_flow_step, language, started_at, same_worker, pre_run_error, email, \
|
||||
visible_to_owner, root_job, tag, concurrent_limit, concurrency_time_window_s, timeout, \
|
||||
flow_step_id, cache_ttl)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, now()), $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, CASE WHEN $3 THEN now() END, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29) \
|
||||
RETURNING id",
|
||||
workspace_id,
|
||||
|
||||
@@ -650,17 +650,36 @@ pub async fn update_flow_status_after_job_completion_internal<
|
||||
|
||||
save_in_cache(db, &flow_job, cached_res_path, &nresult).await;
|
||||
}
|
||||
add_completed_job(
|
||||
db,
|
||||
&flow_job,
|
||||
success && !is_failure_step && !skip_error_handler,
|
||||
stop_early && skip_if_stop_early,
|
||||
Json(&nresult),
|
||||
logs,
|
||||
0,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await?;
|
||||
let success = success && !is_failure_step && !skip_error_handler;
|
||||
if success {
|
||||
add_completed_job(
|
||||
db,
|
||||
&flow_job,
|
||||
success,
|
||||
stop_early && skip_if_stop_early,
|
||||
Json(&nresult),
|
||||
logs,
|
||||
0,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
add_completed_job(
|
||||
db,
|
||||
&flow_job,
|
||||
success,
|
||||
stop_early && skip_if_stop_early,
|
||||
Json(
|
||||
&serde_json::from_str::<Value>(nresult.get()).unwrap_or_else(
|
||||
|e| json!({"error": format!("Impossible to serialize error: {e}")}),
|
||||
),
|
||||
),
|
||||
logs,
|
||||
0,
|
||||
rsmq.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
@@ -2079,6 +2098,7 @@ async fn compute_next_flow_transform(
|
||||
concurrency_time_window_s: None,
|
||||
skip_expr: None,
|
||||
cache_ttl: None,
|
||||
ws_error_handler_muted: None,
|
||||
},
|
||||
path: inner_path,
|
||||
},
|
||||
@@ -2136,6 +2156,7 @@ async fn compute_next_flow_transform(
|
||||
concurrency_time_window_s: None,
|
||||
skip_expr: None,
|
||||
cache_ttl: None,
|
||||
ws_error_handler_muted: None,
|
||||
},
|
||||
path: Some(format!("{}/forloop", flow_job.script_path())),
|
||||
},
|
||||
@@ -2227,6 +2248,7 @@ async fn compute_next_flow_transform(
|
||||
concurrency_time_window_s: None,
|
||||
skip_expr: None,
|
||||
cache_ttl: None,
|
||||
ws_error_handler_muted: None,
|
||||
},
|
||||
path: Some(format!(
|
||||
"{}/branchone-{}",
|
||||
@@ -2271,6 +2293,7 @@ async fn compute_next_flow_transform(
|
||||
concurrency_time_window_s: None,
|
||||
skip_expr: None,
|
||||
cache_ttl: None,
|
||||
ws_error_handler_muted: None,
|
||||
},
|
||||
path: Some(format!(
|
||||
"{}/branchall-{}",
|
||||
@@ -2332,6 +2355,7 @@ async fn compute_next_flow_transform(
|
||||
concurrency_time_window_s: None,
|
||||
skip_expr: None,
|
||||
cache_ttl: None,
|
||||
ws_error_handler_muted: None,
|
||||
},
|
||||
path: Some(format!(
|
||||
"{}/branchall-{}",
|
||||
|
||||
@@ -113,7 +113,8 @@
|
||||
value: flow.value,
|
||||
schema: flow.schema,
|
||||
tag: flow.tag,
|
||||
draft_only: true
|
||||
draft_only: true,
|
||||
ws_error_handler_muted: flow.ws_error_handler_muted
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -159,7 +160,8 @@
|
||||
summary: flow.summary,
|
||||
description: flow.description ?? '',
|
||||
value: flow.value,
|
||||
schema: flow.schema
|
||||
schema: flow.schema,
|
||||
ws_error_handler_muted: flow.ws_error_handler_muted
|
||||
}
|
||||
})
|
||||
if (enabled) {
|
||||
@@ -176,7 +178,8 @@
|
||||
description: flow.description ?? '',
|
||||
value: flow.value,
|
||||
schema: flow.schema,
|
||||
tag: flow.tag
|
||||
tag: flow.tag,
|
||||
ws_error_handler_muted: flow.ws_error_handler_muted
|
||||
}
|
||||
})
|
||||
const scheduleExists = await ScheduleService.existsSchedule({
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import DrawerContent from './common/drawer/DrawerContent.svelte'
|
||||
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
import ErrorHandlerToggleButton from '$lib/components/details/ErrorHandlerToggleButton.svelte'
|
||||
import {
|
||||
Bug,
|
||||
CheckCircle,
|
||||
@@ -190,7 +191,8 @@
|
||||
dedicated_worker: script.dedicated_worker,
|
||||
concurrent_limit: script.concurrent_limit,
|
||||
concurrency_time_window_s: script.concurrency_time_window_s,
|
||||
cache_ttl: script.cache_ttl
|
||||
cache_ttl: script.cache_ttl,
|
||||
ws_error_handler_muted: script.ws_error_handler_muted
|
||||
}
|
||||
})
|
||||
history.replaceState(history.state, '', `/scripts/edit/${script.path}`)
|
||||
@@ -233,7 +235,8 @@
|
||||
envs: script.envs,
|
||||
concurrent_limit: script.concurrent_limit,
|
||||
concurrency_time_window_s: script.concurrency_time_window_s,
|
||||
cache_ttl: script.cache_ttl
|
||||
cache_ttl: script.cache_ttl,
|
||||
ws_error_handler_muted: script.ws_error_handler_muted
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -318,6 +321,16 @@
|
||||
<TabContent value="metadata">
|
||||
<div class="flex flex-col gap-8">
|
||||
<Section label="Metadata">
|
||||
<svelte:fragment slot="action">
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
<ErrorHandlerToggleButton
|
||||
kind="script"
|
||||
scriptOrFlowPath={script.path}
|
||||
bind:errorHandlerMuted={script.ws_error_handler_muted}
|
||||
iconOnly={false}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<div class="flex flex-col gap-4">
|
||||
<Label label="Summary">
|
||||
<input
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
export let moveDrawer: MoveDrawer
|
||||
export let deleteConfirmedCallback: (() => void) | undefined
|
||||
export let deploymentDrawer: DeployWorkspaceDrawer
|
||||
export let errorHandlerMuted: boolean
|
||||
|
||||
let { summary, path, extra_perms, canWrite, workspace_id, archived, draft_only, has_draft } = flow
|
||||
|
||||
@@ -74,6 +75,7 @@
|
||||
{path}
|
||||
{summary}
|
||||
{starred}
|
||||
{errorHandlerMuted}
|
||||
on:change
|
||||
canFavorite={!draft_only}
|
||||
>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
import Star from '$lib/components/Star.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import RowIcon from './RowIcon.svelte'
|
||||
import { BellOff } from 'lucide-svelte'
|
||||
|
||||
export let marked: string | undefined
|
||||
export let starred: boolean
|
||||
export let canFavorite: boolean = true
|
||||
export let errorHandlerMuted: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -37,6 +39,11 @@ first-of-type:rounded-t-md last-of-type:rounded-b-md [*:not(:last-child)]:border
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
{#if errorHandlerMuted}
|
||||
<BellOff class="w-8 opacity-60" size={12} fill="currentcolor" />
|
||||
{/if}
|
||||
|
||||
{#if $$slots.badges}
|
||||
<div class="hidden lg:flex flex-row gap-4 items-center">
|
||||
<slot name="badges" />
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
export let moveDrawer: MoveDrawer
|
||||
export let deploymentDrawer: DeployWorkspaceDrawer
|
||||
export let deleteConfirmedCallback: (() => void) | undefined
|
||||
export let errorHandlerMuted: boolean
|
||||
|
||||
let {
|
||||
summary,
|
||||
@@ -91,6 +92,7 @@
|
||||
{path}
|
||||
{summary}
|
||||
{starred}
|
||||
{errorHandlerMuted}
|
||||
workspaceId={$workspaceStore ?? ''}
|
||||
on:change
|
||||
canFavorite={!draft_only}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import Menu from '$lib/components/details/Menu.svelte'
|
||||
import MenuItem from '$lib/components/common/menu/MenuItem.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import ErrorHandlerToggleButton from './ErrorHandlerToggleButton.svelte'
|
||||
|
||||
type MainButton = {
|
||||
label: string
|
||||
@@ -22,6 +23,10 @@
|
||||
export let mainButtons: MainButton[] = []
|
||||
export let menuItems: MenuItemButton[] = []
|
||||
export let title: string
|
||||
|
||||
export let errorHandlerKind: 'flow' | 'script'
|
||||
export let scriptOrFlowPath: string
|
||||
export let errorHandlerMuted: boolean | undefined
|
||||
</script>
|
||||
|
||||
<div class="border-b p-2 shadow-md">
|
||||
@@ -58,6 +63,11 @@
|
||||
</svelte:fragment>
|
||||
</Menu>
|
||||
{/if}
|
||||
<ErrorHandlerToggleButton
|
||||
kind={errorHandlerKind}
|
||||
{scriptOrFlowPath}
|
||||
bind:errorHandlerMuted
|
||||
/>
|
||||
{#each mainButtons as btn}
|
||||
<Button
|
||||
{...btn.buttonProps}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<script lang="ts">
|
||||
import { Bell, BellOff } from 'lucide-svelte'
|
||||
|
||||
import { Button } from '$lib/components/common'
|
||||
import { FlowService, ScriptService } from '$lib/gen'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
|
||||
export let kind: 'script' | 'flow'
|
||||
export let scriptOrFlowPath: string
|
||||
export let errorHandlerMuted: boolean | undefined
|
||||
export let iconOnly: boolean = true
|
||||
|
||||
async function toggleErrorHandler(): Promise<void> {
|
||||
if ($workspaceStore !== undefined) {
|
||||
try {
|
||||
if (kind === 'flow') {
|
||||
await FlowService.toggleWorkspaceErrorHandlerForFlow({
|
||||
workspace: $workspaceStore,
|
||||
path: scriptOrFlowPath,
|
||||
requestBody: {
|
||||
muted: !errorHandlerMuted
|
||||
}
|
||||
})
|
||||
} else {
|
||||
await ScriptService.toggleWorkspaceErrorHandlerForScript({
|
||||
workspace: $workspaceStore,
|
||||
path: scriptOrFlowPath,
|
||||
requestBody: {
|
||||
muted: !errorHandlerMuted
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
sendUserToast(
|
||||
`Error while toggling Workspace Error Handler: ${error.body || error.message}`,
|
||||
true
|
||||
)
|
||||
return
|
||||
}
|
||||
errorHandlerMuted = !errorHandlerMuted
|
||||
sendUserToast(
|
||||
errorHandlerMuted ? 'Workspace error handler muted' : 'Workspace error handler active',
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Button
|
||||
title={errorHandlerMuted === undefined || !errorHandlerMuted
|
||||
? 'Disable workspace error handler for this script'
|
||||
: 'Enable workspace error handler for this script'}
|
||||
size="xs"
|
||||
on:click={toggleErrorHandler}
|
||||
color="light"
|
||||
>
|
||||
{#if errorHandlerMuted === undefined || !errorHandlerMuted}
|
||||
<div class="flex flex-row items-center">
|
||||
{#if !iconOnly}
|
||||
Mute
|
||||
{/if}
|
||||
<Bell class="w-4" size={12} fill="currentcolor" />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-row items-center">
|
||||
{#if !iconOnly}
|
||||
Unmute
|
||||
{/if}
|
||||
<BellOff class="w-4" size={12} fill="currentcolor" />
|
||||
</div>
|
||||
{/if}
|
||||
</Button>
|
||||
@@ -25,6 +25,7 @@
|
||||
import type { Schema } from '$lib/common'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import ErrorHandlerToggleButton from '$lib/components/details/ErrorHandlerToggleButton.svelte'
|
||||
|
||||
const { selectedId, flowStore, initialPath, previewArgs } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
@@ -111,6 +112,16 @@
|
||||
rows="3"
|
||||
/>
|
||||
</Label>
|
||||
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<ErrorHandlerToggleButton
|
||||
kind="flow"
|
||||
scriptOrFlowPath={$flowStore.path}
|
||||
bind:errorHandlerMuted={$flowStore.ws_error_handler_muted}
|
||||
iconOnly={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Slider text="How to trigger flows?">
|
||||
<div class="text-sm text-tertiary border p-4 mb-20">
|
||||
On-demand:
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
schema?: any
|
||||
kind: 'script' | 'failure' | 'trigger' | 'command' | 'approval' | undefined
|
||||
envs?: string[]
|
||||
ws_error_handler_muted?: boolean
|
||||
}
|
||||
| undefined = undefined
|
||||
|
||||
@@ -64,7 +65,8 @@
|
||||
is_template: false,
|
||||
language: script.language,
|
||||
kind: script.kind as Script.kind | undefined,
|
||||
envs: script.envs
|
||||
envs: script.envs,
|
||||
ws_error_handler_muted: script.ws_error_handler_muted
|
||||
}
|
||||
})
|
||||
callback?.()
|
||||
|
||||
@@ -399,6 +399,10 @@
|
||||
marked={item.marked}
|
||||
on:change={loadScripts}
|
||||
script={item}
|
||||
errorHandlerMuted={item.ws_error_handler_muted === undefined ||
|
||||
item.ws_error_handler_muted === null
|
||||
? false
|
||||
: item.ws_error_handler_muted}
|
||||
{shareModal}
|
||||
{moveDrawer}
|
||||
{deploymentDrawer}
|
||||
@@ -410,6 +414,10 @@
|
||||
marked={item.marked}
|
||||
on:change={loadFlows}
|
||||
flow={item}
|
||||
errorHandlerMuted={item.ws_error_handler_muted === undefined ||
|
||||
item.ws_error_handler_muted === null
|
||||
? false
|
||||
: item.ws_error_handler_muted}
|
||||
{shareModal}
|
||||
{moveDrawer}
|
||||
{deploymentDrawer}
|
||||
|
||||
@@ -252,6 +252,9 @@
|
||||
{mainButtons}
|
||||
menuItems={getMenuItems(flow)}
|
||||
title={defaultIfEmptyString(flow.summary, flow.path)}
|
||||
bind:errorHandlerMuted={flow.ws_error_handler_muted}
|
||||
scriptOrFlowPath={flow.path}
|
||||
errorHandlerKind="flow"
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="form">
|
||||
|
||||
@@ -411,6 +411,9 @@
|
||||
{mainButtons}
|
||||
menuItems={getMenuItems(script)}
|
||||
title={defaultIfEmptyString(script.summary, script.path)}
|
||||
bind:errorHandlerMuted={script.ws_error_handler_muted}
|
||||
errorHandlerKind="script"
|
||||
scriptOrFlowPath={script.path}
|
||||
>
|
||||
{#if script?.concurrent_limit != undefined && script.concurrency_time_window_s != undefined}
|
||||
<div class="hidden md:block">
|
||||
|
||||
Reference in New Issue
Block a user