mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
feat: add gcp trigger (#5501)
* update: add migration for gcp_trigger table, add cli method gcp and add gcp_trigger args for github ci * update: add gcp module and routing it in lib.rs * update: added gcp type to TRIGGER KIND type in db, add new feature condition to try_get_fn and added gcp_trigger feature in cargo file * update: add ui for gcp trigger, added missing triggers type for job kin, add gcp trigger in deploy to functionallity * adding google cloud crate * update: handle pull and push delivery * update: handle pull and push delivery * update: handle push event, changed front ui * update: ui * fix: capture for gcp * update: automatically manage pub sub subscription and refactoring * capture done * update cli types * fix: wrong func argument and type openapi * fix: missing import * update .sqlx * update: svg color and remove pulse button for capture push gcp * update: handle deployto * update script helper and link to hub gcp script/flow * update gcp representation * update: add confirmation modal * add unique index to mitigate same subscriber id, fix `zombie worker`, update test connection logic * Update frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Update cli/gen/services.gen.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * nits * update repo ref * fix feature function * update dependencies * update .sqlx * fix missing import * update: handle existing subscription and creating new one or update it * fix ee build * handle existing config properly * update .sqlx * update .sqlx * remove unused * update documentation link fix ci * use on instance of empty_string_as_none fn * update refo ref * updat script_helper * update ui * update migration * add missing arguments * fix and nits * update repo ref * remove unused * nits * add doc links * nits * Update frontend/src/lib/components/triggers/gcp/GcpTriggerPanel.svelte Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Update frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * use trigger path as default route path * update .sqlx * update repo ref * update ref * update route path * update types * fix deploy to * nits * update repo ref * update .sqlx * Update frontend/src/lib/components/triggers/gcp/GcpTriggerEditorConfigSection.svelte Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * nits * nits * nits * remove dupliacte fn and update repo ref * remove unused import * fix missing import * fix: wong name var * update script helper and template script * Update frontend/src/lib/script_helpers.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Update ee-repo-ref.txt --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: HugoCasa <hugo@casademont.ch>
This commit is contained in:
@@ -64,7 +64,7 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
secrets: |
|
||||
rh_username=${{ secrets.RH_USERNAME }}
|
||||
rh_password=${{ secrets.RH_PASSWORD }}
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
|
||||
mkdir frontend/build && cd backend
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
- name: Rename binary with corresponding architecture
|
||||
run: |
|
||||
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"
|
||||
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages
|
||||
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
|
||||
${{ steps.meta-public.outputs.tags }}
|
||||
@@ -157,7 +157,7 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
|
||||
${{ steps.meta-ee-public.outputs.tags }}
|
||||
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
|
||||
mkdir frontend/build && cd backend
|
||||
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages
|
||||
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages
|
||||
- name: Rename binary with corresponding architecture
|
||||
run: |
|
||||
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "gcp_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "subscription_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "topic_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "delivery_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "delivery_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"push",
|
||||
"pull"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "delivery_config: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "0102308ffa1c0dbfba54d29246535bb81146a4cfae0ec408435570e0813a3bef"
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n gcp_trigger \n SET \n enabled = $1, \n email = $2, \n edited_by = $3, \n edited_at = now(), \n server_id = NULL, \n error = NULL\n WHERE \n path = $4 AND \n workspace_id = $5 \n RETURNING 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "070b8ad0b59f485fa5bf68082b060f5c3561c37e9c6f2834d234a862a475a6eb"
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM\n gcp_trigger\n WHERE\n delivery_type != 'push'::DELIVERY_MODE AND\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "gcp_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "subscription_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "topic_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "delivery_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "delivery_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"push",
|
||||
"pull"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "delivery_config: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "15fbe481789a7817bf37415fb935f9ed537fd7d3b266d928af4d5d3dd8bb5c18"
|
||||
}
|
||||
+6
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT trigger_config as \"trigger_config: _\", owner, email\n FROM capture_config\n WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'",
|
||||
"query": "\n SELECT \n trigger_config AS \"trigger_config: _\", \n owner, \n email\n FROM \n capture_config\n WHERE \n workspace_id = $1\n AND path = $2\n AND is_flow = $3\n AND trigger_kind = $4\n AND last_client_ping > NOW() - INTERVAL '10 seconds'\n AND (\n $5::bool IS FALSE\n OR (\n trigger_config IS NOT NULL\n AND trigger_config ->> 'delivery_type' = 'push'\n )\n )\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -37,11 +37,13 @@
|
||||
"nats",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"mqtt"
|
||||
"mqtt",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
@@ -50,5 +52,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e23e110e1f0438d21534fc4323e0e7bc1f0dbeca2e4f44ced05bae0ca5ca1039"
|
||||
"hash": "23419adcd74c326d716527293eff518b42f4cdb33e034441015494bd26c172d2"
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n capture_config \n SET \n server_id = $1,\n last_server_ping = now(), \n error = 'Connecting...' \n WHERE \n last_client_ping > NOW() - INTERVAL '10 seconds' AND \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'gcp' AND \n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds') \n RETURNING true\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "299e16725162888c01712f371785199960264b54c1ddf928c0c654ab15176f63"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT EXISTS(\n SELECT \n 1 \n FROM \n gcp_trigger \n WHERE \n path = $1 AND \n workspace_id = $2\n )",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "33ee913ce263600a3f94f90e4a42cf0e4086030f3b7994e4892392765cbe1517"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n capture\n SET \n path = $1\n WHERE \n path = $2 \n AND workspace_id = $3 \n AND is_flow = $4\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "37011e7f4cdfc87294e44252cca4f4683a12b82b972842f88f5c01111580224d"
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO gcp_trigger (\n gcp_resource_path,\n subscription_id,\n topic_id,\n delivery_type,\n delivery_config,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4,\n $5,\n $6, \n $7, \n $8, \n $9,\n $10,\n $11,\n $12\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "delivery_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"push",
|
||||
"pull"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Jsonb",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3f5520e0ea00569bf169da9abde31617043fcc0bea3ef62c50fcd881f3d48605"
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM capture WHERE id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "41e557e1b63b13c9fcc195901c0bd0de7e03c539ee046955543d9693551246f7"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE capture_config SET last_client_ping = now() WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4",
|
||||
"query": "\n UPDATE \n capture_config\n SET \n last_client_ping = NOW()\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3 \n AND trigger_kind = $4\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -21,7 +21,8 @@
|
||||
"nats",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"mqtt"
|
||||
"mqtt",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -30,5 +31,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c5270ee815689e42b65df507b850da43239c9a5aaea41c9aed7ed33a6219a534"
|
||||
"hash": "42b4b73e9d60348e2d90fcade9dcad6d8995242dc20a4e14c1a8fae4fc6a9fd2"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n capture_config \n SET \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'gcp'\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "43c8cd9f8560412bb06d9966ccfa2524943ba277c5f9a37c06bd5ee14fd46bda"
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n gcp_trigger \n SET \n gcp_resource_path = $1,\n subscription_id = $2,\n topic_id = $3,\n delivery_type = $4,\n delivery_config = $5,\n is_flow = $6, \n edited_by = $7, \n email = $8,\n script_path = $9,\n path = $10,\n enabled = $11,\n edited_at = now(), \n error = NULL,\n server_id = NULL\n WHERE \n workspace_id = $12 AND \n path = $13\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
{
|
||||
"Custom": {
|
||||
"name": "delivery_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"push",
|
||||
"pull"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Jsonb",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "44b9bea3651edc8ee732def1241b3d956c004376102ccc1707fc016801599dbd"
|
||||
}
|
||||
+3
-1
@@ -220,7 +220,9 @@
|
||||
"schedule",
|
||||
"app",
|
||||
"ui",
|
||||
"postgres"
|
||||
"postgres",
|
||||
"sqs",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT \n gcp_resource_path, \n script_path,\n is_flow, \n workspace_id,\n path,\n edited_by,\n email,\n delivery_config AS \"delivery_config: _\"\n FROM\n gcp_trigger\n WHERE\n workspace_id = $1 AND\n path = $2 AND\n delivery_type = 'push'::DELIVERY_MODE \n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "gcp_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "delivery_config: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5303206bbed76ee3ddc56d8057d8b82359c182ee2a5da6df35a4375d5d2d1ef7"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE \n capture_config \n SET \n last_server_ping = NULL \n WHERE \n workspace_id = $1 AND \n path = $2 AND \n is_flow = $3 AND \n trigger_kind = 'gcp' AND \n server_id IS NULL\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "673564e6c4dcf30dae3d7a75c397998ea800860ede856e5fc6cd1f57d5408333"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, created_at, trigger_kind as \"trigger_kind: _\", payload as \"payload!: _\", trigger_extra as \"trigger_extra: _\" FROM capture WHERE id = $1 AND workspace_id = $2",
|
||||
"query": "\n SELECT \n id, \n created_at, \n trigger_kind AS \"trigger_kind: _\", \n payload AS \"payload!: _\", \n trigger_extra AS \"trigger_extra: _\"\n FROM \n capture\n WHERE \n id = $1 \n AND workspace_id = $2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -29,7 +29,8 @@
|
||||
"nats",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"mqtt"
|
||||
"mqtt",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -60,5 +61,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "e17ec84003e2ec414622d100f5dfdda86bee33f31835317df512a20c805b35d7"
|
||||
"hash": "6781ba76dfce321dca4634566496ea5d698ac09d8264e35dfaa4cd8edc9e8414"
|
||||
}
|
||||
+6
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT id, created_at, trigger_kind as \"trigger_kind: _\", CASE WHEN pg_column_size(payload) < 40000 THEN payload ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as \"payload!: _\", trigger_extra as \"trigger_extra: _\"\n FROM capture\n WHERE workspace_id = $1\n AND path = $2 AND is_flow = $3\n AND ($4::trigger_kind IS NULL OR trigger_kind = $4)\n ORDER BY created_at DESC\n OFFSET $5\n LIMIT $6",
|
||||
"query": "\n SELECT \n id, \n created_at, \n trigger_kind AS \"trigger_kind: _\",\n CASE \n WHEN pg_column_size(payload) < 40000 THEN payload \n ELSE '\"WINDMILL_TOO_BIG\"'::jsonb \n END AS \"payload!: _\",\n trigger_extra AS \"trigger_extra: _\"\n FROM \n capture\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3 \n AND ($4::trigger_kind IS NULL OR trigger_kind = $4)\n ORDER BY \n created_at DESC\n OFFSET $5\n LIMIT $6\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -29,7 +29,8 @@
|
||||
"nats",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"mqtt"
|
||||
"mqtt",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -64,7 +65,8 @@
|
||||
"nats",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"mqtt"
|
||||
"mqtt",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -81,5 +83,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5c1de8473e0e96c1063a9a735a064c5a91e3ed8d9260c72b783fc12542b88fbd"
|
||||
"hash": "7cba31d597215a343cb0bca5b204a15fbba193262f7895c2bba90feb4215d6f3"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n DELETE FROM \n capture\n WHERE \n id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7d5a161d916cb8d1485f8d72e6b4044f505f49edcd2ebeee4a77eae737f48795"
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE capture_config SET path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow = $4",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7e891e053b8545c800d629421f239319a854f761a300970e2fd909f8058ec566"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT trigger_config as \"trigger_config: _\", trigger_kind as \"trigger_kind: _\", error, last_server_ping\n FROM capture_config\n WHERE workspace_id = $1 AND path = $2 AND is_flow = $3",
|
||||
"query": "\n SELECT \n trigger_config AS \"trigger_config: _\", \n trigger_kind AS \"trigger_kind: _\", \n error, \n last_server_ping\n FROM \n capture_config\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -24,7 +24,8 @@
|
||||
"nats",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"mqtt"
|
||||
"mqtt",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -55,5 +56,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "c223f8b7fa4ef1aa06e1ba2a56d677774aa237508d5610714efd2e9b8b93c7b8"
|
||||
"hash": "7fbf72d9059fcd77e4c1112fa4fa22e4276c1da653475628889ce17dc904fbaa"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT owner, email\n FROM capture_config\n WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'",
|
||||
"query": "\n SELECT \n owner, \n email\n FROM \n capture_config\n WHERE \n workspace_id = $1 \n AND path = $2 \n AND is_flow = $3 \n AND trigger_kind = $4 \n AND last_client_ping > NOW() - INTERVAL '10 seconds'\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -32,7 +32,8 @@
|
||||
"nats",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"mqtt"
|
||||
"mqtt",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -44,5 +45,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "71d51bbc35da7b9930e3ea3a634451217ccb9f1bc35b1ad6e10d16bc19c41447"
|
||||
"hash": "87564a196a1662f524407d853db506bf08c28efe82b68b3d44bafbd3d0e91c29"
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n gcp_trigger \n SET \n server_id = $1, \n last_server_ping = now(),\n error = 'Connecting...'\n WHERE \n enabled IS TRUE \n AND workspace_id = $2 \n AND path = $3 \n AND (last_server_ping IS NULL \n OR last_server_ping < now() - INTERVAL '15 seconds'\n ) \n RETURNING true\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "92c2b66eb6287449f5f8cc9f8d1329f748006ab131489d2fcac21d32641bb633"
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM capture\n WHERE workspace_id = $1\n AND created_at <=\n (\n SELECT created_at\n FROM capture\n WHERE workspace_id = $1\n ORDER BY created_at DESC\n OFFSET $2\n LIMIT 1\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "97942578df746c8c8103b403cfc4e44ef5a0f082bdde854900064325adc4dd77"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n gcp_trigger \n SET\n last_server_ping = NULL \n WHERE \n workspace_id = $1 \n AND path = $2 \n AND server_id IS NULL",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "999edc6f54a9efb6dc6237992dad59f418ed6fc2a98ddb9bbc33dce5f029d904"
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n capture_config \n SET \n last_server_ping = now(), \n error = $1 \n WHERE \n workspace_id = $2 AND \n path = $3 AND \n is_flow = $4 AND \n trigger_kind = 'gcp' AND \n server_id = $5 AND \n last_client_ping > NOW() - INTERVAL '10 seconds' \n RETURNING 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "a7ffc5b983d365159ef379ec6a2ab0dc7217d1b3f86f362c213982984d2cf652"
|
||||
}
|
||||
+8
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS \"websocket_used!\",\n EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS \"http_routes_used!\",\n EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as \"kafka_used!\",\n EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as \"nats_used!\",\n EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS \"postgres_used!\",\n EXISTS(SELECT 1 FROM mqtt_trigger WHERE workspace_id = $1) AS \"mqtt_used!\",\n EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS \"sqs_used!\"\n ",
|
||||
"query": "\n SELECT\n EXISTS(SELECT 1 FROM websocket_trigger WHERE workspace_id = $1) AS \"websocket_used!\",\n EXISTS(SELECT 1 FROM http_trigger WHERE workspace_id = $1) AS \"http_routes_used!\",\n EXISTS(SELECT 1 FROM kafka_trigger WHERE workspace_id = $1) as \"kafka_used!\",\n EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as \"nats_used!\",\n EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS \"postgres_used!\",\n EXISTS(SELECT 1 FROM mqtt_trigger WHERE workspace_id = $1) AS \"mqtt_used!\",\n EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS \"sqs_used!\",\n EXISTS(SELECT 1 FROM gcp_trigger WHERE workspace_id = $1) AS \"gcp_used!\"\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -37,6 +37,11 @@
|
||||
"ordinal": 6,
|
||||
"name": "sqs_used!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "gcp_used!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -51,8 +56,9 @@
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "9ae20f2f29406cfa5337736ad710c496f1757d950cdec63c93c77fe79d5212cc"
|
||||
"hash": "aa0215d4174c1aeda8631bcd582c895329d2daf722d360fbcbdef6f04bb1400f"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO capture_config\n (workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (workspace_id, path, is_flow, trigger_kind)\n DO UPDATE SET trigger_config = $5, owner = $6, email = $7, server_id = NULL, error = NULL",
|
||||
"query": "\n INSERT INTO capture_config (\n workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7\n )\n ON CONFLICT (workspace_id, path, is_flow, trigger_kind)\n DO UPDATE \n SET \n trigger_config = $5, \n owner = $6, \n email = $7, \n server_id = NULL, \n error = NULL\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -21,7 +21,8 @@
|
||||
"nats",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"mqtt"
|
||||
"mqtt",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -33,5 +34,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "62475252dcf54f32433b97ae011daf5d4205d160d2aedf463c7dfe944e93257a"
|
||||
"hash": "b3f0595cacba194e08b9a3e244d9e637e9e156cd85b69126c87dfff89a47711d"
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n path,\n is_flow,\n workspace_id,\n owner,\n email,\n trigger_config as \"trigger_config!: _\"\n FROM\n capture_config\n WHERE\n trigger_kind = 'gcp' AND\n last_client_ping > NOW() - INTERVAL '10 seconds' AND\n trigger_config IS NOT NULL AND\n trigger_config->>'delivery_type' IS DISTINCT FROM 'push' AND\n (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "owner",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "trigger_config!: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "c0e6dbce7a401b06e1bf45155c3f81572818177a6024e743b12f558b46edf74c"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n DELETE \n FROM \n gcp_trigger \n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c19a60a9dc3f95af218baf40c62f14572ac204cfe377166aa1d91cf58f731f50"
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n capture_config\n SET \n path = $1\n WHERE \n path = $2 \n AND workspace_id = $3 \n AND is_flow = $4\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c353aa9abe673749f0836fd16894d91ba89aab2318d85621ad2868017adfb48a"
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "ddf2eccb78a310ed00c7d8b9c3f05d394a7cbcf0038c72a78add5c7b02ef5927"
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n gcp_trigger\n SET \n last_server_ping = now(),\n error = $1\n WHERE\n workspace_id = $2\n AND path = $3\n AND server_id = $4 \n AND enabled IS TRUE\n RETURNING 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "df2a426658f0a36683cc2163fe70912d0221d191b8ab091601cb53cdd932ad58"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n DELETE FROM \n capture\n WHERE \n workspace_id = $1\n AND created_at <= (\n SELECT \n created_at\n FROM \n capture\n WHERE \n workspace_id = $1\n ORDER BY \n created_at DESC\n OFFSET $2\n LIMIT 1\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e04a8a9f1e9cc3bb5c990194585e08f4c248e1af1c6580bdf8f2735ae1388981"
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE capture SET path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow = $4",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e70c3da5864b7946735b7ad6c416874ee8b277a2914dff6f9c33a1f5a2351114"
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "gcp_resource_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "subscription_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "topic_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "delivery_type: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
"name": "delivery_mode",
|
||||
"kind": {
|
||||
"Enum": [
|
||||
"push",
|
||||
"pull"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "delivery_config: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "script_path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "is_flow",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "edited_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "server_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "last_server_ping",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "extra_perms",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "enabled",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f4f6336fc671b00bed7835124892f7a4d3bbe673f7c48153819dab385a5cb357"
|
||||
}
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO capture (workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
"query": "\n INSERT INTO \n capture (\n workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -21,7 +21,8 @@
|
||||
"nats",
|
||||
"postgres",
|
||||
"sqs",
|
||||
"mqtt"
|
||||
"mqtt",
|
||||
"gcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -33,5 +34,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "07da723ce5c9ee2d7c236e8eabe254c783fc34b617c8a9a95a0eb0cda535dab5"
|
||||
"hash": "f9f7f31390f8ea1f4facd8e6a888886ea136f2327169bf93f126e2ef130d0946"
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE \n gcp_trigger \n SET \n enabled = FALSE, \n error = $1, \n server_id = NULL, \n last_server_ping = NULL \n WHERE \n workspace_id = $2 AND \n path = $3\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "fd0a0ca4a107dc813240ad71f372ab7e0bf26431158fbfb5f0023ed847ca7dc5"
|
||||
}
|
||||
Vendored
+1
-1
@@ -11,5 +11,5 @@
|
||||
"remote.autoForwardPorts": true,
|
||||
"conventionalCommits.scopes": [
|
||||
"restructring triggers, decoding trigger message on work"
|
||||
]
|
||||
],
|
||||
}
|
||||
|
||||
Generated
+139
-4
@@ -554,6 +554,17 @@ dependencies = [
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"event-listener 2.5.3",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.19"
|
||||
@@ -4602,6 +4613,12 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.4.0"
|
||||
@@ -5347,6 +5364,94 @@ dependencies = [
|
||||
"gl_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-auth"
|
||||
version = "0.17.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e57a13fbacc5e9c41ded3ad8d0373175a6b7a6ad430d99e89d314ac121b7ab06"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.21.7",
|
||||
"google-cloud-metadata",
|
||||
"google-cloud-token",
|
||||
"home",
|
||||
"jsonwebtoken 9.3.1",
|
||||
"reqwest 0.12.15",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 1.0.69",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-gax"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de13e62d7e0ffc3eb40a0113ddf753cf6ec741be739164442b08893db4f9bfca"
|
||||
dependencies = [
|
||||
"google-cloud-token",
|
||||
"http 1.3.1",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"tokio-retry2",
|
||||
"tonic",
|
||||
"tower 0.4.13",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-googleapis"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "886aa8ec755382a1fdf4651f6e6ec01f2f3bf49f2cb0f068b9a74cafd574a715"
|
||||
dependencies = [
|
||||
"prost",
|
||||
"prost-types",
|
||||
"tonic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-metadata"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d901aeb453fd80e51d64df4ee005014f6cf39f2d736dd64f7239c132d9d39a6a"
|
||||
dependencies = [
|
||||
"reqwest 0.12.15",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-pubsub"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bebc6e7327e49a66ffb40508c673b7643191bd6b509530193bda97f09272cdcf"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-stream",
|
||||
"google-cloud-auth",
|
||||
"google-cloud-gax",
|
||||
"google-cloud-googleapis",
|
||||
"google-cloud-token",
|
||||
"prost-types",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "google-cloud-token"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f49c12ba8b21d128a2ce8585955246977fbce4415f680ebf9199b6f9d6d725f"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gosyn"
|
||||
version = "0.2.9"
|
||||
@@ -6427,6 +6532,21 @@ dependencies = [
|
||||
"simple_asn1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "9.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"js-sys",
|
||||
"pem 3.0.5",
|
||||
"ring 0.17.14",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simple_asn1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "junction"
|
||||
version = "1.2.0"
|
||||
@@ -11025,7 +11145,7 @@ dependencies = [
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
"event-listener",
|
||||
"event-listener 5.4.0",
|
||||
"futures-core",
|
||||
"futures-intrusive",
|
||||
"futures-io",
|
||||
@@ -12447,6 +12567,16 @@ dependencies = [
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-retry2"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1264d076dd34560544a2799e40e457bd07c43d30f4a845686b031bcd8455c84f"
|
||||
dependencies = [
|
||||
"pin-project",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.24.1"
|
||||
@@ -12624,6 +12754,7 @@ dependencies = [
|
||||
"axum",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"flate2",
|
||||
"h2 0.4.8",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
@@ -12644,6 +12775,7 @@ dependencies = [
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -13843,6 +13975,8 @@ dependencies = [
|
||||
"datafusion",
|
||||
"futures",
|
||||
"git-version",
|
||||
"google-cloud-googleapis",
|
||||
"google-cloud-pubsub",
|
||||
"half",
|
||||
"hex",
|
||||
"hf-hub",
|
||||
@@ -13850,7 +13984,7 @@ dependencies = [
|
||||
"http 1.3.1",
|
||||
"hyper 1.6.0",
|
||||
"itertools 0.14.0",
|
||||
"jsonwebtoken",
|
||||
"jsonwebtoken 8.3.0",
|
||||
"lazy_static",
|
||||
"magic-crypt",
|
||||
"mail-parser",
|
||||
@@ -13893,6 +14027,7 @@ dependencies = [
|
||||
"tokio-tar",
|
||||
"tokio-tungstenite",
|
||||
"tokio-util",
|
||||
"tonic",
|
||||
"tower 0.5.2",
|
||||
"tower-cookies",
|
||||
"tower-http",
|
||||
@@ -13982,7 +14117,7 @@ dependencies = [
|
||||
"hyper 1.6.0",
|
||||
"indexmap 2.8.0",
|
||||
"itertools 0.14.0",
|
||||
"jsonwebtoken",
|
||||
"jsonwebtoken 8.3.0",
|
||||
"lazy_static",
|
||||
"magic-crypt",
|
||||
"mail-send",
|
||||
@@ -14350,7 +14485,7 @@ dependencies = [
|
||||
"git-version",
|
||||
"hex",
|
||||
"itertools 0.14.0",
|
||||
"jsonwebtoken",
|
||||
"jsonwebtoken 8.3.0",
|
||||
"lazy_static",
|
||||
"mappable-rc",
|
||||
"mysql_async",
|
||||
|
||||
+4
-1
@@ -69,13 +69,14 @@ nats = ["windmill-api/nats"]
|
||||
otel = ["windmill-common/otel", "windmill-worker/otel"]
|
||||
dind = ["windmill-worker/dind"]
|
||||
websocket = ["windmill-api/websocket"]
|
||||
http_trigger = ["windmill-api/http_trigger"]
|
||||
postgres_trigger = ["windmill-api/postgres_trigger"]
|
||||
mqtt_trigger = ["windmill-api/mqtt_trigger"]
|
||||
sqs_trigger = ["windmill-api/sqs_trigger"]
|
||||
gcp_trigger = ["windmill-api/gcp_trigger"]
|
||||
smtp = ["windmill-api/smtp", "windmill-common/smtp"]
|
||||
license = ["windmill-api/license"]
|
||||
oauth2 = ["windmill-api/oauth2"]
|
||||
http_trigger = ["windmill-api/http_trigger"]
|
||||
zip = ["windmill-api/zip"]
|
||||
static_frontend = ["windmill-api/static_frontend"]
|
||||
scoped_cache = ["windmill-common/scoped_cache"]
|
||||
@@ -242,6 +243,8 @@ deno_runtime = { version = "0.198.0", features = ["transpile"] }
|
||||
deno_telemetry = "0.12.0"
|
||||
deno_error = "=0.5.5"
|
||||
|
||||
google-cloud-pubsub = "0.30.0"
|
||||
google-cloud-googleapis = {version = "0.16.1", features = ["pubsub"]}
|
||||
# TODO: remove once deno fixes the issue on their end
|
||||
# https://github.com/denoland/deno/issues/28557
|
||||
winapi = { version = "0.3.9", features = ["sysinfoapi"] }
|
||||
|
||||
@@ -1 +1 @@
|
||||
0706783f5ebc6fad0f26cb1c0ba52fe1deeeb168
|
||||
85c37983ffb8f622458425c182613206625c6cee
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add down migration script here
|
||||
DROP TABLE gcp_trigger;
|
||||
DROP TYPE DELIVERY_MODE;
|
||||
@@ -0,0 +1,80 @@
|
||||
-- Add up migration script here
|
||||
|
||||
ALTER TYPE TRIGGER_KIND ADD VALUE IF NOT EXISTS 'gcp';
|
||||
CREATE TYPE DELIVERY_MODE AS ENUM ('push', 'pull');
|
||||
|
||||
CREATE TABLE gcp_trigger (
|
||||
gcp_resource_path VARCHAR(255) NOT NULL,
|
||||
topic_id VARCHAR(255) NOT NULL CHECK (
|
||||
CHAR_LENGTH(topic_id) BETWEEN 3 AND 255
|
||||
),
|
||||
subscription_id VARCHAR(255) NOT NULL CHECK (
|
||||
CHAR_LENGTH(subscription_id) BETWEEN 3 AND 255
|
||||
),
|
||||
delivery_type DELIVERY_MODE NOT NULL,
|
||||
delivery_config JSONB NULL CHECK (delivery_type != 'push'::DELIVERY_MODE OR (delivery_config IS NOT NULL)),
|
||||
path VARCHAR(255) NOT NULL,
|
||||
script_path VARCHAR(255) NOT NULL,
|
||||
is_flow BOOLEAN NOT NULL,
|
||||
workspace_id VARCHAR(50) NOT NULL,
|
||||
edited_by VARCHAR(50) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
edited_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
extra_perms JSONB NOT NULL DEFAULT '{}',
|
||||
server_id VARCHAR(50),
|
||||
last_server_ping TIMESTAMPTZ,
|
||||
error TEXT,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
PRIMARY KEY (path, workspace_id)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX unique_subscription_per_gcp_resource
|
||||
ON gcp_trigger (subscription_id, gcp_resource_path, workspace_id);
|
||||
|
||||
GRANT ALL ON gcp_trigger TO windmill_user;
|
||||
GRANT ALL ON gcp_trigger TO windmill_admin;
|
||||
|
||||
ALTER TABLE gcp_trigger ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY admin_policy ON gcp_trigger FOR ALL TO windmill_admin USING (true);
|
||||
|
||||
CREATE POLICY see_folder_extra_perms_user_select ON gcp_trigger FOR SELECT TO windmill_user
|
||||
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_read'), ',')::text[]));
|
||||
CREATE POLICY see_folder_extra_perms_user_insert ON gcp_trigger FOR INSERT TO windmill_user
|
||||
WITH CHECK (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
|
||||
CREATE POLICY see_folder_extra_perms_user_update ON gcp_trigger FOR UPDATE TO windmill_user
|
||||
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
|
||||
CREATE POLICY see_folder_extra_perms_user_delete ON gcp_trigger FOR DELETE TO windmill_user
|
||||
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'f' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.folders_write'), ',')::text[]));
|
||||
|
||||
CREATE POLICY see_own ON gcp_trigger FOR ALL TO windmill_user
|
||||
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'u' AND SPLIT_PART(gcp_trigger.path, '/', 2) = current_setting('session.user'));
|
||||
CREATE POLICY see_member ON gcp_trigger FOR ALL TO windmill_user
|
||||
USING (SPLIT_PART(gcp_trigger.path, '/', 1) = 'g' AND SPLIT_PART(gcp_trigger.path, '/', 2) = any(regexp_split_to_array(current_setting('session.groups'), ',')::text[]));
|
||||
|
||||
CREATE POLICY see_extra_perms_user_select ON gcp_trigger FOR SELECT TO windmill_user
|
||||
USING (extra_perms ? CONCAT('u/', current_setting('session.user')));
|
||||
CREATE POLICY see_extra_perms_user_insert ON gcp_trigger FOR INSERT TO windmill_user
|
||||
WITH CHECK ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
|
||||
CREATE POLICY see_extra_perms_user_update ON gcp_trigger FOR UPDATE TO windmill_user
|
||||
USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
|
||||
CREATE POLICY see_extra_perms_user_delete ON gcp_trigger FOR DELETE TO windmill_user
|
||||
USING ((extra_perms ->> CONCAT('u/', current_setting('session.user')))::boolean);
|
||||
|
||||
CREATE POLICY see_extra_perms_groups_select ON gcp_trigger FOR SELECT TO windmill_user
|
||||
USING (extra_perms ?| regexp_split_to_array(current_setting('session.pgroups'), ',')::text[]);
|
||||
CREATE POLICY see_extra_perms_groups_insert ON gcp_trigger FOR INSERT TO windmill_user
|
||||
WITH CHECK (exists(
|
||||
SELECT key, value FROM jsonb_each_text(extra_perms)
|
||||
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
|
||||
AND value::boolean));
|
||||
CREATE POLICY see_extra_perms_groups_update ON gcp_trigger FOR UPDATE TO windmill_user
|
||||
USING (exists(
|
||||
SELECT key, value FROM jsonb_each_text(extra_perms)
|
||||
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
|
||||
AND value::boolean));
|
||||
CREATE POLICY see_extra_perms_groups_delete ON gcp_trigger FOR DELETE TO windmill_user
|
||||
USING (exists(
|
||||
SELECT key, value FROM jsonb_each_text(extra_perms)
|
||||
WHERE SPLIT_PART(key, '/', 1) = 'g' AND key = ANY(regexp_split_to_array(current_setting('session.pgroups'), ',')::text[])
|
||||
AND value::boolean));
|
||||
+1
@@ -0,0 +1 @@
|
||||
-- Add down migration script here
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
-- Add up migration script here
|
||||
ALTER TYPE job_trigger_kind ADD VALUE IF NOT EXISTS 'sqs';
|
||||
ALTER TYPE job_trigger_kind ADD VALUE IF NOT EXISTS 'gcp';
|
||||
+3
-10
@@ -13,7 +13,7 @@ use std::{
|
||||
|
||||
use chrono::{NaiveDateTime, Utc};
|
||||
use futures::{stream::FuturesUnordered, StreamExt};
|
||||
use serde::{de::DeserializeOwned, Deserializer};
|
||||
use serde::de::DeserializeOwned;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use tokio::{
|
||||
join,
|
||||
@@ -34,6 +34,7 @@ use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts};
|
||||
#[cfg(feature = "oauth2")]
|
||||
use windmill_common::global_settings::OAUTH_SETTING;
|
||||
use windmill_common::{
|
||||
utils::empty_string_as_none,
|
||||
agent_workers::DECODED_AGENT_TOKEN, auth::create_token_for_owner, ee::CriticalErrorChannel, error, flow_status::{FlowStatus, FlowStatusModule}, global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING,
|
||||
CRITICAL_ERROR_CHANNELS_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING,
|
||||
@@ -46,7 +47,7 @@ use windmill_common::{
|
||||
SAML_METADATA_SETTING, SCIM_TOKEN_SETTING, TIMEOUT_WAIT_RESULT_SETTING,
|
||||
}, indexer::load_indexer_config, jobs::QueuedJob, jwt::JWT_SECRET, oauth2::REQUIRE_PREEXISTING_USER_FOR_OAUTH, server::load_smtp_config, tracing_init::JSON_FMT, users::truncate_token, utils::{now_from_db, rd_string, report_critical_error, Mode}, worker::{
|
||||
load_worker_config, reload_custom_tags_setting, store_pull_query, store_suspended_pull_query, update_min_version, Connection, DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, INDEXER_CONFIG, SCRIPT_TOKEN_EXPIRY, SMTP_CONFIG, TMP_DIR, WORKER_CONFIG, WORKER_GROUP
|
||||
}, KillpillSender, BASE_URL, CRITICAL_ALERT_MUTE_UI_ENABLED, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, METRICS_DEBUG_ENABLED, METRICS_ENABLED, MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED, OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, SERVICE_LOG_RETENTION_SECS
|
||||
}, KillpillSender, BASE_URL, CRITICAL_ALERT_MUTE_UI_ENABLED, CRITICAL_ERROR_CHANNELS, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, JOB_RETENTION_SECS, METRICS_DEBUG_ENABLED, METRICS_ENABLED, MONITOR_LOGS_ON_OBJECT_STORE, OTEL_LOGS_ENABLED, OTEL_METRICS_ENABLED, OTEL_TRACING_ENABLED, SERVICE_LOG_RETENTION_SECS,
|
||||
};
|
||||
use windmill_queue::{cancel_job, MiniPulledJob, SameWorkerPayload};
|
||||
use windmill_worker::{
|
||||
@@ -221,14 +222,6 @@ pub async fn load_metrics_enabled(conn: &Connection) -> error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let option = <Option<String> as serde::Deserialize>::deserialize(deserializer)?;
|
||||
Ok(option.filter(|s| !s.is_empty()))
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct OtelSetting {
|
||||
metrics_enabled: Option<bool>,
|
||||
|
||||
@@ -32,6 +32,7 @@ static_frontend = ["dep:rust-embed"]
|
||||
postgres_trigger = ["dep:rust-postgres", "dep:pg_escape", "dep:byteorder", "dep:thiserror", "dep:rust_decimal", "dep:rust-postgres-native-tls"]
|
||||
mqtt_trigger = ["dep:thiserror", "dep:rumqttc"]
|
||||
sqs_trigger = ["dep:aws-sdk-sqs", "dep:thiserror", "dep:aws-config"]
|
||||
gcp_trigger = ["dep:thiserror", "dep:google-cloud-pubsub", "dep:google-cloud-googleapis", "dep:tonic"]
|
||||
cloud = ["windmill-common/cloud"]
|
||||
|
||||
[dependencies]
|
||||
@@ -129,4 +130,7 @@ rust_decimal = { workspace = true, optional = true }
|
||||
rust-postgres-native-tls = { workspace = true, optional = true}
|
||||
rumqttc = { workspace = true, optional = true }
|
||||
aws-sdk-sqs = { workspace = true, optional = true }
|
||||
aws-config = { workspace = true, optional = true}
|
||||
aws-config = { workspace = true, optional = true }
|
||||
google-cloud-pubsub = { workspace = true, optional = true }
|
||||
google-cloud-googleapis = { workspace = true , optional = true }
|
||||
tonic = { workspace = true, optional = true }
|
||||
@@ -2702,6 +2702,8 @@ paths:
|
||||
type: boolean
|
||||
mqtt_used:
|
||||
type: boolean
|
||||
gcp_used:
|
||||
type: boolean
|
||||
sqs_used:
|
||||
type: boolean
|
||||
required:
|
||||
@@ -2711,6 +2713,7 @@ paths:
|
||||
- nats_used
|
||||
- postgres_used
|
||||
- mqtt_used
|
||||
- gcp_used
|
||||
- sqs_used
|
||||
/w/{workspace}/users/list:
|
||||
get:
|
||||
@@ -9222,6 +9225,264 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/gcp_triggers/create:
|
||||
post:
|
||||
summary: create gcp trigger
|
||||
operationId: createGcpTrigger
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: new gcp trigger
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/NewGcpTrigger"
|
||||
responses:
|
||||
"201":
|
||||
description: gcp trigger created
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/gcp_triggers/update/{path}:
|
||||
post:
|
||||
summary: update gcp trigger
|
||||
operationId: updateGcpTrigger
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
requestBody:
|
||||
description: updated trigger
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/EditGcpTrigger"
|
||||
responses:
|
||||
"200":
|
||||
description: gcp trigger updated
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/gcp_triggers/delete/{path}:
|
||||
delete:
|
||||
summary: delete gcp trigger
|
||||
operationId: deleteGcpTrigger
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
responses:
|
||||
"200":
|
||||
description: gcp trigger deleted
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/gcp_triggers/get/{path}:
|
||||
get:
|
||||
summary: get gcp trigger
|
||||
operationId: getGcpTrigger
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
responses:
|
||||
"200":
|
||||
description: gcp trigger deleted
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GcpTrigger"
|
||||
|
||||
/w/{workspace}/gcp_triggers/list:
|
||||
get:
|
||||
summary: list gcp triggers
|
||||
operationId: listGcpTriggers
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
required: true
|
||||
- $ref: "#/components/parameters/Page"
|
||||
- $ref: "#/components/parameters/PerPage"
|
||||
- name: path
|
||||
description: filter by path
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: is_flow
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
- name: path_start
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: gcp trigger list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/GcpTrigger"
|
||||
|
||||
/w/{workspace}/gcp_triggers/exists/{path}:
|
||||
get:
|
||||
summary: does gcp trigger exists
|
||||
operationId: existsGcpTrigger
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
responses:
|
||||
"200":
|
||||
description: gcp trigger exists
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: boolean
|
||||
|
||||
/w/{workspace}/gcp_triggers/setenabled/{path}:
|
||||
post:
|
||||
summary: set enabled gcp trigger
|
||||
operationId: setGcpTriggerEnabled
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
requestBody:
|
||||
description: updated gcp trigger enable
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
required:
|
||||
- enabled
|
||||
responses:
|
||||
"200":
|
||||
description: gcp trigger enabled set
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/gcp_triggers/test:
|
||||
post:
|
||||
summary: test gcp connection
|
||||
operationId: testGcpConnection
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: test gcp connection
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
connection:
|
||||
type: object
|
||||
required:
|
||||
- connection
|
||||
responses:
|
||||
"200":
|
||||
description: try to connect to a gcp broker
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
|
||||
/w/{workspace}/gcp_triggers/subscriptions/delete/{path}:
|
||||
delete:
|
||||
summary: delete gcp trigger
|
||||
operationId: deleteGcpSubscription
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
requestBody:
|
||||
description: args to delete subscription from google cloud
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/DeleteGcpSubscription"
|
||||
responses:
|
||||
"200":
|
||||
description: gcp trigger deleted
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/gcp_triggers/topics/list/{path}:
|
||||
get:
|
||||
summary: list all topics of google cloud service
|
||||
operationId: listGoogleTopics
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
responses:
|
||||
"200":
|
||||
description: get all google topics
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/gcp_triggers/subscriptions/list/{path}:
|
||||
post:
|
||||
summary: list all subscription of a give topic from google cloud service
|
||||
operationId: listAllTGoogleTopicSubscriptions
|
||||
tags:
|
||||
- gcp_trigger
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Path"
|
||||
requestBody:
|
||||
description: args to get subscription's topic from google cloud
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/GetAllTopicSubscription"
|
||||
responses:
|
||||
"200":
|
||||
description: get all google topic subscriptions name
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/postgres_triggers/is_valid_postgres_configuration/{path}:
|
||||
get:
|
||||
@@ -10563,6 +10824,7 @@ paths:
|
||||
nats_trigger,
|
||||
postgres_trigger,
|
||||
mqtt_trigger,
|
||||
gcp_trigger,
|
||||
sqs_trigger
|
||||
]
|
||||
responses:
|
||||
@@ -10606,6 +10868,7 @@ paths:
|
||||
nats_trigger,
|
||||
postgres_trigger,
|
||||
mqtt_trigger,
|
||||
gcp_trigger,
|
||||
sqs_trigger
|
||||
]
|
||||
requestBody:
|
||||
@@ -10660,6 +10923,7 @@ paths:
|
||||
nats_trigger,
|
||||
postgres_trigger,
|
||||
mqtt_trigger,
|
||||
gcp_trigger,
|
||||
sqs_trigger
|
||||
]
|
||||
requestBody:
|
||||
@@ -10712,6 +10976,10 @@ paths:
|
||||
responses:
|
||||
"200":
|
||||
description: capture config set
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
|
||||
/w/{workspace}/capture/ping_config/{trigger_kind}/{runnable_kind}/{path}:
|
||||
post:
|
||||
@@ -14190,6 +14458,8 @@ components:
|
||||
type: number
|
||||
mqtt_count:
|
||||
type: number
|
||||
gcp_count:
|
||||
type: number
|
||||
sqs_count:
|
||||
type: number
|
||||
|
||||
@@ -14474,6 +14744,173 @@ components:
|
||||
- subscribe_topics
|
||||
- mqtt_resource_path
|
||||
|
||||
DeliveryType:
|
||||
type: string
|
||||
enum:
|
||||
- push
|
||||
- pull
|
||||
|
||||
PushConfig:
|
||||
type: object
|
||||
properties:
|
||||
audience:
|
||||
type: string
|
||||
authenticate:
|
||||
type: boolean
|
||||
base_endpoint:
|
||||
type: string
|
||||
required:
|
||||
- authenticate
|
||||
- base_endpoint
|
||||
|
||||
GcpTrigger:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/TriggerExtraProperty"
|
||||
type: object
|
||||
properties:
|
||||
gcp_resource_path:
|
||||
type: string
|
||||
topic_id:
|
||||
type: string
|
||||
subscription_id:
|
||||
type: string
|
||||
server_id:
|
||||
type: string
|
||||
delivery_type:
|
||||
$ref: "#/components/schemas/DeliveryType"
|
||||
delivery_config:
|
||||
$ref: "#/components/schemas/PushConfig"
|
||||
last_server_ping:
|
||||
type: string
|
||||
format: date-time
|
||||
error:
|
||||
type: string
|
||||
enabled:
|
||||
type: boolean
|
||||
required:
|
||||
- gcp_resource_path
|
||||
- topic_id
|
||||
- subscription_id
|
||||
- enabled
|
||||
- delivery_type
|
||||
|
||||
SubscriptionMode:
|
||||
type: string
|
||||
enum:
|
||||
- existing
|
||||
- create_update
|
||||
description: "The mode of subscription. 'existing' means using an existing GCP subscription, while 'create_update' involves creating or updating a new subscription."
|
||||
|
||||
GcpExistingSubscription:
|
||||
type: object
|
||||
properties:
|
||||
subscription_id:
|
||||
type: string
|
||||
base_endpoint:
|
||||
type: string
|
||||
required:
|
||||
- subscription_id
|
||||
- base_endpoint
|
||||
|
||||
GcpCreateUpdateSubscription:
|
||||
type: object
|
||||
properties:
|
||||
subscription_id:
|
||||
type: string
|
||||
delivery_type:
|
||||
$ref: "#/components/schemas/DeliveryType"
|
||||
delivery_config:
|
||||
$ref: "#/components/schemas/PushConfig"
|
||||
required:
|
||||
- delivery_type
|
||||
|
||||
|
||||
GcpSubscriptionModeConfig:
|
||||
type: object
|
||||
properties:
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/SubscriptionMode"
|
||||
required:
|
||||
- subscription_mode
|
||||
allOf:
|
||||
- oneOf:
|
||||
- $ref: "#/components/schemas/GcpExistingSubscription"
|
||||
- $ref: "#/components/schemas/GcpCreateUpdateSubscription"
|
||||
description: |
|
||||
"This is a union type representing the subscription mode.
|
||||
- 'existing': Represents an existing GCP subscription, and should be accompanied by an 'ExistingGcpSubscription' object.
|
||||
- 'create_update': Represents a new or updated GCP subscription, and should be accompanied by a 'CreateUpdateConfig' object."
|
||||
|
||||
NewGcpTrigger:
|
||||
type: object
|
||||
properties:
|
||||
gcp_resource_path:
|
||||
type: string
|
||||
topic_id:
|
||||
type: string
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/GcpSubscriptionModeConfig"
|
||||
path:
|
||||
type: string
|
||||
script_path:
|
||||
type: string
|
||||
is_flow:
|
||||
type: boolean
|
||||
enabled:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
- is_flow
|
||||
- gcp_resource_path
|
||||
- topic_id
|
||||
- subscription_mode
|
||||
|
||||
EditGcpTrigger:
|
||||
type: object
|
||||
properties:
|
||||
gcp_resource_path:
|
||||
type: string
|
||||
topic_id:
|
||||
type: string
|
||||
subscription_mode:
|
||||
$ref: "#/components/schemas/GcpSubscriptionModeConfig"
|
||||
path:
|
||||
type: string
|
||||
script_path:
|
||||
type: string
|
||||
is_flow:
|
||||
type: boolean
|
||||
enabled:
|
||||
type: boolean
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
- is_flow
|
||||
- enabled
|
||||
- mqtt_resource_path
|
||||
- subscription_id
|
||||
- delivery_type
|
||||
- topic_id
|
||||
- subscription_mode
|
||||
|
||||
GetAllTopicSubscription:
|
||||
type: object
|
||||
properties:
|
||||
topic_id:
|
||||
type: string
|
||||
required:
|
||||
- topic_id
|
||||
|
||||
|
||||
DeleteGcpSubscription:
|
||||
type: object
|
||||
properties:
|
||||
subscription_id:
|
||||
type: string
|
||||
required:
|
||||
- subscription_id
|
||||
|
||||
SqsTrigger:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/TriggerExtraProperty"
|
||||
@@ -15859,7 +16296,7 @@ components:
|
||||
|
||||
CaptureTriggerKind:
|
||||
type: string
|
||||
enum: [webhook, http, websocket, kafka, email, nats, postgres, sqs, mqtt]
|
||||
enum: [webhook, http, websocket, kafka, email, nats, postgres, sqs, mqtt, gcp]
|
||||
|
||||
Capture:
|
||||
type: object
|
||||
|
||||
@@ -509,7 +509,6 @@ where
|
||||
.map(|x| x.0)
|
||||
.unwrap_or_default();
|
||||
let path_vec: Vec<&str> = original_uri.path().split("/").collect();
|
||||
|
||||
let workspace_id = if path_vec.len() >= 4 && path_vec[0] == "" && path_vec[2] == "w" {
|
||||
Some(path_vec[3].to_owned())
|
||||
} else {
|
||||
|
||||
@@ -7,34 +7,47 @@
|
||||
*/
|
||||
|
||||
#[cfg(feature = "http_trigger")]
|
||||
use crate::http_triggers::{build_http_trigger_extra, HttpMethod};
|
||||
use {
|
||||
crate::{
|
||||
args::try_from_request_body,
|
||||
http_triggers::{build_http_trigger_extra, HttpMethod},
|
||||
},
|
||||
axum::response::{IntoResponse, Response},
|
||||
std::collections::HashMap,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
use crate::gcp_triggers_ee::{
|
||||
manage_google_subscription, process_google_push_request, validate_jwt_token, SubscriptionMode,
|
||||
};
|
||||
|
||||
#[cfg(any(
|
||||
feature = "http_trigger",
|
||||
all(feature = "enterprise", feature = "gcp_trigger")
|
||||
))]
|
||||
use {
|
||||
axum::extract::Request, http::HeaderMap, serde::de::DeserializeOwned,
|
||||
windmill_common::error::Error,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "kafka"))]
|
||||
use crate::kafka_triggers_ee::KafkaTriggerConfigConnection;
|
||||
|
||||
#[cfg(feature = "mqtt_trigger")]
|
||||
use crate::mqtt_triggers::{MqttClientVersion, MqttV3Config, MqttV5Config, SubscribeTopic};
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "nats"))]
|
||||
use crate::nats_triggers_ee::NatsTriggerConfigConnection;
|
||||
|
||||
#[cfg(feature = "postgres_trigger")]
|
||||
use crate::postgres_triggers::{
|
||||
create_logical_replication_slot_query, create_publication_query, drop_publication_query,
|
||||
generate_random_string, get_database_connection, PublicationData,
|
||||
use {
|
||||
crate::postgres_triggers::{
|
||||
create_logical_replication_slot_query, create_publication_query, drop_publication_query,
|
||||
generate_random_string, get_database_connection, PublicationData,
|
||||
},
|
||||
itertools::Itertools,
|
||||
pg_escape::quote_literal,
|
||||
};
|
||||
#[cfg(feature = "http_trigger")]
|
||||
use axum::extract::Request;
|
||||
#[cfg(feature = "http_trigger")]
|
||||
use axum::response::IntoResponse;
|
||||
#[cfg(feature = "http_trigger")]
|
||||
use http::HeaderMap;
|
||||
#[cfg(feature = "postgres_trigger")]
|
||||
use itertools::Itertools;
|
||||
#[cfg(feature = "postgres_trigger")]
|
||||
use pg_escape::quote_literal;
|
||||
#[cfg(feature = "http_trigger")]
|
||||
use serde::de::DeserializeOwned;
|
||||
#[cfg(feature = "http_trigger")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "http_trigger")]
|
||||
use windmill_common::error::Error;
|
||||
|
||||
use crate::{
|
||||
args::WebhookArgs,
|
||||
@@ -42,23 +55,26 @@ use crate::{
|
||||
users::fetch_api_authed,
|
||||
utils::RunnableKind,
|
||||
};
|
||||
|
||||
use axum::{
|
||||
extract::{Extension, Path, Query},
|
||||
routing::{delete, get, head, post},
|
||||
Json, Router,
|
||||
};
|
||||
|
||||
use hyper::StatusCode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue;
|
||||
use sqlx::types::Json as SqlxJson;
|
||||
|
||||
use windmill_common::{
|
||||
db::UserDB,
|
||||
error::{JsonResult, Result},
|
||||
utils::{not_found_if_none, paginate, Pagination, StripPath},
|
||||
worker::{to_raw_value, CLOUD_HOSTED},
|
||||
};
|
||||
use windmill_queue::TriggerKind;
|
||||
use windmill_queue::{PushArgs, PushArgsOwned};
|
||||
|
||||
use windmill_queue::{PushArgs, PushArgsOwned, TriggerKind};
|
||||
|
||||
const KEEP_LAST: i64 = 20;
|
||||
|
||||
@@ -85,14 +101,26 @@ pub fn workspaced_unauthed_service() -> Router {
|
||||
head(|| async {}).post(webhook_payload),
|
||||
);
|
||||
|
||||
#[cfg(feature = "http_trigger")]
|
||||
#[cfg(any(
|
||||
feature = "http_trigger",
|
||||
all(feature = "enterprise", feature = "gcp_trigger")
|
||||
))]
|
||||
{
|
||||
router.route("/http/:runnable_kind/:path/*route_path", {
|
||||
#[cfg(feature = "http_trigger")]
|
||||
let router = router.route("/http/:runnable_kind/:path/*route_path", {
|
||||
head(|| async {}).fallback(http_payload)
|
||||
})
|
||||
});
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
let router = router.route("/gcp/:runnable_kind/*path", post(gcp_payload));
|
||||
|
||||
router
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "http_trigger"))]
|
||||
#[cfg(not(any(
|
||||
feature = "http_trigger",
|
||||
all(feature = "enterprise", feature = "gcp_trigger")
|
||||
)))]
|
||||
{
|
||||
router
|
||||
}
|
||||
@@ -124,6 +152,15 @@ pub struct SqsTriggerConfig {
|
||||
pub message_attributes: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct GcpTriggerConfig {
|
||||
pub gcp_resource_path: String,
|
||||
#[serde(flatten)]
|
||||
pub subscription_mode: SubscriptionMode,
|
||||
pub topic_id: String,
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "nats"))]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct NatsTriggerConfig {
|
||||
@@ -181,6 +218,8 @@ enum TriggerConfig {
|
||||
Nats(NatsTriggerConfig),
|
||||
#[cfg(feature = "mqtt_trigger")]
|
||||
Mqtt(MqttTriggerConfig),
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
Gcp(GcpTriggerConfig),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -208,15 +247,26 @@ async fn get_configs(
|
||||
|
||||
let configs = sqlx::query_as!(
|
||||
CaptureConfig,
|
||||
r#"SELECT trigger_config as "trigger_config: _", trigger_kind as "trigger_kind: _", error, last_server_ping
|
||||
FROM capture_config
|
||||
WHERE workspace_id = $1 AND path = $2 AND is_flow = $3"#,
|
||||
r#"
|
||||
SELECT
|
||||
trigger_config AS "trigger_config: _",
|
||||
trigger_kind AS "trigger_kind: _",
|
||||
error,
|
||||
last_server_ping
|
||||
FROM
|
||||
capture_config
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND path = $2
|
||||
AND is_flow = $3
|
||||
"#,
|
||||
&w_id,
|
||||
&path.to_path(),
|
||||
matches!(runnable_kind, RunnableKind::Flow),
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Json(configs))
|
||||
@@ -284,33 +334,100 @@ async fn set_postgres_trigger_config(
|
||||
Ok(capture_config)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(feature = "postgres_trigger"))]
|
||||
async fn set_postgres_trigger_config(
|
||||
_w_id: &str,
|
||||
_authed: ApiAuthed,
|
||||
_db: &DB,
|
||||
_user_db: UserDB,
|
||||
capture_config: NewCaptureConfig,
|
||||
) -> Result<NewCaptureConfig> {
|
||||
Ok(capture_config)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
async fn set_gcp_trigger_config(
|
||||
w_id: &str,
|
||||
authed: ApiAuthed,
|
||||
db: &DB,
|
||||
mut capture_config: NewCaptureConfig,
|
||||
) -> Result<NewCaptureConfig> {
|
||||
let Some(TriggerConfig::Gcp(mut gcp_config)) = capture_config.trigger_config else {
|
||||
return Err(windmill_common::error::Error::BadRequest(
|
||||
"Invalid GCP Pub/Sub config".to_string(),
|
||||
));
|
||||
};
|
||||
|
||||
let config = manage_google_subscription(
|
||||
authed,
|
||||
db,
|
||||
w_id,
|
||||
&gcp_config.gcp_resource_path,
|
||||
&capture_config.path,
|
||||
&gcp_config.topic_id,
|
||||
gcp_config.subscription_mode,
|
||||
)
|
||||
.await?;
|
||||
|
||||
gcp_config.subscription_mode = SubscriptionMode::CreateUpdate(config);
|
||||
capture_config.trigger_config = Some(TriggerConfig::Gcp(gcp_config));
|
||||
|
||||
Ok(capture_config)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(all(feature = "enterprise", feature = "gcp_trigger")))]
|
||||
async fn set_gcp_trigger_config(
|
||||
_w_id: &str,
|
||||
_authed: ApiAuthed,
|
||||
_db: &DB,
|
||||
capture_config: NewCaptureConfig,
|
||||
) -> Result<NewCaptureConfig> {
|
||||
Ok(capture_config)
|
||||
}
|
||||
|
||||
async fn set_config(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
#[cfg(feature = "postgres_trigger")] Extension(db): Extension<DB>,
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(nc): Json<NewCaptureConfig>,
|
||||
) -> Result<()> {
|
||||
#[cfg(feature = "postgres_trigger")]
|
||||
let nc = if let TriggerKind::Postgres = nc.trigger_kind {
|
||||
set_postgres_trigger_config(&w_id, authed.clone(), &db, user_db.clone(), nc).await?
|
||||
} else {
|
||||
nc
|
||||
) -> JsonResult<Option<TriggerConfig>> {
|
||||
let nc = match nc.trigger_kind {
|
||||
TriggerKind::Postgres => {
|
||||
set_postgres_trigger_config(&w_id, authed.clone(), &db, user_db.clone(), nc).await?
|
||||
}
|
||||
TriggerKind::Gcp => set_gcp_trigger_config(&w_id, authed.clone(), &db, nc).await?,
|
||||
_ => nc,
|
||||
};
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO capture_config
|
||||
(workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
r#"
|
||||
INSERT INTO capture_config (
|
||||
workspace_id, path, is_flow, trigger_kind, trigger_config, owner, email
|
||||
)
|
||||
VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7
|
||||
)
|
||||
ON CONFLICT (workspace_id, path, is_flow, trigger_kind)
|
||||
DO UPDATE SET trigger_config = $5, owner = $6, email = $7, server_id = NULL, error = NULL",
|
||||
DO UPDATE
|
||||
SET
|
||||
trigger_config = $5,
|
||||
owner = $6,
|
||||
email = $7,
|
||||
server_id = NULL,
|
||||
error = NULL
|
||||
"#,
|
||||
&w_id,
|
||||
&nc.path,
|
||||
nc.is_flow,
|
||||
nc.trigger_kind as TriggerKind,
|
||||
nc.trigger_config.map(|x| SqlxJson(to_raw_value(&x))) as Option<SqlxJson<Box<RawValue>>>,
|
||||
nc.trigger_config
|
||||
.as_ref()
|
||||
.map(|x| SqlxJson(to_raw_value(&x))) as Option<SqlxJson<Box<RawValue>>>,
|
||||
&authed.username,
|
||||
&authed.email,
|
||||
)
|
||||
@@ -319,7 +436,7 @@ async fn set_config(
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(())
|
||||
Ok(Json(nc.trigger_config))
|
||||
}
|
||||
|
||||
async fn ping_config(
|
||||
@@ -333,8 +450,19 @@ async fn ping_config(
|
||||
)>,
|
||||
) -> Result<()> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE capture_config SET last_client_ping = now() WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4",
|
||||
r#"
|
||||
UPDATE
|
||||
capture_config
|
||||
SET
|
||||
last_client_ping = NOW()
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND path = $2
|
||||
AND is_flow = $3
|
||||
AND trigger_kind = $4
|
||||
"#,
|
||||
&w_id,
|
||||
&path.to_path(),
|
||||
matches!(runnable_kind, RunnableKind::Flow),
|
||||
@@ -342,6 +470,7 @@ async fn ping_config(
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -374,14 +503,28 @@ async fn list_captures(
|
||||
|
||||
let captures = sqlx::query_as!(
|
||||
Capture,
|
||||
r#"SELECT id, created_at, trigger_kind as "trigger_kind: _", CASE WHEN pg_column_size(payload) < 40000 THEN payload ELSE '"WINDMILL_TOO_BIG"'::jsonb END as "payload!: _", trigger_extra as "trigger_extra: _"
|
||||
FROM capture
|
||||
WHERE workspace_id = $1
|
||||
AND path = $2 AND is_flow = $3
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
created_at,
|
||||
trigger_kind AS "trigger_kind: _",
|
||||
CASE
|
||||
WHEN pg_column_size(payload) < 40000 THEN payload
|
||||
ELSE '"WINDMILL_TOO_BIG"'::jsonb
|
||||
END AS "payload!: _",
|
||||
trigger_extra AS "trigger_extra: _"
|
||||
FROM
|
||||
capture
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND path = $2
|
||||
AND is_flow = $3
|
||||
AND ($4::trigger_kind IS NULL OR trigger_kind = $4)
|
||||
ORDER BY created_at DESC
|
||||
ORDER BY
|
||||
created_at DESC
|
||||
OFFSET $5
|
||||
LIMIT $6"#,
|
||||
LIMIT $6
|
||||
"#,
|
||||
&w_id,
|
||||
&path.to_path(),
|
||||
matches!(runnable_kind, RunnableKind::Flow),
|
||||
@@ -403,14 +546,28 @@ async fn get_capture(
|
||||
Path((w_id, id)): Path<(String, i64)>,
|
||||
) -> JsonResult<Capture> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let capture = sqlx::query_as!(
|
||||
Capture,
|
||||
r#"SELECT id, created_at, trigger_kind as "trigger_kind: _", payload as "payload!: _", trigger_extra as "trigger_extra: _" FROM capture WHERE id = $1 AND workspace_id = $2"#,
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
created_at,
|
||||
trigger_kind AS "trigger_kind: _",
|
||||
payload AS "payload!: _",
|
||||
trigger_extra AS "trigger_extra: _"
|
||||
FROM
|
||||
capture
|
||||
WHERE
|
||||
id = $1
|
||||
AND workspace_id = $2
|
||||
"#,
|
||||
id,
|
||||
&w_id,
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(Json(capture))
|
||||
}
|
||||
@@ -421,9 +578,17 @@ async fn delete_capture(
|
||||
Path((_, id)): Path<(String, i64)>,
|
||||
) -> Result<()> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
sqlx::query!("DELETE FROM capture WHERE id = $1", id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
r#"
|
||||
DELETE FROM
|
||||
capture
|
||||
WHERE
|
||||
id = $1
|
||||
"#,
|
||||
id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -441,8 +606,18 @@ async fn move_captures_and_configs(
|
||||
) -> Result<()> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let old_path = old_path.to_path();
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE capture_config SET path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow = $4",
|
||||
r#"
|
||||
UPDATE
|
||||
capture_config
|
||||
SET
|
||||
path = $1
|
||||
WHERE
|
||||
path = $2
|
||||
AND workspace_id = $3
|
||||
AND is_flow = $4
|
||||
"#,
|
||||
body.new_path,
|
||||
old_path,
|
||||
&w_id,
|
||||
@@ -450,8 +625,18 @@ async fn move_captures_and_configs(
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE capture SET path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow = $4",
|
||||
r#"
|
||||
UPDATE
|
||||
capture
|
||||
SET
|
||||
path = $1
|
||||
WHERE
|
||||
path = $2
|
||||
AND workspace_id = $3
|
||||
AND is_flow = $4
|
||||
"#,
|
||||
body.new_path,
|
||||
old_path,
|
||||
&w_id,
|
||||
@@ -459,6 +644,7 @@ async fn move_captures_and_configs(
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -478,9 +664,19 @@ pub async fn get_active_capture_owner_and_email(
|
||||
) -> Result<(String, String)> {
|
||||
let capture_config = sqlx::query_as!(
|
||||
ActiveCaptureOwner,
|
||||
"SELECT owner, email
|
||||
FROM capture_config
|
||||
WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'",
|
||||
r#"
|
||||
SELECT
|
||||
owner,
|
||||
email
|
||||
FROM
|
||||
capture_config
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND path = $2
|
||||
AND is_flow = $3
|
||||
AND trigger_kind = $4
|
||||
AND last_client_ping > NOW() - INTERVAL '10 seconds'
|
||||
"#,
|
||||
&w_id,
|
||||
&path,
|
||||
is_flow,
|
||||
@@ -498,7 +694,10 @@ pub async fn get_active_capture_owner_and_email(
|
||||
Ok((capture_config.owner, capture_config.email))
|
||||
}
|
||||
|
||||
#[cfg(feature = "http_trigger")]
|
||||
#[cfg(any(
|
||||
feature = "http_trigger",
|
||||
all(feature = "enterprise", feature = "gcp_trigger")
|
||||
))]
|
||||
async fn get_capture_trigger_config_and_owner<T: DeserializeOwned>(
|
||||
db: &DB,
|
||||
w_id: &str,
|
||||
@@ -512,16 +711,34 @@ async fn get_capture_trigger_config_and_owner<T: DeserializeOwned>(
|
||||
owner: String,
|
||||
email: String,
|
||||
}
|
||||
|
||||
let capture_config = sqlx::query_as!(
|
||||
CaptureTriggerConfigAndOwner,
|
||||
r#"SELECT trigger_config as "trigger_config: _", owner, email
|
||||
FROM capture_config
|
||||
WHERE workspace_id = $1 AND path = $2 AND is_flow = $3 AND trigger_kind = $4 AND last_client_ping > NOW() - INTERVAL '10 seconds'"#,
|
||||
r#"
|
||||
SELECT
|
||||
trigger_config AS "trigger_config: _",
|
||||
owner,
|
||||
email
|
||||
FROM
|
||||
capture_config
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND path = $2
|
||||
AND is_flow = $3
|
||||
AND trigger_kind = $4
|
||||
AND last_client_ping > NOW() - INTERVAL '10 seconds'
|
||||
AND (
|
||||
$5::bool IS FALSE
|
||||
OR (
|
||||
trigger_config IS NOT NULL
|
||||
AND trigger_config ->> 'delivery_type' = 'push'
|
||||
)
|
||||
)
|
||||
"#,
|
||||
&w_id,
|
||||
&path,
|
||||
is_flow,
|
||||
kind as &TriggerKind,
|
||||
matches!(kind, TriggerKind::Gcp)
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
@@ -554,17 +771,24 @@ async fn clear_captures_history(db: &DB, w_id: &str) -> Result<()> {
|
||||
if *CLOUD_HOSTED {
|
||||
/* Retain only KEEP_LAST most recent captures in this workspace. */
|
||||
sqlx::query!(
|
||||
"DELETE FROM capture
|
||||
WHERE workspace_id = $1
|
||||
AND created_at <=
|
||||
(
|
||||
SELECT created_at
|
||||
FROM capture
|
||||
WHERE workspace_id = $1
|
||||
ORDER BY created_at DESC
|
||||
OFFSET $2
|
||||
LIMIT 1
|
||||
)",
|
||||
r#"
|
||||
DELETE FROM
|
||||
capture
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
AND created_at <= (
|
||||
SELECT
|
||||
created_at
|
||||
FROM
|
||||
capture
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
ORDER BY
|
||||
created_at DESC
|
||||
OFFSET $2
|
||||
LIMIT 1
|
||||
)
|
||||
"#,
|
||||
&w_id,
|
||||
KEEP_LAST,
|
||||
)
|
||||
@@ -585,8 +809,15 @@ pub async fn insert_capture_payload(
|
||||
owner: &str,
|
||||
) -> Result<()> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO capture (workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
r#"
|
||||
INSERT INTO
|
||||
capture (
|
||||
workspace_id, path, is_flow, trigger_kind, payload, trigger_extra, created_by
|
||||
)
|
||||
VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7
|
||||
)
|
||||
"#,
|
||||
&w_id,
|
||||
path,
|
||||
is_flow,
|
||||
@@ -642,36 +873,74 @@ async fn webhook_payload(
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
async fn gcp_payload(
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, runnable_kind, path)): Path<(String, RunnableKind, String)>,
|
||||
headers: HeaderMap,
|
||||
request: Request,
|
||||
) -> Result<StatusCode> {
|
||||
let is_flow = matches!(runnable_kind, RunnableKind::Flow);
|
||||
let (gcp_trigger_config, owner, email): (GcpTriggerConfig, _, _) =
|
||||
get_capture_trigger_config_and_owner(&db, &w_id, &path, is_flow, &TriggerKind::Gcp).await?;
|
||||
|
||||
let authed = fetch_api_authed(owner.clone(), email, &w_id, &db, None).await?;
|
||||
|
||||
let SubscriptionMode::CreateUpdate(config) = &gcp_trigger_config.subscription_mode else {
|
||||
return Err(Error::BadConfig("Bad config".to_string()));
|
||||
};
|
||||
|
||||
validate_jwt_token(
|
||||
&db,
|
||||
user_db.clone(),
|
||||
authed.clone(),
|
||||
&headers,
|
||||
&gcp_trigger_config.gcp_resource_path,
|
||||
&w_id,
|
||||
config.delivery_config.as_ref().unwrap(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let (args, extra) = process_google_push_request(headers, request).await?;
|
||||
|
||||
let payload = PushArgsOwned { args, extra: None };
|
||||
|
||||
let _ = insert_capture_payload(
|
||||
&db,
|
||||
&w_id,
|
||||
&path,
|
||||
is_flow,
|
||||
&TriggerKind::Gcp,
|
||||
payload,
|
||||
Some(to_raw_value(&extra)),
|
||||
&owner,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
#[cfg(feature = "http_trigger")]
|
||||
async fn http_payload(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, kind, path, route_path)): Path<(String, RunnableKind, String, StripPath)>,
|
||||
Path((w_id, runnable_kind, path, route_path)): Path<(String, RunnableKind, String, StripPath)>,
|
||||
Query(query): Query<HashMap<String, String>>,
|
||||
method: http::Method,
|
||||
headers: HeaderMap,
|
||||
request: Request,
|
||||
) -> std::result::Result<StatusCode, impl IntoResponse> {
|
||||
use axum::response::Response;
|
||||
|
||||
use crate::args::try_from_request_body;
|
||||
|
||||
let route_path = route_path.to_path();
|
||||
) -> std::result::Result<StatusCode, Response> {
|
||||
let path = path.replace(".", "/");
|
||||
|
||||
let is_flow = matches!(runnable_kind, RunnableKind::Flow);
|
||||
let route_path = route_path.to_path();
|
||||
let (http_trigger_config, owner, email): (HttpTriggerConfig, _, _) =
|
||||
get_capture_trigger_config_and_owner(
|
||||
&db,
|
||||
&w_id,
|
||||
&path,
|
||||
matches!(kind, RunnableKind::Flow),
|
||||
&TriggerKind::Http,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| e.into_response())?;
|
||||
get_capture_trigger_config_and_owner(&db, &w_id, &path, is_flow, &TriggerKind::Http)
|
||||
.await
|
||||
.map_err(|e| e.into_response())?;
|
||||
|
||||
let args = try_from_request_body(
|
||||
request,
|
||||
&db,
|
||||
&(),
|
||||
http_trigger_config.raw_string,
|
||||
http_trigger_config.wrap_body,
|
||||
)
|
||||
@@ -721,7 +990,7 @@ async fn http_payload(
|
||||
&db,
|
||||
&w_id,
|
||||
&path,
|
||||
matches!(kind, RunnableKind::Flow),
|
||||
is_flow,
|
||||
&TriggerKind::Http,
|
||||
args,
|
||||
extra,
|
||||
@@ -730,5 +999,5 @@ async fn http_payload(
|
||||
.await
|
||||
.map_err(|e| e.into_response())?;
|
||||
|
||||
Ok::<_, Response>(StatusCode::NO_CONTENT)
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
use crate::db::{ApiAuthed, DB};
|
||||
use axum::{extract::Request, Router};
|
||||
use http::HeaderMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue;
|
||||
use sqlx::prelude::FromRow;
|
||||
use sqlx::types::Json as SqlxJson;
|
||||
use std::collections::HashMap;
|
||||
use windmill_common::db::UserDB;
|
||||
use windmill_common::{
|
||||
error::{Error as WindmillError, Result as WindmillResult},
|
||||
utils::empty_string_as_none,
|
||||
};
|
||||
|
||||
#[derive(sqlx::Type, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))]
|
||||
#[sqlx(type_name = "DELIVERY_MODE", rename_all = "lowercase")]
|
||||
#[allow(unused)]
|
||||
pub enum DeliveryType {
|
||||
Pull,
|
||||
Push,
|
||||
}
|
||||
|
||||
impl Default for DeliveryType {
|
||||
fn default() -> Self {
|
||||
Self::Pull
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromRow, Deserialize, Serialize, Debug)]
|
||||
#[allow(unused)]
|
||||
pub struct PushConfig {
|
||||
#[serde(deserialize_with = "empty_string_as_none")]
|
||||
route_path: Option<String>,
|
||||
#[serde(deserialize_with = "empty_string_as_none")]
|
||||
audience: Option<String>,
|
||||
authenticate: bool,
|
||||
base_endpoint: String,
|
||||
}
|
||||
#[derive(Default, Debug, Serialize, Deserialize)]
|
||||
#[allow(unused)]
|
||||
pub struct CreateUpdateConfig {
|
||||
pub delivery_type: DeliveryType,
|
||||
#[serde(default, deserialize_with = "empty_string_as_none")]
|
||||
pub subscription_id: Option<String>,
|
||||
pub delivery_config: Option<SqlxJson<PushConfig>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ExistingGcpSubscription {
|
||||
pub subscription_id: String,
|
||||
pub base_endpoint: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "subscription_mode", rename_all = "snake_case")]
|
||||
pub enum SubscriptionMode {
|
||||
Existing(ExistingGcpSubscription),
|
||||
CreateUpdate(CreateUpdateConfig),
|
||||
}
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
}
|
||||
|
||||
pub fn start_consuming_gcp_pubsub_event(
|
||||
_db: DB,
|
||||
mut _killpill_rx: tokio::sync::broadcast::Receiver<()>,
|
||||
) -> () {
|
||||
// implementation is not open source
|
||||
}
|
||||
|
||||
pub async fn manage_google_subscription(
|
||||
_authed: ApiAuthed,
|
||||
_db: &DB,
|
||||
_workspace_id: &str,
|
||||
_gcp_resource_path: &str,
|
||||
_path: &str,
|
||||
_topic_id: &str,
|
||||
_subscription_mode: SubscriptionMode,
|
||||
) -> WindmillResult<CreateUpdateConfig> {
|
||||
Ok(CreateUpdateConfig::default())
|
||||
}
|
||||
|
||||
pub async fn process_google_push_request(
|
||||
_headers: HeaderMap,
|
||||
_request: Request,
|
||||
) -> Result<
|
||||
(
|
||||
HashMap<String, Box<RawValue>>,
|
||||
Option<HashMap<String, Box<RawValue>>>,
|
||||
),
|
||||
WindmillError,
|
||||
> {
|
||||
Ok((HashMap::new(), None))
|
||||
}
|
||||
|
||||
pub async fn validate_jwt_token(
|
||||
_db: &DB,
|
||||
_user_db: UserDB,
|
||||
_authed: ApiAuthed,
|
||||
_headers: &HeaderMap,
|
||||
_gcp_resource_path: &str,
|
||||
_workspace_id: &str,
|
||||
_delivery_config: &PushConfig,
|
||||
) -> Result<(), windmill_common::error::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn gcp_push_route_handler() -> Router {
|
||||
Router::new()
|
||||
}
|
||||
|
||||
#[derive(FromRow, Deserialize, Serialize, Debug)]
|
||||
pub struct GcpTrigger {
|
||||
pub gcp_resource_path: String,
|
||||
pub subscription_id: String,
|
||||
pub delivery_type: DeliveryType,
|
||||
pub delivery_config: Option<SqlxJson<PushConfig>>,
|
||||
pub topic_id: String,
|
||||
pub path: String,
|
||||
pub script_path: String,
|
||||
pub is_flow: bool,
|
||||
pub workspace_id: String,
|
||||
pub edited_by: String,
|
||||
pub email: String,
|
||||
pub edited_at: chrono::DateTime<chrono::Utc>,
|
||||
pub extra_perms: Option<serde_json::Value>,
|
||||
pub error: Option<String>,
|
||||
pub server_id: Option<String>,
|
||||
pub last_server_ping: Option<chrono::DateTime<chrono::Utc>>,
|
||||
pub enabled: bool,
|
||||
}
|
||||
@@ -618,7 +618,9 @@ impl AuthenticationMethod {
|
||||
api_key_header,
|
||||
api_key_secret,
|
||||
}) => {
|
||||
let api_key_to_cmp = headers.try_get_webhook_header(&api_key_header)?;
|
||||
let api_key_to_cmp = headers
|
||||
.try_get_webhook_header(&api_key_header)
|
||||
.map_err(|_| AuthenticationError::InvalidApiKey)?;
|
||||
if api_key_to_cmp != api_key_secret {
|
||||
return Err(AuthenticationError::InvalidApiKey);
|
||||
}
|
||||
@@ -734,7 +736,9 @@ impl IntoResponse for AuthenticationError {
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
AuthenticationError::InvalidApiKey => (StatusCode::FORBIDDEN, self.to_string()),
|
||||
AuthenticationError::InvalidApiKey => {
|
||||
return (StatusCode::UNAUTHORIZED, "Unauthorized").into_response()
|
||||
}
|
||||
};
|
||||
|
||||
let body = json!({ "error": error_message });
|
||||
|
||||
@@ -953,7 +953,7 @@ async fn route_job(
|
||||
|
||||
let args = try_from_request_body(
|
||||
request,
|
||||
&db,
|
||||
&(),
|
||||
Some(match trigger.authentication_method {
|
||||
AuthenticationMethod::CustomScript | AuthenticationMethod::Signature => true,
|
||||
_ => trigger.raw_string,
|
||||
|
||||
@@ -89,6 +89,8 @@ mod postgres_triggers;
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
mod apps_ee;
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
mod gcp_triggers_ee;
|
||||
#[cfg(feature = "enterprise")]
|
||||
mod git_sync_ee;
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -117,6 +119,7 @@ mod slack_approvals;
|
||||
mod smtp_server_ee;
|
||||
#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))]
|
||||
mod sqs_triggers_ee;
|
||||
|
||||
mod static_assets;
|
||||
#[cfg(all(feature = "stripe", feature = "enterprise"))]
|
||||
mod stripe_ee;
|
||||
@@ -347,6 +350,18 @@ pub async fn run_server(
|
||||
}
|
||||
};
|
||||
|
||||
let gcp_triggers_service = {
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
{
|
||||
gcp_triggers_ee::workspaced_service()
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "enterprise", feature = "gcp_trigger")))]
|
||||
{
|
||||
Router::new()
|
||||
}
|
||||
};
|
||||
|
||||
let sqs_triggers_service = {
|
||||
#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))]
|
||||
{
|
||||
@@ -425,6 +440,12 @@ pub async fn run_server(
|
||||
let sqs_killpill_rx = killpill_rx.resubscribe();
|
||||
sqs_triggers_ee::start_sqs(db.clone(), sqs_killpill_rx);
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
{
|
||||
let gcp_killpill_rx = killpill_rx.resubscribe();
|
||||
gcp_triggers_ee::start_consuming_gcp_pubsub_event(db.clone(), gcp_killpill_rx);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
@@ -488,6 +509,7 @@ pub async fn run_server(
|
||||
.nest("/nats_triggers", nats_triggers_service)
|
||||
.nest("/mqtt_triggers", mqtt_triggers_service)
|
||||
.nest("/sqs_triggers", sqs_triggers_service)
|
||||
.nest("/gcp_triggers", gcp_triggers_service)
|
||||
.nest("/postgres_triggers", postgres_triggers_service),
|
||||
)
|
||||
.nest("/workspaces", workspaces::global_service())
|
||||
@@ -638,6 +660,20 @@ pub async fn run_server(
|
||||
}
|
||||
.layer(from_extractor::<OptAuthed>()),
|
||||
)
|
||||
.nest(
|
||||
"/gcp/w/:workspace_id",
|
||||
{
|
||||
#[cfg(feature = "gcp_trigger")]
|
||||
{
|
||||
gcp_triggers_ee::gcp_push_route_handler()
|
||||
}
|
||||
#[cfg(not(feature = "gcp_trigger"))]
|
||||
{
|
||||
Router::new()
|
||||
}
|
||||
}
|
||||
.layer(from_extractor::<OptAuthed>()),
|
||||
)
|
||||
.route("/version", get(git_v))
|
||||
.route("/uptodate", get(is_up_to_date))
|
||||
.route("/ee_license", get(ee_license))
|
||||
|
||||
@@ -599,11 +599,10 @@ pub async fn transform_json_value<'c>(
|
||||
}
|
||||
Value::Object(mut m) => {
|
||||
for (a, b) in m.clone().into_iter() {
|
||||
m.insert(
|
||||
a.clone(),
|
||||
let v =
|
||||
transform_json_value(authed, user_db.clone(), db, workspace, b, job_id, token)
|
||||
.await?,
|
||||
);
|
||||
.await?;
|
||||
m.insert(a.clone(), v);
|
||||
}
|
||||
Ok(Value::Object(m))
|
||||
}
|
||||
@@ -1212,7 +1211,10 @@ async fn update_resource_type(
|
||||
feature = "http_trigger",
|
||||
feature = "postgres_trigger",
|
||||
feature = "mqtt_trigger",
|
||||
all(feature = "sqs_trigger", feature = "enterprise")
|
||||
all(
|
||||
feature = "enterprise",
|
||||
any(feature = "sqs_trigger", feature = "gcp_trigger")
|
||||
)
|
||||
))]
|
||||
pub async fn try_get_resource_from_db_as<T>(
|
||||
authed: ApiAuthed,
|
||||
|
||||
@@ -1307,6 +1307,7 @@ struct UsedTriggers {
|
||||
pub postgres_used: bool,
|
||||
pub mqtt_used: bool,
|
||||
pub sqs_used: bool,
|
||||
pub gcp_used: bool
|
||||
}
|
||||
|
||||
async fn get_used_triggers(
|
||||
@@ -1325,7 +1326,8 @@ async fn get_used_triggers(
|
||||
EXISTS(SELECT 1 FROM nats_trigger WHERE workspace_id = $1) as "nats_used!",
|
||||
EXISTS(SELECT 1 FROM postgres_trigger WHERE workspace_id = $1) AS "postgres_used!",
|
||||
EXISTS(SELECT 1 FROM mqtt_trigger WHERE workspace_id = $1) AS "mqtt_used!",
|
||||
EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS "sqs_used!"
|
||||
EXISTS(SELECT 1 FROM sqs_trigger WHERE workspace_id = $1) AS "sqs_used!",
|
||||
EXISTS(SELECT 1 FROM gcp_trigger WHERE workspace_id = $1) AS "gcp_used!"
|
||||
"#,
|
||||
w_id
|
||||
)
|
||||
|
||||
@@ -637,6 +637,47 @@ pub(crate) async fn tarball_workspace(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
|
||||
{
|
||||
let gcp_triggers = sqlx::query_as!(
|
||||
crate::gcp_triggers_ee::GcpTrigger,
|
||||
r#"
|
||||
SELECT
|
||||
gcp_resource_path,
|
||||
subscription_id,
|
||||
topic_id,
|
||||
workspace_id,
|
||||
delivery_type AS "delivery_type: _",
|
||||
delivery_config AS "delivery_config: _",
|
||||
path,
|
||||
script_path,
|
||||
is_flow,
|
||||
edited_by,
|
||||
email,
|
||||
edited_at,
|
||||
server_id,
|
||||
last_server_ping,
|
||||
extra_perms,
|
||||
error,
|
||||
enabled
|
||||
FROM
|
||||
gcp_trigger
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
"#,
|
||||
&w_id
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
|
||||
for trigger in gcp_triggers {
|
||||
let trigger_str = &to_string_without_metadata(&trigger, false, None).unwrap();
|
||||
archive
|
||||
.write_to_archive(&trigger_str, &format!("{}.gcp_trigger.json", trigger.path))
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "enterprise", feature = "nats"))]
|
||||
{
|
||||
let nats_triggers = sqlx::query_as!(
|
||||
|
||||
@@ -22,6 +22,8 @@ pub type JsonResult<T> = std::result::Result<Json<T>, Error>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Bad gateway: {0}")]
|
||||
BadGateway(String),
|
||||
#[error("Bad config: {0}")]
|
||||
BadConfig(String),
|
||||
#[error("Connecting to database: {0}")]
|
||||
@@ -76,6 +78,8 @@ pub enum Error {
|
||||
FindPythonError(String),
|
||||
#[error("Problem with arguments: {0}")]
|
||||
ArgumentErr(String),
|
||||
#[error("{1}")]
|
||||
Generic(StatusCode, String),
|
||||
}
|
||||
|
||||
fn prettify_location(location: &'static Location<'static>) -> String {
|
||||
@@ -181,6 +185,8 @@ impl IntoResponse for Error {
|
||||
| Self::BadRequest(_)
|
||||
| Self::AIError(_)
|
||||
| Self::QuotaExceeded(_) => axum::http::StatusCode::BAD_REQUEST,
|
||||
Self::BadGateway(_) => axum::http::StatusCode::BAD_GATEWAY,
|
||||
Self::Generic(status_code, _) => status_code,
|
||||
_ => axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
};
|
||||
|
||||
|
||||
@@ -418,16 +418,6 @@ pub async fn report_critical_error(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty_string_as_none<'de, D>(
|
||||
deserializer: D,
|
||||
) -> std::result::Result<Option<String>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let option = <Option<String> as serde::Deserialize>::deserialize(deserializer)?;
|
||||
Ok(option.filter(|s| !s.is_empty()))
|
||||
}
|
||||
|
||||
pub async fn report_recovered_critical_error(
|
||||
message: String,
|
||||
_db: DB,
|
||||
@@ -478,6 +468,16 @@ pub async fn report_recovered_critical_error(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty_string_as_none<'de, D>(
|
||||
deserializer: D,
|
||||
) -> std::result::Result<Option<String>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let option = <Option<String> as serde::Deserialize>::deserialize(deserializer)?;
|
||||
Ok(option.filter(|s| !s.is_empty()))
|
||||
}
|
||||
|
||||
pub async fn fetch_mute_workspace(_db: &DB, workspace_id: &str) -> Result<bool> {
|
||||
match sqlx::query!(
|
||||
"SELECT mute_critical_alerts FROM workspace_settings WHERE workspace_id = $1",
|
||||
|
||||
@@ -1952,6 +1952,7 @@ pub enum TriggerKind {
|
||||
Mqtt,
|
||||
Sqs,
|
||||
Postgres,
|
||||
Gcp
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone)]
|
||||
@@ -1968,6 +1969,7 @@ pub enum JobTriggerKind {
|
||||
Sqs,
|
||||
Postgres,
|
||||
Schedule,
|
||||
Gcp
|
||||
}
|
||||
|
||||
impl fmt::Display for TriggerKind {
|
||||
@@ -1982,6 +1984,7 @@ impl fmt::Display for TriggerKind {
|
||||
TriggerKind::Mqtt => "mqtt",
|
||||
TriggerKind::Sqs => "sqs",
|
||||
TriggerKind::Postgres => "postgres",
|
||||
TriggerKind::Gcp => "gcp",
|
||||
};
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ use windmill_common::{
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use windmill_queue::MiniPulledJob;
|
||||
|
||||
use std::ops::AsyncFn;
|
||||
use std::path::Path;
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
|
||||
|
||||
@@ -8,9 +8,11 @@ use tiberius::{AuthMethod, Client, ColumnData, Config, FromSqlOwned, Query, Row,
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::compat::TokioAsyncWriteCompatExt;
|
||||
use uuid::Uuid;
|
||||
use windmill_common::error::to_anyhow;
|
||||
use windmill_common::error::{self, Error};
|
||||
use windmill_common::worker::{to_raw_value, Connection};
|
||||
use windmill_common::{
|
||||
error::{self, to_anyhow, Error},
|
||||
utils::empty_string_as_none,
|
||||
worker::{to_raw_value, Connection},
|
||||
};
|
||||
use windmill_parser_sql::{parse_db_resource, parse_mssql_sig};
|
||||
use windmill_queue::MiniPulledJob;
|
||||
use windmill_queue::{append_logs, CanceledBy};
|
||||
@@ -379,14 +381,6 @@ fn sql_to_json_value(val: ColumnData) -> Result<Value, Error> {
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_string_as_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let option = <Option<String> as serde::Deserialize>::deserialize(deserializer)?;
|
||||
Ok(option.filter(|s| !s.is_empty()))
|
||||
}
|
||||
|
||||
fn deserialize_aad_token<'de, D>(deserializer: D) -> Result<Option<AadToken>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
|
||||
@@ -54,7 +54,7 @@ export const OpenAPI: OpenAPIConfig = {
|
||||
PASSWORD: undefined,
|
||||
TOKEN: getEnv("WM_TOKEN"),
|
||||
USERNAME: undefined,
|
||||
VERSION: '1.479.2',
|
||||
VERSION: '1.481.0',
|
||||
WITH_CREDENTIALS: true,
|
||||
interceptors: {
|
||||
request: new Interceptors(),
|
||||
|
||||
+222
-1
File diff suppressed because one or more lines are too long
+227
-6
@@ -1,6 +1,6 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
export type AIProvider = 'openai' | 'anthropic' | 'mistral' | 'deepseek' | 'googleai' | 'groq' | 'openrouter' | 'togetherai' | 'customai';
|
||||
export type AIProvider = 'openai' | 'azure_openai' | 'anthropic' | 'mistral' | 'deepseek' | 'googleai' | 'groq' | 'openrouter' | 'togetherai' | 'customai';
|
||||
|
||||
export type AIProviderModel = {
|
||||
model: string;
|
||||
@@ -659,6 +659,7 @@ export type TriggersCount = {
|
||||
kafka_count?: number;
|
||||
nats_count?: number;
|
||||
mqtt_count?: number;
|
||||
gcp_count?: number;
|
||||
sqs_count?: number;
|
||||
};
|
||||
|
||||
@@ -774,6 +775,80 @@ export type EditMqttTrigger = {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export type DeliveryType = 'push' | 'pull';
|
||||
|
||||
export type PushConfig = {
|
||||
audience?: string;
|
||||
authenticate: boolean;
|
||||
base_endpoint: string;
|
||||
};
|
||||
|
||||
export type GcpTrigger = TriggerExtraProperty & {
|
||||
gcp_resource_path: string;
|
||||
topic_id: string;
|
||||
subscription_id: string;
|
||||
server_id?: string;
|
||||
delivery_type: DeliveryType;
|
||||
delivery_config?: PushConfig;
|
||||
last_server_ping?: string;
|
||||
error?: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The mode of subscription. 'existing' means using an existing GCP subscription, while 'create_update' involves creating or updating a new subscription.
|
||||
*/
|
||||
export type SubscriptionMode = 'existing' | 'create_update';
|
||||
|
||||
export type GcpExistingSubscription = {
|
||||
subscription_id: string;
|
||||
base_endpoint: string;
|
||||
};
|
||||
|
||||
export type GcpCreateUpdateSubscription = {
|
||||
subscription_id?: string;
|
||||
delivery_type: DeliveryType;
|
||||
delivery_config?: PushConfig;
|
||||
};
|
||||
|
||||
/**
|
||||
* "This is a union type representing the subscription mode.
|
||||
* - 'existing': Represents an existing GCP subscription, and should be accompanied by an 'ExistingGcpSubscription' object.
|
||||
* - 'create_update': Represents a new or updated GCP subscription, and should be accompanied by a 'CreateUpdateConfig' object."
|
||||
*
|
||||
*/
|
||||
export type GcpSubscriptionModeConfig = (GcpExistingSubscription | GcpCreateUpdateSubscription) & {
|
||||
subscription_mode: SubscriptionMode;
|
||||
};
|
||||
|
||||
export type NewGcpTrigger = {
|
||||
gcp_resource_path: string;
|
||||
topic_id: string;
|
||||
subscription_mode: GcpSubscriptionModeConfig;
|
||||
path: string;
|
||||
script_path: string;
|
||||
is_flow: boolean;
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
||||
export type EditGcpTrigger = {
|
||||
gcp_resource_path?: string;
|
||||
topic_id: string;
|
||||
subscription_mode: GcpSubscriptionModeConfig;
|
||||
path: string;
|
||||
script_path: string;
|
||||
is_flow: boolean;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export type GetAllTopicSubscription = {
|
||||
topic_id: string;
|
||||
};
|
||||
|
||||
export type DeleteGcpSubscription = {
|
||||
subscription_id: string;
|
||||
};
|
||||
|
||||
export type SqsTrigger = TriggerExtraProperty & {
|
||||
queue_url: string;
|
||||
aws_resource_path: string;
|
||||
@@ -1384,7 +1459,7 @@ export type CriticalAlert = {
|
||||
workspace_id?: (string) | null;
|
||||
};
|
||||
|
||||
export type CaptureTriggerKind = 'webhook' | 'http' | 'websocket' | 'kafka' | 'email' | 'nats' | 'postgres' | 'sqs' | 'mqtt';
|
||||
export type CaptureTriggerKind = 'webhook' | 'http' | 'websocket' | 'kafka' | 'email' | 'nats' | 'postgres' | 'sqs' | 'mqtt' | 'gcp';
|
||||
|
||||
export type Capture = {
|
||||
trigger_kind: CaptureTriggerKind;
|
||||
@@ -1889,6 +1964,11 @@ export type ParameterSuspended = boolean;
|
||||
*/
|
||||
export type ParameterRunning = boolean;
|
||||
|
||||
/**
|
||||
* allow wildcards (*) in the filter of label, tag, worker
|
||||
*/
|
||||
export type ParameterAllowWildcards = boolean;
|
||||
|
||||
/**
|
||||
* filter on jobs containing those args as a json subset (@> in postgres)
|
||||
*/
|
||||
@@ -2976,6 +3056,7 @@ export type GetUsedTriggersResponse = ({
|
||||
nats_used: boolean;
|
||||
postgres_used: boolean;
|
||||
mqtt_used: boolean;
|
||||
gcp_used: boolean;
|
||||
sqs_used: boolean;
|
||||
});
|
||||
|
||||
@@ -4983,6 +5064,10 @@ export type RunFlowPreviewData = {
|
||||
export type RunFlowPreviewResponse = (string);
|
||||
|
||||
export type ListQueueData = {
|
||||
/**
|
||||
* allow wildcards (*) in the filter of label, tag, worker
|
||||
*/
|
||||
allowWildcards?: boolean;
|
||||
/**
|
||||
* get jobs from all workspaces (only valid if request come from the `admins` workspace)
|
||||
*/
|
||||
@@ -5108,6 +5193,10 @@ export type CountCompletedJobsData = {
|
||||
export type CountCompletedJobsResponse = (number);
|
||||
|
||||
export type ListFilteredUuidsData = {
|
||||
/**
|
||||
* allow wildcards (*) in the filter of label, tag, worker
|
||||
*/
|
||||
allowWildcards?: boolean;
|
||||
/**
|
||||
* get jobs from all workspaces (only valid if request come from the `admins` workspace)
|
||||
*/
|
||||
@@ -5209,6 +5298,10 @@ export type CancelSelectionData = {
|
||||
export type CancelSelectionResponse = (Array<(string)>);
|
||||
|
||||
export type ListCompletedJobsData = {
|
||||
/**
|
||||
* allow wildcards (*) in the filter of label, tag, worker
|
||||
*/
|
||||
allowWildcards?: boolean;
|
||||
/**
|
||||
* filter on jobs containing those args as a json subset (@> in postgres)
|
||||
*/
|
||||
@@ -5303,6 +5396,10 @@ export type ListCompletedJobsData = {
|
||||
export type ListCompletedJobsResponse = (Array<CompletedJob>);
|
||||
|
||||
export type ListJobsData = {
|
||||
/**
|
||||
* allow wildcards (*) in the filter of label, tag, worker
|
||||
*/
|
||||
allowWildcards?: boolean;
|
||||
/**
|
||||
* get jobs from all workspaces (only valid if request come from the `admins` workspace)
|
||||
*/
|
||||
@@ -6366,6 +6463,124 @@ export type TestMqttConnectionData = {
|
||||
|
||||
export type TestMqttConnectionResponse = (string);
|
||||
|
||||
export type CreateGcpTriggerData = {
|
||||
/**
|
||||
* new gcp trigger
|
||||
*/
|
||||
requestBody: NewGcpTrigger;
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type CreateGcpTriggerResponse = (string);
|
||||
|
||||
export type UpdateGcpTriggerData = {
|
||||
path: string;
|
||||
/**
|
||||
* updated trigger
|
||||
*/
|
||||
requestBody: EditGcpTrigger;
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type UpdateGcpTriggerResponse = (string);
|
||||
|
||||
export type DeleteGcpTriggerData = {
|
||||
path: string;
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type DeleteGcpTriggerResponse = (string);
|
||||
|
||||
export type GetGcpTriggerData = {
|
||||
path: string;
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type GetGcpTriggerResponse = (GcpTrigger);
|
||||
|
||||
export type ListGcpTriggersData = {
|
||||
isFlow?: boolean;
|
||||
/**
|
||||
* which page to return (start at 1, default 1)
|
||||
*/
|
||||
page?: number;
|
||||
/**
|
||||
* filter by path
|
||||
*/
|
||||
path?: string;
|
||||
pathStart?: string;
|
||||
/**
|
||||
* number of items to return for a given page (default 30, max 100)
|
||||
*/
|
||||
perPage?: number;
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type ListGcpTriggersResponse = (Array<GcpTrigger>);
|
||||
|
||||
export type ExistsGcpTriggerData = {
|
||||
path: string;
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type ExistsGcpTriggerResponse = (boolean);
|
||||
|
||||
export type SetGcpTriggerEnabledData = {
|
||||
path: string;
|
||||
/**
|
||||
* updated gcp trigger enable
|
||||
*/
|
||||
requestBody: {
|
||||
enabled: boolean;
|
||||
};
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type SetGcpTriggerEnabledResponse = (string);
|
||||
|
||||
export type TestGcpConnectionData = {
|
||||
/**
|
||||
* test gcp connection
|
||||
*/
|
||||
requestBody: {
|
||||
connection: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
};
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type TestGcpConnectionResponse = (string);
|
||||
|
||||
export type DeleteGcpSubscriptionData = {
|
||||
path: string;
|
||||
/**
|
||||
* args to delete subscription from google cloud
|
||||
*/
|
||||
requestBody: DeleteGcpSubscription;
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type DeleteGcpSubscriptionResponse = (string);
|
||||
|
||||
export type ListGoogleTopicsData = {
|
||||
path: string;
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type ListGoogleTopicsResponse = (Array<(string)>);
|
||||
|
||||
export type ListAllTgoogleTopicSubscriptionsData = {
|
||||
path: string;
|
||||
/**
|
||||
* args to get subscription's topic from google cloud
|
||||
*/
|
||||
requestBody: GetAllTopicSubscription;
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type ListAllTgoogleTopicSubscriptionsResponse = (Array<(string)>);
|
||||
|
||||
export type IsValidPostgresConfigurationData = {
|
||||
path: string;
|
||||
workspace: string;
|
||||
@@ -6907,7 +7122,7 @@ export type ListAutoscalingEventsData = {
|
||||
export type ListAutoscalingEventsResponse = (Array<AutoscalingEvent>);
|
||||
|
||||
export type GetGranularAclsData = {
|
||||
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'mqtt_trigger' | 'sqs_trigger';
|
||||
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'mqtt_trigger' | 'gcp_trigger' | 'sqs_trigger';
|
||||
path: string;
|
||||
workspace: string;
|
||||
};
|
||||
@@ -6917,7 +7132,7 @@ export type GetGranularAclsResponse = ({
|
||||
});
|
||||
|
||||
export type AddGranularAclsData = {
|
||||
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'mqtt_trigger' | 'sqs_trigger';
|
||||
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'mqtt_trigger' | 'gcp_trigger' | 'sqs_trigger';
|
||||
path: string;
|
||||
/**
|
||||
* acl to add
|
||||
@@ -6932,7 +7147,7 @@ export type AddGranularAclsData = {
|
||||
export type AddGranularAclsResponse = (string);
|
||||
|
||||
export type RemoveGranularAclsData = {
|
||||
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'mqtt_trigger' | 'sqs_trigger';
|
||||
kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger' | 'mqtt_trigger' | 'gcp_trigger' | 'sqs_trigger';
|
||||
path: string;
|
||||
/**
|
||||
* acl to add
|
||||
@@ -6960,7 +7175,9 @@ export type SetCaptureConfigData = {
|
||||
workspace: string;
|
||||
};
|
||||
|
||||
export type SetCaptureConfigResponse = (unknown);
|
||||
export type SetCaptureConfigResponse = ({
|
||||
[key: string]: unknown;
|
||||
});
|
||||
|
||||
export type PingCaptureConfigData = {
|
||||
path: string;
|
||||
@@ -7432,6 +7649,10 @@ export type GetConcurrencyKeyData = {
|
||||
export type GetConcurrencyKeyResponse = (string);
|
||||
|
||||
export type ListExtendedJobsData = {
|
||||
/**
|
||||
* allow wildcards (*) in the filter of label, tag, worker
|
||||
*/
|
||||
allowWildcards?: boolean;
|
||||
/**
|
||||
* get jobs from all workspaces (only valid if request come from the `admins` workspace)
|
||||
*/
|
||||
|
||||
+11
-2
@@ -659,7 +659,9 @@ export async function elementsToMap(
|
||||
path.endsWith(".nats_trigger" + ext) ||
|
||||
path.endsWith(".postgres_trigger" + ext) ||
|
||||
path.endsWith(".mqtt_trigger" + ext) ||
|
||||
path.endsWith(".sqs_trigger" + ext))
|
||||
path.endsWith(".sqs_trigger" + ext) ||
|
||||
path.endsWith(".gcp_trigger" + ext)
|
||||
)
|
||||
)
|
||||
continue;
|
||||
if (!skips.includeUsers && path.endsWith(".user" + ext)) continue;
|
||||
@@ -921,7 +923,8 @@ function getOrderFromPath(p: string) {
|
||||
typ == "nats_trigger" ||
|
||||
typ == "postgres_trigger" ||
|
||||
typ == "mqtt_trigger" ||
|
||||
typ == "sqs_trigger"
|
||||
typ == "sqs_trigger" ||
|
||||
typ == "gcp_trigger"
|
||||
) {
|
||||
return 8;
|
||||
} else if (typ == "variable") {
|
||||
@@ -1787,6 +1790,12 @@ export async function push(opts: GlobalOptions & SyncOptions) {
|
||||
path: removeSuffix(target, ".sqs_trigger.json"),
|
||||
});
|
||||
break;
|
||||
case "gcp_trigger":
|
||||
await wmill.deleteGcpTrigger({
|
||||
workspace: workspaceId,
|
||||
path: removeSuffix(target, ".gcp_trigger.json"),
|
||||
});
|
||||
break;
|
||||
case "variable":
|
||||
await wmill.deleteVariable({
|
||||
workspace: workspaceId,
|
||||
|
||||
+10
-2
@@ -1,5 +1,6 @@
|
||||
import * as wmill from "./gen/services.gen.ts";
|
||||
import {
|
||||
GcpTrigger,
|
||||
HttpTrigger,
|
||||
KafkaTrigger,
|
||||
MqttTrigger,
|
||||
@@ -27,6 +28,7 @@ type Trigger = {
|
||||
postgres: PostgresTrigger;
|
||||
mqtt: MqttTrigger;
|
||||
sqs: SqsTrigger;
|
||||
gcp: GcpTrigger
|
||||
};
|
||||
|
||||
type TriggerFile<K extends TriggerType> = Omit<
|
||||
@@ -60,6 +62,7 @@ async function getTrigger<K extends TriggerType>(
|
||||
postgres: wmill.getPostgresTrigger,
|
||||
mqtt: wmill.getMqttTrigger,
|
||||
sqs: wmill.getSqsTrigger,
|
||||
gcp: wmill.getGcpTrigger
|
||||
};
|
||||
const triggerFunction = triggerFunctions[triggerType];
|
||||
|
||||
@@ -87,6 +90,7 @@ async function updateTrigger<K extends TriggerType>(
|
||||
postgres: wmill.updatePostgresTrigger,
|
||||
mqtt: wmill.updateMqttTrigger,
|
||||
sqs: wmill.updateSqsTrigger,
|
||||
gcp: wmill.updateGcpTrigger
|
||||
};
|
||||
const triggerFunction = triggerFunctions[triggerType];
|
||||
await triggerFunction({ workspace, path, requestBody: trigger });
|
||||
@@ -112,6 +116,7 @@ async function createTrigger<K extends TriggerType>(
|
||||
postgres: wmill.createPostgresTrigger,
|
||||
mqtt: wmill.createMqttTrigger,
|
||||
sqs: wmill.createSqsTrigger,
|
||||
gcp: wmill.createGcpTrigger
|
||||
};
|
||||
const triggerFunction = triggerFunctions[triggerType];
|
||||
await triggerFunction({ workspace, path, requestBody: trigger });
|
||||
@@ -191,7 +196,9 @@ async function list(opts: GlobalOptions) {
|
||||
const sqsTriggers = await wmill.listSqsTriggers({
|
||||
workspace: workspace.workspaceId,
|
||||
});
|
||||
|
||||
const gcpTriggers = await wmill.listGcpTriggers({
|
||||
workspace: workspace.workspaceId
|
||||
})
|
||||
const triggers = [
|
||||
...httpTriggers.map((x) => ({ path: x.path, kind: "http" })),
|
||||
...websocketTriggers.map((x) => ({ path: x.path, kind: "websocket" })),
|
||||
@@ -200,6 +207,7 @@ async function list(opts: GlobalOptions) {
|
||||
...postgresTriggers.map((x) => ({ path: x.path, kind: "postgres" })),
|
||||
...mqttTriggers.map((x) => ({ path: x.path, kind: "mqtt" })),
|
||||
...sqsTriggers.map((x) => ({ path: x.path, kind: "sqs" })),
|
||||
...gcpTriggers.map((x) => ({ path: x.path, kind: "gcp" }))
|
||||
];
|
||||
|
||||
new Table()
|
||||
@@ -213,7 +221,7 @@ async function list(opts: GlobalOptions) {
|
||||
function checkIfValidTrigger(kind: string | undefined): kind is TriggerType {
|
||||
if (
|
||||
kind &&
|
||||
["http", "websocket", "kafka", "nats", "postgres", "mqtt", "sqs"].includes(kind)
|
||||
["http", "websocket", "kafka", "nats", "postgres", "mqtt", "sqs", "gcp"].includes(kind)
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -154,6 +154,8 @@ export async function pushObj(
|
||||
await pushTrigger("mqtt", workspace, p, befObj, newObj);
|
||||
} else if (typeEnding === "sqs_trigger") {
|
||||
await pushTrigger("sqs", workspace, p, befObj, newObj);
|
||||
} else if (typeEnding === "gcp_trigger") {
|
||||
await pushTrigger("gcp", workspace, p, befObj, newObj);
|
||||
} else if (typeEnding === "user") {
|
||||
await pushWorkspaceUser(workspace, p, befObj, newObj);
|
||||
} else if (typeEnding === "group") {
|
||||
@@ -203,6 +205,7 @@ export function getTypeStrFromPath(
|
||||
| "postgres_trigger"
|
||||
| "mqtt_trigger"
|
||||
| "sqs_trigger"
|
||||
| "gcp_trigger"
|
||||
| "user"
|
||||
| "group"
|
||||
| "settings"
|
||||
@@ -253,6 +256,7 @@ export function getTypeStrFromPath(
|
||||
typeEnding === "postgres_trigger" ||
|
||||
typeEnding === "mqtt_trigger" ||
|
||||
typeEnding === "sqs_trigger" ||
|
||||
typeEnding === "gcp_trigger" ||
|
||||
typeEnding === "user" ||
|
||||
typeEnding === "group" ||
|
||||
typeEnding === "settings" ||
|
||||
|
||||
@@ -230,7 +230,8 @@
|
||||
'routes',
|
||||
'schedules',
|
||||
'sqs',
|
||||
'websockets'
|
||||
'websockets',
|
||||
'gcp'
|
||||
]
|
||||
if (
|
||||
additionalInformation?.triggers &&
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
PostgresTriggerService,
|
||||
NatsTriggerService,
|
||||
MqttTriggerService,
|
||||
SqsTriggerService
|
||||
SqsTriggerService,
|
||||
GcpTriggerService
|
||||
|
||||
} from '$lib/gen'
|
||||
import { superadmin, userStore, workspaceStore } from '$lib/stores'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
@@ -49,6 +51,7 @@
|
||||
| 'nats_trigger'
|
||||
| 'mqtt_trigger'
|
||||
| 'sqs_trigger'
|
||||
| 'gcp_trigger'
|
||||
let meta: Meta | undefined = undefined
|
||||
export let fullNamePlaceholder: string | undefined = undefined
|
||||
export let namePlaceholder = ''
|
||||
@@ -262,6 +265,11 @@
|
||||
workspace: $workspaceStore!,
|
||||
path: path
|
||||
})
|
||||
} else if (kind === 'gcp_trigger') {
|
||||
return await GcpTriggerService.existsGcpTrigger({
|
||||
workspace: $workspaceStore!,
|
||||
path: path
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
| 'mqtt_trigger'
|
||||
| 'sqs_trigger'
|
||||
| 'postgres_trigger'
|
||||
| 'gcp_trigger'
|
||||
let kind: Kind
|
||||
|
||||
let path: string = ''
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
| 'kafka'
|
||||
| 'mqtt'
|
||||
| 'sqs'
|
||||
| 'gcp'
|
||||
| 'nats' = 'webhooks'
|
||||
export let flow_json: any | undefined = undefined
|
||||
export let simplfiedPoll: boolean = false
|
||||
@@ -61,6 +62,7 @@
|
||||
<slot slot="nats" name="nats" />
|
||||
<slot slot="mqtt" name="mqtt" />
|
||||
<slot slot="sqs" name="sqs" />
|
||||
<slot slot="gcp" name="gcp" />
|
||||
<slot slot="emails" name="emails" />
|
||||
<slot slot="schedules" name="schedules" />
|
||||
<slot slot="cli" name="cli" />
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
| 'nats'
|
||||
| 'mqtt'
|
||||
| 'sqs'
|
||||
| 'gcp'
|
||||
>('webhooks')
|
||||
|
||||
const simplifiedPoll = writable(false)
|
||||
@@ -71,6 +72,7 @@
|
||||
<slot slot="nats" name="nats" />
|
||||
<slot slot="mqtt" name="mqtt" />
|
||||
<slot slot="sqs" name="sqs" />
|
||||
<slot slot="gcp" name="gcp" />
|
||||
<slot slot="emails" name="emails" />
|
||||
<slot slot="schedules" name="schedules" />
|
||||
<slot slot="cli" name="cli" />
|
||||
@@ -120,6 +122,7 @@
|
||||
<slot slot="nats" name="nats" />
|
||||
<slot slot="mqtt" name="mqtt" />
|
||||
<slot slot="sqs" name="sqs" />
|
||||
<slot slot="gcp" name="gcp" />
|
||||
<slot slot="emails" name="emails" />
|
||||
<slot slot="schedules" name="schedules" />
|
||||
<slot slot="cli" name="cli" />
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import ToggleButtonGroup from '../common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '../common/toggleButton-v2/ToggleButton.svelte'
|
||||
import { MqttIcon, NatsIcon, KafkaIcon, AwsIcon } from '../icons'
|
||||
import GoogleCloudIcon from '../icons/GoogleCloudIcon.svelte'
|
||||
|
||||
export let triggerSelected:
|
||||
| 'webhooks'
|
||||
@@ -28,17 +29,19 @@
|
||||
| 'nats'
|
||||
| 'sqs'
|
||||
| 'mqtt'
|
||||
| 'gcp'
|
||||
| 'scheduledPoll' = 'webhooks'
|
||||
export let simplfiedPoll: boolean = false
|
||||
|
||||
export let eventStreamType: 'kafka' | 'nats' | 'sqs' | 'mqtt' = 'kafka'
|
||||
export let eventStreamType: 'kafka' | 'nats' | 'sqs' | 'mqtt' | 'gcp' = 'kafka'
|
||||
|
||||
$: {
|
||||
if (
|
||||
triggerSelected === 'kafka' ||
|
||||
triggerSelected === 'nats' ||
|
||||
triggerSelected === 'sqs' ||
|
||||
triggerSelected === 'mqtt'
|
||||
triggerSelected === 'mqtt' ||
|
||||
triggerSelected === 'gcp'
|
||||
) {
|
||||
eventStreamType = triggerSelected
|
||||
}
|
||||
@@ -80,7 +83,7 @@
|
||||
Postgres
|
||||
</span>
|
||||
</Tab>
|
||||
<Tab value="kafka" otherValues={['nats', 'sqs', 'mqtt']}>
|
||||
<Tab value="kafka" otherValues={['nats', 'sqs', 'mqtt', 'gcp']}>
|
||||
<span class="flex flex-row gap-2 items-center text-xs">
|
||||
<PlugZap size={12} />
|
||||
Event streams
|
||||
@@ -113,13 +116,14 @@
|
||||
<slot name="websockets" />
|
||||
{:else if triggerSelected === 'postgres'}
|
||||
<slot name="postgres" />
|
||||
{:else if triggerSelected === 'kafka' || triggerSelected === 'nats' || triggerSelected === 'sqs' || triggerSelected === 'mqtt'}
|
||||
{:else if triggerSelected === 'kafka' || triggerSelected === 'nats' || triggerSelected === 'sqs' || triggerSelected === 'mqtt' || triggerSelected === 'gcp'}
|
||||
<div class="m-1.5">
|
||||
<ToggleButtonGroup bind:selected={eventStreamType} let:item>
|
||||
<ToggleButton value="kafka" label="Kafka" icon={KafkaIcon} {item} />
|
||||
<ToggleButton value="nats" label="NATS" icon={NatsIcon} {item} />
|
||||
<ToggleButton value="mqtt" label="MQTT" icon={MqttIcon} {item} />
|
||||
<ToggleButton value="sqs" label="SQS" icon={AwsIcon} {item} />
|
||||
<ToggleButton value="gcp" label="GCP Pub/Sub" icon={GoogleCloudIcon} {item} />
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{#if eventStreamType === 'kafka'}
|
||||
@@ -130,6 +134,8 @@
|
||||
<slot name="sqs" />
|
||||
{:else if eventStreamType === 'mqtt'}
|
||||
<slot name="mqtt" />
|
||||
{:else if eventStreamType === 'gcp'}
|
||||
<slot name="gcp" />
|
||||
{/if}
|
||||
{:else if triggerSelected === 'cli'}
|
||||
<slot name="cli" />
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import { FlowService, ScriptService } from '$lib/gen'
|
||||
import { enterpriseLicense, workspaceStore } from '$lib/stores'
|
||||
import { MqttIcon, NatsIcon, KafkaIcon, AwsIcon } from '$lib/components/icons'
|
||||
import GoogleCloudIcon from '$lib/components/icons/GoogleCloudIcon.svelte'
|
||||
|
||||
const { selectedTrigger, triggersCount } = getContext<TriggerContext>('TriggerContext')
|
||||
|
||||
@@ -31,6 +32,7 @@
|
||||
| 'eventStreams'
|
||||
| 'postgres'
|
||||
| 'sqs'
|
||||
| 'gcp'
|
||||
)[] = showOnlyWithCount
|
||||
? ['webhooks', 'schedules', 'routes', 'websockets', 'kafka', 'nats', 'emails']
|
||||
: ['webhooks', 'schedules', 'routes', 'websockets', 'eventStreams', 'emails']
|
||||
@@ -69,6 +71,7 @@
|
||||
nats: { icon: NatsIcon, countKey: 'nats_count' },
|
||||
mqtt: { icon: MqttIcon, countKey: 'mqtt_count' },
|
||||
sqs: { icon: AwsIcon, countKey: 'sqs_count' },
|
||||
gcp: { icon: GoogleCloudIcon, countKey: 'gcp_count' },
|
||||
eventStreams: { icon: PlugZap }
|
||||
}
|
||||
|
||||
@@ -80,7 +83,7 @@
|
||||
|
||||
{#each triggersToDisplay as type}
|
||||
{@const { icon, countKey } = triggerTypeConfig[type]}
|
||||
{#if (!showOnlyWithCount || ((countKey && $triggersCount?.[countKey]) || 0) > 0) && !(type === 'sqs' && !$enterpriseLicense) && !(type === 'kafka' && !$enterpriseLicense) && !(type === 'nats' && !$enterpriseLicense) && !(type === 'mqtt')}
|
||||
{#if (!showOnlyWithCount || ((countKey && $triggersCount?.[countKey]) || 0) > 0) && !(type === 'gcp' && !$enterpriseLicense) && !(type === 'sqs' && !$enterpriseLicense) && !(type === 'kafka' && !$enterpriseLicense) && !(type === 'nats' && !$enterpriseLicense) && !(type === 'mqtt')}
|
||||
<Popover>
|
||||
<svelte:fragment slot="text">{camelCaseToWords(type)}</svelte:fragment>
|
||||
<TriggerButton
|
||||
@@ -94,7 +97,8 @@
|
||||
($selectedTrigger === 'kafka' ||
|
||||
$selectedTrigger === 'nats' ||
|
||||
$selectedTrigger === 'sqs' ||
|
||||
$selectedTrigger === 'mqtt')))}
|
||||
$selectedTrigger === 'mqtt' ||
|
||||
$selectedTrigger === 'gcp')))}
|
||||
>
|
||||
{#if countKey}
|
||||
<TriggerCount count={$triggersCount?.[countKey]} />
|
||||
|
||||
@@ -1,21 +1,33 @@
|
||||
<script lang="ts">
|
||||
export let height = '24px'
|
||||
export let width = '24px'
|
||||
export let height = '20px'
|
||||
export let width = '20px'
|
||||
</script>
|
||||
|
||||
<svg {width} {height} viewBox="0 -25 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<path d="M170.2517,56.8186 L192.5047,34.5656 L193.9877,25.1956 C153.4367,-11.6774 88.9757,-7.4964 52.4207,33.9196 C42.2667,45.4226 34.7337,59.7636 30.7167,74.5726 L38.6867,73.4496 L83.1917,66.1106 L86.6277,62.5966 C106.4247,40.8546 139.8977,37.9296 162.7557,56.4286 L170.2517,56.8186 Z" fill="#EA4335">
|
||||
|
||||
</path>
|
||||
<path d="M224.2048,73.9182 C219.0898,55.0822 208.5888,38.1492 193.9878,25.1962 L162.7558,56.4282 C175.9438,67.2042 183.4568,83.4382 183.1348,100.4652 L183.1348,106.0092 C198.4858,106.0092 210.9318,118.4542 210.9318,133.8052 C210.9318,149.1572 198.4858,161.2902 183.1348,161.2902 L127.4638,161.2902 L121.9978,167.2242 L121.9978,200.5642 L127.4638,205.7952 L183.1348,205.7952 C223.0648,206.1062 255.6868,174.3012 255.9978,134.3712 C256.1858,110.1682 244.2528,87.4782 224.2048,73.9182" fill="#4285F4">
|
||||
|
||||
</path>
|
||||
<path d="M71.8704,205.7957 L127.4634,205.7957 L127.4634,161.2897 L71.8704,161.2897 C67.9094,161.2887 64.0734,160.4377 60.4714,158.7917 L52.5844,161.2117 L30.1754,183.4647 L28.2234,191.0387 C40.7904,200.5277 56.1234,205.8637 71.8704,205.7957" fill="#34A853">
|
||||
|
||||
</path>
|
||||
<path d="M71.8704,61.4255 C31.9394,61.6635 -0.2366,94.2275 0.0014,134.1575 C0.1344,156.4555 10.5484,177.4455 28.2234,191.0385 L60.4714,158.7915 C46.4804,152.4705 40.2634,136.0055 46.5844,122.0155 C52.9044,108.0255 69.3704,101.8085 83.3594,108.1285 C89.5244,110.9135 94.4614,115.8515 97.2464,122.0155 L129.4944,89.7685 C115.7734,71.8315 94.4534,61.3445 71.8704,61.4255" fill="#FBBC05">
|
||||
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
<svg
|
||||
{width}
|
||||
{height}
|
||||
viewBox="0 -25 256 256"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M170.2517,56.8186 L192.5047,34.5656 L193.9877,25.1956 C153.4367,-11.6774 88.9757,-7.4964 52.4207,33.9196 C42.2667,45.4226 34.7337,59.7636 30.7167,74.5726 L38.6867,73.4496 L83.1917,66.1106 L86.6277,62.5966 C106.4247,40.8546 139.8977,37.9296 162.7557,56.4286 L170.2517,56.8186 Z"
|
||||
fill="#B0B0B0"
|
||||
/>
|
||||
<path
|
||||
d="M224.2048,73.9182 C219.0898,55.0822 208.5888,38.1492 193.9878,25.1962 L162.7558,56.4282 C175.9438,67.2042 183.4568,83.4382 183.1348,100.4652 L183.1348,106.0092 C198.4858,106.0092 210.9318,118.4542 210.9318,133.8052 C210.9318,149.1572 198.4858,161.2902 183.1348,161.2902 L127.4638,161.2902 L121.9978,167.2242 L121.9978,200.5642 L127.4638,205.7952 L183.1348,205.7952 C223.0648,206.1062 255.6868,174.3012 255.9978,134.3712 C256.1858,110.1682 244.2528,87.4782 224.2048,73.9182"
|
||||
fill="#D0D0D0"
|
||||
/>
|
||||
<path
|
||||
d="M71.8704,205.7957 L127.4634,205.7957 L127.4634,161.2897 L71.8704,161.2897 C67.9094,161.2887 64.0734,160.4377 60.4714,158.7917 L52.5844,161.2117 L30.1754,183.4647 L28.2234,191.0387 C40.7904,200.5277 56.1234,205.8637 71.8704,205.7957"
|
||||
fill="#E0E0E0"
|
||||
/>
|
||||
<path
|
||||
d="M71.8704,61.4255 C31.9394,61.6635 -0.2366,94.2275 0.0014,134.1575 C0.1344,156.4555 10.5484,177.4455 28.2234,191.0385 L60.4714,158.7915 C46.4804,152.4705 40.2634,136.0055 46.5844,122.0155 C52.9044,108.0255 69.3704,101.8085 83.3594,108.1285 C89.5244,110.9135 94.4614,115.8515 97.2464,122.0155 L129.4944,89.7685 C115.7734,71.8315 94.4534,61.3445 71.8704,61.4255"
|
||||
fill="#FFFFFF"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
@@ -89,6 +89,11 @@
|
||||
id: 'triggers',
|
||||
href: `${base}/sqs_triggers`
|
||||
},
|
||||
{
|
||||
label: 'GCP Pub/Sub triggers',
|
||||
id: 'triggers',
|
||||
href: `${base}/gcp_triggers`
|
||||
},
|
||||
{
|
||||
label: 'MQTT triggers',
|
||||
id: 'triggers',
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
MeltButton
|
||||
} from '$lib/components/meltComponents'
|
||||
import MenuButton from './MenuButton.svelte'
|
||||
import GoogleCloudIcon from '../icons/GoogleCloudIcon.svelte'
|
||||
|
||||
export let numUnacknowledgedCriticalAlerts = 0
|
||||
|
||||
@@ -136,13 +137,20 @@
|
||||
disabled: $userStore?.operator || !$enterpriseLicense,
|
||||
kind: 'sqs'
|
||||
},
|
||||
{
|
||||
label: 'GCP Pub/Sub' + ($enterpriseLicense ? '' : ' (EE)'),
|
||||
href: '/gcp_triggers',
|
||||
icon: GoogleCloudIcon,
|
||||
disabled: $userStore?.operator || !$enterpriseLicense,
|
||||
kind: 'gcp'
|
||||
},
|
||||
{
|
||||
label: 'MQTT',
|
||||
href: '/mqtt_triggers',
|
||||
icon: MqttIcon,
|
||||
disabled: $userStore?.operator,
|
||||
kind: 'mqtt'
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
$: extraTriggerLinks = defaultExtraTriggerLinks.filter((link) => {
|
||||
|
||||
@@ -54,6 +54,7 @@ export type TriggerKind =
|
||||
| 'postgres'
|
||||
| 'mqtt'
|
||||
| 'sqs'
|
||||
| 'gcp'
|
||||
export function captureTriggerKindToTriggerKind(kind: CaptureTriggerKind): TriggerKind {
|
||||
switch (kind) {
|
||||
case 'webhook':
|
||||
@@ -74,6 +75,8 @@ export function captureTriggerKindToTriggerKind(kind: CaptureTriggerKind): Trigg
|
||||
return 'sqs'
|
||||
case 'postgres':
|
||||
return 'postgres'
|
||||
case 'gcp':
|
||||
return 'gcp'
|
||||
default:
|
||||
throw new Error(`Unknown CaptureTriggerKind: ${kind}`)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import AwsIcon from '../icons/AwsIcon.svelte'
|
||||
import DropdownV2 from '$lib/components/DropdownV2.svelte'
|
||||
import MqttIcon from '../icons/MqttIcon.svelte'
|
||||
import GoogleCloudIcon from '../icons/GoogleCloudIcon.svelte'
|
||||
|
||||
export let small = false
|
||||
|
||||
@@ -45,6 +46,12 @@
|
||||
action: () => handleClick('sqs'),
|
||||
disabled: !$enterpriseLicense
|
||||
},
|
||||
{
|
||||
icon: GoogleCloudIcon,
|
||||
displayName: 'GCP Pub/Sub',
|
||||
action: () => handleClick('gcp'),
|
||||
disabled: !$enterpriseLicense
|
||||
},
|
||||
{
|
||||
icon: MqttIcon,
|
||||
displayName: 'MQTT',
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
isFlow: boolean
|
||||
path: string
|
||||
connectionInfo: ConnectionInfo | undefined
|
||||
loading?: boolean
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -70,7 +71,11 @@
|
||||
<div class="flex flex-col gap-1 mb-4">
|
||||
<div class="flex flex-row items-center justify-start gap-1">
|
||||
<PulseButton bind:this={pulseButton} numberOfPulses={1} pulseDuration={1}>
|
||||
<AnimatedButton animate={captureInfo.active} baseRadius="6px" wrapperClasses="ml-[-2px]">
|
||||
<AnimatedButton
|
||||
animate={captureInfo.active || captureInfo.loading}
|
||||
baseRadius="6px"
|
||||
wrapperClasses="ml-[-2px]"
|
||||
>
|
||||
<Button
|
||||
size="xs2"
|
||||
on:click={() => dispatch('captureToggle', {})}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import SchemaPickerRow from '$lib/components/schema/SchemaPickerRow.svelte'
|
||||
import type { Capture } from '$lib/gen'
|
||||
import { AwsIcon, MqttIcon } from '../icons'
|
||||
import GoogleCloudIcon from '../icons/GoogleCloudIcon.svelte'
|
||||
|
||||
export let path: string
|
||||
export let hasPreprocessor = false
|
||||
@@ -169,7 +170,8 @@
|
||||
kafka: KafkaIcon,
|
||||
mqtt: MqttIcon,
|
||||
sqs: AwsIcon,
|
||||
postgres: DatabaseIcon
|
||||
postgres: DatabaseIcon,
|
||||
gcp: GoogleCloudIcon
|
||||
}
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import PostgresEditorConfigSection from './postgres/PostgresEditorConfigSection.svelte'
|
||||
import { invalidRelations } from './postgres/utils'
|
||||
import { DEFAULT_V3_CONFIG, DEFAULT_V5_CONFIG } from './mqtt/constant'
|
||||
import GcpTriggerEditorConfigSection from './gcp/GcpTriggerEditorConfigSection.svelte'
|
||||
|
||||
export let isFlow: boolean
|
||||
export let path: string
|
||||
@@ -28,6 +29,7 @@
|
||||
export let showCapture = false
|
||||
export let data: any = {}
|
||||
export let connectionInfo: ConnectionInfo | undefined = undefined
|
||||
export let loading = false
|
||||
export let args: Record<string, any> = {}
|
||||
export let captureTable: CaptureTable | undefined = undefined
|
||||
|
||||
@@ -37,7 +39,6 @@
|
||||
sendUserToast('Table to track must be set', true)
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
invalidRelations(args.publication.table_to_track, {
|
||||
showError: true,
|
||||
@@ -48,7 +49,8 @@
|
||||
}
|
||||
}
|
||||
try {
|
||||
await CaptureService.setCaptureConfig({
|
||||
loading = true
|
||||
args = await CaptureService.setCaptureConfig({
|
||||
requestBody: {
|
||||
trigger_kind: captureType,
|
||||
path,
|
||||
@@ -57,8 +59,10 @@
|
||||
},
|
||||
workspace: $workspaceStore!
|
||||
})
|
||||
loading = false
|
||||
return true
|
||||
} catch (error) {
|
||||
loading = false
|
||||
sendUserToast(error.body, true)
|
||||
return false
|
||||
}
|
||||
@@ -69,19 +73,20 @@
|
||||
let captureConfigs: {
|
||||
[key: string]: CaptureConfig
|
||||
} = {}
|
||||
|
||||
const STREAMING_CAPTURES = ['mqtt', 'sqs', 'websocket', 'postgres', 'kafka', 'nats', 'gcp']
|
||||
|
||||
async function getCaptureConfigs() {
|
||||
const captureConfigsList = await CaptureService.getCaptureConfigs({
|
||||
workspace: $workspaceStore!,
|
||||
runnableKind: isFlow ? 'flow' : 'script',
|
||||
path
|
||||
})
|
||||
|
||||
captureConfigs = captureConfigsList.reduce((acc, c) => {
|
||||
acc[c.trigger_kind] = c
|
||||
return acc
|
||||
}, {})
|
||||
const streamingCapture = ['postgres', 'websocket', 'kafka', 'sqs', 'mqtt']
|
||||
if (streamingCapture.includes(captureType) && captureActive) {
|
||||
if (isStreamingCapture() && captureActive) {
|
||||
const config = captureConfigs[captureType]
|
||||
if (config && config.error) {
|
||||
const serverEnabled = getServerEnabled(config)
|
||||
@@ -130,6 +135,7 @@
|
||||
subscribe_topics: []
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
args = {}
|
||||
}
|
||||
@@ -153,7 +159,6 @@
|
||||
captureActive = false
|
||||
} else {
|
||||
const configSet = await setConfig()
|
||||
|
||||
if (configSet) {
|
||||
capture()
|
||||
}
|
||||
@@ -162,11 +167,18 @@
|
||||
|
||||
let config: CaptureConfig | undefined
|
||||
$: config = captureConfigs[captureType]
|
||||
const streamingCaptures = ['mqtt', 'sqs', 'websocket', 'postgres', 'kafka', 'nats']
|
||||
let cloudDisabled = streamingCaptures.includes(captureType) && isCloudHosted()
|
||||
|
||||
let cloudDisabled = STREAMING_CAPTURES.includes(captureType) && isCloudHosted()
|
||||
|
||||
function isStreamingCapture() {
|
||||
if (captureType === 'gcp' && args.delivery_type === 'push') {
|
||||
return false
|
||||
}
|
||||
return ['mqtt', 'sqs', 'websocket', 'postgres', 'kafka', 'nats', 'gcp'].includes(captureType)
|
||||
}
|
||||
|
||||
function updateConnectionInfo(config: CaptureConfig | undefined, captureActive: boolean) {
|
||||
if (streamingCaptures.includes(captureType) && config && captureActive) {
|
||||
if (isStreamingCapture() && config && captureActive) {
|
||||
const serverEnabled = getServerEnabled(config)
|
||||
const connected = serverEnabled && !config.error
|
||||
const message = connected
|
||||
@@ -189,7 +201,8 @@
|
||||
canHavePreprocessor,
|
||||
isFlow,
|
||||
path,
|
||||
connectionInfo
|
||||
connectionInfo,
|
||||
loading: loading
|
||||
}
|
||||
|
||||
$: args && (captureActive = false)
|
||||
@@ -348,6 +361,28 @@
|
||||
on:captureToggle={handleCapture}
|
||||
on:testWithArgs
|
||||
/>
|
||||
{:else if captureType === 'gcp'}
|
||||
<GcpTriggerEditorConfigSection
|
||||
can_write={true}
|
||||
headless={true}
|
||||
bind:gcp_resource_path={args.gcp_resource_path}
|
||||
bind:topic_id={args.topic_id}
|
||||
bind:subscription_id={args.subscription_id}
|
||||
bind:cloud_subscription_id={args.subscription_id}
|
||||
bind:create_update_subscription_id={args.subscription_id}
|
||||
bind:delivery_config={args.delivery_config}
|
||||
bind:delivery_type={args.delivery_type}
|
||||
bind:subscription_mode={args.subscription_mode}
|
||||
bind:base_endpoint={args.base_endpoint}
|
||||
{showCapture}
|
||||
{captureInfo}
|
||||
bind:captureTable
|
||||
on:applyArgs
|
||||
on:updateSchema
|
||||
on:addPreprocessor
|
||||
on:captureToggle={handleCapture}
|
||||
on:testWithArgs
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/key}
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
NatsTriggerService,
|
||||
SqsTriggerService,
|
||||
PostgresTriggerService,
|
||||
WebsocketTriggerService
|
||||
WebsocketTriggerService,
|
||||
GcpTriggerService
|
||||
} from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import Button from '../common/button/Button.svelte'
|
||||
|
||||
export let kind: 'websocket' | 'nats' | 'kafka' | 'postgres' | 'sqs' | 'mqtt'
|
||||
export let kind: 'websocket' | 'nats' | 'kafka' | 'postgres' | 'sqs' | 'mqtt' | 'gcp'
|
||||
export let args: Record<string, any>
|
||||
export let noButton = false
|
||||
export let testLoading: boolean = false
|
||||
@@ -23,7 +24,8 @@
|
||||
kafka: 'Kafka broker(s)',
|
||||
sqs: 'SQS',
|
||||
postgres: 'Postgres',
|
||||
mqtt: 'MQTT broker'
|
||||
mqtt: 'MQTT broker',
|
||||
gcp: 'Google Cloud Pub/Sub'
|
||||
}
|
||||
|
||||
let promise: CancelablePromise<any> | null = null
|
||||
@@ -65,6 +67,11 @@
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: args as any
|
||||
})
|
||||
} else if (kind === 'gcp') {
|
||||
promise = GcpTriggerService.testGcpConnection({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: args as any
|
||||
})
|
||||
}
|
||||
await promise
|
||||
sendUserToast(`Successfully connected to ${kindToName[kind]}`)
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
import PostgresTriggersPanel from './postgres/PostgresTriggersPanel.svelte'
|
||||
import ToggleButtonGroup from '../common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '../common/toggleButton-v2/ToggleButton.svelte'
|
||||
import { KafkaIcon, MqttIcon, NatsIcon, AwsIcon } from '../icons'
|
||||
import { KafkaIcon, MqttIcon, NatsIcon, AwsIcon, GoogleCloudIcon } from '../icons'
|
||||
import KafkaTriggersPanel from './kafka/KafkaTriggersPanel.svelte'
|
||||
import NatsTriggersPanel from './nats/NatsTriggersPanel.svelte'
|
||||
import MqttTriggersPanel from './mqtt/MqttTriggersPanel.svelte'
|
||||
import SqsTriggerPanel from './sqs/SqsTriggerPanel.svelte'
|
||||
import GcpTriggerPanel from './gcp/GcpTriggerPanel.svelte'
|
||||
|
||||
export let noEditor: boolean
|
||||
export let newItem = false
|
||||
@@ -32,14 +33,15 @@
|
||||
export let canHavePreprocessor: boolean = false
|
||||
export let hasPreprocessor: boolean = false
|
||||
export let args: Record<string, any> = {}
|
||||
let eventStreamType: 'kafka' | 'nats' | 'sqs' | 'mqtt' = 'kafka'
|
||||
let eventStreamType: 'kafka' | 'nats' | 'sqs' | 'mqtt' | 'gcp' = 'kafka'
|
||||
|
||||
$: {
|
||||
if (
|
||||
$selectedTrigger === 'kafka' ||
|
||||
$selectedTrigger === 'nats' ||
|
||||
$selectedTrigger === 'sqs' ||
|
||||
$selectedTrigger === 'mqtt'
|
||||
$selectedTrigger === 'mqtt' ||
|
||||
$selectedTrigger === 'gcp'
|
||||
) {
|
||||
eventStreamType = $selectedTrigger
|
||||
}
|
||||
@@ -64,7 +66,7 @@
|
||||
<Tab value="postgres" selectedClass="text-primary text-sm font-semibold">Postgres</Tab>
|
||||
<Tab
|
||||
value="kafka"
|
||||
otherValues={['nats', 'sqs', 'mqtt']}
|
||||
otherValues={['nats', 'sqs', 'mqtt', 'gcp']}
|
||||
selectedClass="text-primary text-sm font-semibold"
|
||||
>
|
||||
Event streams
|
||||
@@ -160,13 +162,14 @@
|
||||
isEditor={true}
|
||||
/>
|
||||
</div>
|
||||
{:else if $selectedTrigger === 'kafka' || $selectedTrigger === 'nats' || $selectedTrigger === 'sqs' || $selectedTrigger === 'mqtt'}
|
||||
{:else if $selectedTrigger === 'kafka' || $selectedTrigger === 'nats' || $selectedTrigger === 'sqs' || $selectedTrigger === 'mqtt' || $selectedTrigger === 'gcp'}
|
||||
<div class="p-4 flex flex-col gap-2">
|
||||
<ToggleButtonGroup bind:selected={eventStreamType} let:item>
|
||||
<ToggleButton value="kafka" label="Kafka" icon={KafkaIcon} {item} />
|
||||
<ToggleButton value="nats" label="NATS" icon={NatsIcon} {item} />
|
||||
<ToggleButton value="mqtt" label="MQTT" icon={MqttIcon} {item} />
|
||||
<ToggleButton value="sqs" label="SQS" icon={AwsIcon} {item} />
|
||||
<ToggleButton value="gcp" label="GCP" icon={GoogleCloudIcon} {item} />
|
||||
</ToggleButtonGroup>
|
||||
{#if eventStreamType === 'kafka'}
|
||||
<KafkaTriggersPanel
|
||||
@@ -218,6 +221,19 @@
|
||||
{canHavePreprocessor}
|
||||
{hasPreprocessor}
|
||||
/>
|
||||
{:else if eventStreamType === 'gcp'}
|
||||
<GcpTriggerPanel
|
||||
on:applyArgs
|
||||
on:addPreprocessor
|
||||
on:updateSchema
|
||||
on:testWithArgs
|
||||
{newItem}
|
||||
path={initialPath || fakeInitialPath}
|
||||
{isFlow}
|
||||
isEditor={true}
|
||||
{canHavePreprocessor}
|
||||
{hasPreprocessor}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $selectedTrigger === 'schedules'}
|
||||
|
||||
@@ -26,15 +26,16 @@
|
||||
export let showCapture: boolean = false
|
||||
|
||||
const captureTypeLabels: Record<CaptureTriggerKind, string> = {
|
||||
http: '+ New custom HTTP route',
|
||||
websocket: '+ New WebSocket trigger',
|
||||
http: 'custom HTTP route',
|
||||
websocket: 'WebSocket trigger',
|
||||
webhook: 'Webhook',
|
||||
kafka: '+ New Kafka trigger',
|
||||
kafka: 'Kafka trigger',
|
||||
email: 'Email trigger',
|
||||
nats: '+ New NATS trigger',
|
||||
mqtt: '+ New MQTT trigger',
|
||||
sqs: '+ New SQS trigger',
|
||||
postgres: '+ New Postgres trigger'
|
||||
nats: 'NATS trigger',
|
||||
mqtt: 'MQTT trigger',
|
||||
sqs: 'SQS trigger',
|
||||
postgres: 'Postgres trigger',
|
||||
gcp: 'GCP Pub/Sub trigger'
|
||||
}
|
||||
|
||||
const { captureOn } = getContext<TriggerContext>('TriggerContext')
|
||||
@@ -55,7 +56,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Section label={captureTypeLabels[triggerType]} collapsable={!alwaysOpened} bind:collapsed>
|
||||
<Section
|
||||
label={`+ New ${captureTypeLabels[triggerType]}`}
|
||||
collapsable={!alwaysOpened}
|
||||
bind:collapsed
|
||||
>
|
||||
<svelte:fragment slot="action">
|
||||
{#if !collapsed || alwaysOpened}
|
||||
<div class="flex flex-row grow w-min-0 gap-2 items-center justify-end">
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import MqttEditorConfigSection from './mqtt/MqttEditorConfigSection.svelte'
|
||||
import SqsTriggerEditorConfigSection from './sqs/SqsTriggerEditorConfigSection.svelte'
|
||||
import PostgresEditorConfigSection from './postgres/PostgresEditorConfigSection.svelte'
|
||||
import GcpTriggerEditorConfigSection from './gcp/GcpTriggerEditorConfigSection.svelte'
|
||||
|
||||
export let triggerType: CaptureTriggerKind = 'webhook'
|
||||
export let cloudDisabled: boolean = false
|
||||
@@ -96,5 +97,20 @@
|
||||
can_write={true}
|
||||
showCapture={false}
|
||||
/>
|
||||
{:else if triggerType === 'gcp'}
|
||||
<GcpTriggerEditorConfigSection
|
||||
bind:gcp_resource_path={args.gcp_resource_path}
|
||||
bind:topic_id={args.topic_id}
|
||||
bind:subscription_id={args.subscription_id}
|
||||
bind:delivery_config={args.delivery_config}
|
||||
bind:delivery_type={args.delivery_type}
|
||||
bind:subscription_mode={args.subscription_mode}
|
||||
bind:cloud_subscription_id={args.subscription_id}
|
||||
bind:create_update_subscription_id={args.subscription_id}
|
||||
bind:base_endpoint={args.base_endpoint}
|
||||
headless={true}
|
||||
can_write={true}
|
||||
showCapture={false}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import { tick } from 'svelte'
|
||||
import GcpTriggerEditorInner from './GcpTriggerEditorInner.svelte'
|
||||
|
||||
let open = false
|
||||
export async function openEdit(ePath: string, isFlow: boolean) {
|
||||
open = true
|
||||
await tick()
|
||||
drawer?.openEdit(ePath, isFlow)
|
||||
}
|
||||
|
||||
export async function openNew(
|
||||
is_flow: boolean,
|
||||
initial_script_path?: string,
|
||||
defaultValues?: Record<string, any>
|
||||
) {
|
||||
open = true
|
||||
await tick()
|
||||
drawer?.openNew(is_flow, initial_script_path, defaultValues)
|
||||
}
|
||||
|
||||
let drawer: GcpTriggerEditorInner
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
<GcpTriggerEditorInner on:update bind:this={drawer} />
|
||||
{/if}
|
||||
@@ -0,0 +1,342 @@
|
||||
<script lang="ts">
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import CaptureSection, { type CaptureInfo } from '../CaptureSection.svelte'
|
||||
import CaptureTable from '../CaptureTable.svelte'
|
||||
import Required from '$lib/components/Required.svelte'
|
||||
import ResourcePicker from '$lib/components/ResourcePicker.svelte'
|
||||
import { copyToClipboard, emptyStringTrimmed } from '$lib/utils'
|
||||
import TestTriggerConnection from '../TestTriggerConnection.svelte'
|
||||
import Subsection from '$lib/components/Subsection.svelte'
|
||||
import {
|
||||
GcpTriggerService,
|
||||
type DeliveryType,
|
||||
type PushConfig,
|
||||
type SubscriptionMode
|
||||
} from '$lib/gen'
|
||||
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import ClipboardPanel from '$lib/components/details/ClipboardPanel.svelte'
|
||||
import { base } from '$lib/base'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '$lib/defaults'
|
||||
import DarkModeObserver from '$lib/components/DarkModeObserver.svelte'
|
||||
import Select from '$lib/components/apps/svelte-select/lib/Select.svelte'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
|
||||
import { Button } from '$lib/components/common'
|
||||
import { ClipboardCopy, RefreshCw } from 'lucide-svelte'
|
||||
import Alert from '$lib/components/common/alert/Alert.svelte'
|
||||
|
||||
export let can_write: boolean = false
|
||||
export let headless: boolean = false
|
||||
export let showCapture: boolean = false
|
||||
export let captureTable: CaptureTable | undefined = undefined
|
||||
export let captureInfo: CaptureInfo | undefined = undefined
|
||||
export let isValid: boolean = false
|
||||
export let gcp_resource_path = ''
|
||||
export let subscription_id = ''
|
||||
export let topic_id = ''
|
||||
export let delivery_type: DeliveryType | undefined = 'pull'
|
||||
export let delivery_config: PushConfig | undefined
|
||||
export let subscription_mode: SubscriptionMode = 'create_update'
|
||||
export let base_endpoint: string = getBaseUrl(captureInfo)
|
||||
export let path = captureInfo ? captureInfo.path : ''
|
||||
|
||||
let topic_items: string[] = []
|
||||
let subscription_items: string[] = []
|
||||
let darkMode = false
|
||||
|
||||
const DEFAULT_PUSH_CONFIG: PushConfig = {
|
||||
audience: '',
|
||||
authenticate: false,
|
||||
base_endpoint
|
||||
}
|
||||
|
||||
async function loadAllPubSubTopicsFromProject() {
|
||||
if (!emptyStringTrimmed(gcp_resource_path)) {
|
||||
topic_items = await GcpTriggerService.listGoogleTopics({
|
||||
workspace: $workspaceStore!,
|
||||
path: gcp_resource_path
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAllSubscriptionFromGooglePubSubTopic() {
|
||||
if (!emptyStringTrimmed(gcp_resource_path) && !emptyStringTrimmed(topic_id)) {
|
||||
subscription_items = await GcpTriggerService.listAllTgoogleTopicSubscriptions({
|
||||
workspace: $workspaceStore!,
|
||||
path: gcp_resource_path,
|
||||
requestBody: {
|
||||
topic_id
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export let cloud_subscription_id = ''
|
||||
export let create_update_subscription_id = ''
|
||||
|
||||
$: isValid = !emptyStringTrimmed(gcp_resource_path) && !emptyStringTrimmed(topic_id)
|
||||
$: if (!delivery_type) {
|
||||
delivery_type = 'pull'
|
||||
}
|
||||
$: if (!delivery_config) {
|
||||
delivery_config = DEFAULT_PUSH_CONFIG
|
||||
}
|
||||
|
||||
function getBaseUrl(captureInfo: CaptureInfo | undefined) {
|
||||
if (captureInfo) {
|
||||
return `${location.origin}${base}/api/w/${$workspaceStore}/capture_u/gcp/${
|
||||
captureInfo.isFlow ? 'flow' : 'script'
|
||||
}`
|
||||
} else {
|
||||
return `${window.location.origin}${base}/api/gcp/w/${$workspaceStore!}`
|
||||
}
|
||||
}
|
||||
|
||||
$: !base_endpoint && (base_endpoint = getBaseUrl(captureInfo))
|
||||
</script>
|
||||
|
||||
<DarkModeObserver bind:darkMode />
|
||||
|
||||
<div>
|
||||
{#if showCapture && captureInfo}
|
||||
{@const captureURL = `${base_endpoint}/${path}`}
|
||||
<CaptureSection
|
||||
captureType="gcp"
|
||||
disabled={!isValid}
|
||||
{captureInfo}
|
||||
on:captureToggle
|
||||
on:applyArgs
|
||||
on:updateSchema
|
||||
on:addPreprocessor
|
||||
on:testWithArgs
|
||||
bind:captureTable
|
||||
>
|
||||
{#if delivery_type === 'push'}
|
||||
<Label label="URL">
|
||||
<ClipboardPanel content={captureURL} disabled={!captureInfo.active} />
|
||||
</Label>
|
||||
{/if}
|
||||
</CaptureSection>
|
||||
{/if}
|
||||
<Section label="GCP Pub/Sub" {headless}>
|
||||
<div class="flex flex-col w-full gap-4">
|
||||
<Subsection label="Connection setup">
|
||||
<div class="flex flex-col gap-1 mt-2">
|
||||
<ResourcePicker
|
||||
on:change={() => {
|
||||
loadAllPubSubTopicsFromProject()
|
||||
}}
|
||||
resourceType="gcloud"
|
||||
bind:value={gcp_resource_path}
|
||||
/>
|
||||
{#if !emptyStringTrimmed(gcp_resource_path)}
|
||||
<TestTriggerConnection kind="gcp" args={{ gcp_resource_path }} />
|
||||
{/if}
|
||||
</div>
|
||||
</Subsection>
|
||||
|
||||
<div class="flex flex-col gap-1">
|
||||
<Subsection
|
||||
label="Topic"
|
||||
tooltip="Select the Pub/Sub topic that this subscription will be attached to. Messages published to this topic will be delivered to your subscription."
|
||||
>
|
||||
<div class="flex gap-1 mt-2">
|
||||
<Select
|
||||
class="grow shrink max-w-full"
|
||||
on:change={(e) => {
|
||||
topic_id = e.detail.value
|
||||
loadAllSubscriptionFromGooglePubSubTopic()
|
||||
}}
|
||||
on:clear={() => {
|
||||
topic_id = ''
|
||||
}}
|
||||
value={topic_id}
|
||||
items={topic_items}
|
||||
placeholder="Choose a topic"
|
||||
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
|
||||
containerStyles={darkMode
|
||||
? SELECT_INPUT_DEFAULT_STYLE.containerStylesDark
|
||||
: SELECT_INPUT_DEFAULT_STYLE.containerStyles}
|
||||
portal={false}
|
||||
/>
|
||||
<Button
|
||||
disabled={!can_write}
|
||||
variant="border"
|
||||
color="light"
|
||||
wrapperClasses="self-stretch"
|
||||
on:click={loadAllPubSubTopicsFromProject}
|
||||
startIcon={{ icon: RefreshCw }}
|
||||
iconOnly
|
||||
/>
|
||||
</div>
|
||||
</Subsection>
|
||||
</div>
|
||||
{#if !emptyStringTrimmed(gcp_resource_path) && !emptyStringTrimmed(topic_id)}
|
||||
<Section
|
||||
label="Subscription"
|
||||
tooltip="Choose whether to create or update a Pub/Sub subscription, or link an existing one from your Google Cloud project."
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/gcp_triggers#subscription-setup"
|
||||
>
|
||||
<div class="flex flex-col gap-3">
|
||||
<ToggleButtonGroup
|
||||
bind:selected={subscription_mode}
|
||||
on:selected={(e) => {
|
||||
if (e.detail === 'existing' && subscription_items.length === 0) {
|
||||
loadAllSubscriptionFromGooglePubSubTopic()
|
||||
}
|
||||
}}
|
||||
let:item
|
||||
>
|
||||
<ToggleButton
|
||||
label="Create/Update"
|
||||
value="create_update"
|
||||
tooltip="Create a new subscription or update an existing one with custom settings"
|
||||
showTooltipIcon
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
label="Existing subscription"
|
||||
value="existing"
|
||||
tooltip="Select an existing subscription from GCP Pub/Sub"
|
||||
showTooltipIcon
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
|
||||
{#if subscription_mode === 'create_update'}
|
||||
<Subsection
|
||||
label="Subscription id"
|
||||
tooltip="The unique identifier for the Pub/Sub subscription. Leave it empty to let the system automatically generate a subscription ID, or provide a custom one if needed."
|
||||
>
|
||||
<div class="mt-2">
|
||||
<input
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
placeholder="Enter subscription ID (leave empty to auto-generate)"
|
||||
bind:value={create_update_subscription_id}
|
||||
on:input={(event) => {
|
||||
subscription_id = event?.currentTarget.value
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Subsection>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Subsection
|
||||
label="Delivery type"
|
||||
tooltip="Select the delivery type for the Pub/Sub subscription. If the subscription already exists and you want to keep it as-is, choose the same delivery type as in Google Cloud. You can switch the type here if the API allows it — otherwise, make the change directly in Google Cloud."
|
||||
>
|
||||
<div class="flex flex-col gap-2 mt-2">
|
||||
<ToggleButtonGroup bind:selected={delivery_type} let:item>
|
||||
<ToggleButton
|
||||
label="Pull"
|
||||
tooltip="Create a subscription where your service will pull messages from the queue. Suitable for services that periodically check for new messages."
|
||||
value="pull"
|
||||
showTooltipIcon
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton
|
||||
label="Push"
|
||||
tooltip="Windmill will auto-generate a push endpoint for this subscription. You must not modify this endpoint in Google Cloud, as it is managed internally by Windmill."
|
||||
showTooltipIcon
|
||||
value="push"
|
||||
{item}
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
</Subsection>
|
||||
{#if delivery_type === 'push' && delivery_config}
|
||||
<div class="flex flex-col gap-3 mt-1">
|
||||
<Subsection
|
||||
label="URL"
|
||||
tooltip="The URL of the service that receives push messages."
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
placeholder="URL"
|
||||
disabled
|
||||
value={`${base_endpoint}/${path}`}
|
||||
/>
|
||||
<Button
|
||||
on:click={() => copyToClipboard(`${base_endpoint}/${path}`)}
|
||||
color="dark"
|
||||
size="xs"
|
||||
startIcon={{ icon: ClipboardCopy }}
|
||||
>
|
||||
Copy URL
|
||||
</Button>
|
||||
</div>
|
||||
</Subsection>
|
||||
<Subsection
|
||||
label="Audience"
|
||||
tooltip="Provide the expected audience value for verifying OIDC tokens in push requests. If
|
||||
left empty, the URL of the endpoint will be used as the default audience"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
placeholder="audience"
|
||||
bind:value={delivery_config.audience}
|
||||
disabled={!can_write}
|
||||
/>
|
||||
</Subsection>
|
||||
<Subsection label="Authenticate">
|
||||
<p class="text-xs mb-2 text-tertiary">
|
||||
Enable Google Cloud authentication for push delivery using a verified token.<Required
|
||||
required={true}
|
||||
/>
|
||||
</p>
|
||||
<Toggle bind:checked={delivery_config.authenticate} />
|
||||
</Subsection>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if subscription_mode === 'existing'}
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex gap-1">
|
||||
<Select
|
||||
class="grow shrink max-w-full"
|
||||
on:change={(e) => {
|
||||
subscription_id = e.detail.value
|
||||
cloud_subscription_id = e.detail.value
|
||||
}}
|
||||
on:clear={() => {
|
||||
subscription_id = ''
|
||||
}}
|
||||
value={cloud_subscription_id}
|
||||
items={subscription_items}
|
||||
placeholder="Choose a subscription"
|
||||
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
|
||||
containerStyles={darkMode
|
||||
? SELECT_INPUT_DEFAULT_STYLE.containerStylesDark
|
||||
: SELECT_INPUT_DEFAULT_STYLE.containerStyles}
|
||||
portal={false}
|
||||
/>
|
||||
<Button
|
||||
disabled={!can_write}
|
||||
variant="border"
|
||||
color="light"
|
||||
wrapperClasses="self-stretch"
|
||||
on:click={loadAllSubscriptionFromGooglePubSubTopic}
|
||||
startIcon={{ icon: RefreshCw }}
|
||||
iconOnly
|
||||
/>
|
||||
</div>
|
||||
<Alert title="Push Subscription URL Requirements" type="warning">
|
||||
If the subscription uses <strong>push delivery</strong>, its endpoint URL must
|
||||
match the following format: <strong>{base_endpoint}/*</strong>, meaning it must
|
||||
start with
|
||||
<strong>{base_endpoint}</strong> followed by any path segment.
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Section>
|
||||
{/if}
|
||||
</div>
|
||||
</Section>
|
||||
</div>
|
||||
@@ -0,0 +1,293 @@
|
||||
<script lang="ts">
|
||||
import { Alert, Button } from '$lib/components/common'
|
||||
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
|
||||
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
|
||||
import Path from '$lib/components/Path.svelte'
|
||||
import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores'
|
||||
import { canWrite, emptyString, sendUserToast } from '$lib/utils'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { Loader2, Save } from 'lucide-svelte'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import {
|
||||
GcpTriggerService,
|
||||
type DeliveryType,
|
||||
type PushConfig,
|
||||
type SubscriptionMode
|
||||
} from '$lib/gen'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import ScriptPicker from '$lib/components/ScriptPicker.svelte'
|
||||
import Required from '$lib/components/Required.svelte'
|
||||
import GcpTriggerEditorConfigSection from './GcpTriggerEditorConfigSection.svelte'
|
||||
import { base } from '$app/paths'
|
||||
|
||||
let is_loading = false
|
||||
let drawer: Drawer
|
||||
let is_flow: boolean = false
|
||||
let initialPath = ''
|
||||
let edit = true
|
||||
let delivery_type: DeliveryType = 'pull'
|
||||
let itemKind: 'flow' | 'script' = 'script'
|
||||
let script_path = ''
|
||||
let initialScriptPath = ''
|
||||
let fixedScriptPath = ''
|
||||
let path: string = ''
|
||||
let pathError = ''
|
||||
let enabled = false
|
||||
let dirtyPath = false
|
||||
let can_write = true
|
||||
let drawerLoading = true
|
||||
let topic_id: string = ''
|
||||
let gcp_resource_path: string = ''
|
||||
let subscription_id: string = ''
|
||||
let isValid = false
|
||||
let delivery_config: PushConfig | undefined = undefined
|
||||
let subscription_mode: SubscriptionMode = 'create_update'
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: is_flow = itemKind === 'flow'
|
||||
|
||||
export async function openEdit(ePath: string, isFlow: boolean) {
|
||||
drawerLoading = true
|
||||
try {
|
||||
drawer?.openDrawer()
|
||||
initialPath = ePath
|
||||
itemKind = isFlow ? 'flow' : 'script'
|
||||
edit = true
|
||||
dirtyPath = false
|
||||
await loadTrigger()
|
||||
} catch (err) {
|
||||
sendUserToast(`Could not load GCP Pub/Sub trigger: ${err.body}`, true)
|
||||
} finally {
|
||||
drawerLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
export async function openNew(
|
||||
nis_flow: boolean,
|
||||
fixedScriptPath_?: string,
|
||||
defaultValues?: Record<string, any>
|
||||
) {
|
||||
drawerLoading = true
|
||||
try {
|
||||
drawer?.openDrawer()
|
||||
is_flow = nis_flow
|
||||
itemKind = nis_flow ? 'flow' : 'script'
|
||||
initialScriptPath = ''
|
||||
fixedScriptPath = fixedScriptPath_ ?? ''
|
||||
script_path = fixedScriptPath
|
||||
gcp_resource_path = defaultValues?.gcp_resource_path ?? ''
|
||||
delivery_type = defaultValues?.delivery_type ?? 'pull'
|
||||
delivery_config = defaultValues?.delivery_config ?? undefined
|
||||
subscription_id = ''
|
||||
topic_id = defaultValues?.topic_id
|
||||
subscription_mode = defaultValues?.subscription_mode ?? 'create_update'
|
||||
path = ''
|
||||
initialPath = ''
|
||||
edit = false
|
||||
dirtyPath = false
|
||||
} finally {
|
||||
drawerLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTrigger(): Promise<void> {
|
||||
try {
|
||||
const s = await GcpTriggerService.getGcpTrigger({
|
||||
workspace: $workspaceStore!,
|
||||
path: initialPath
|
||||
})
|
||||
script_path = s.script_path
|
||||
initialScriptPath = s.script_path
|
||||
gcp_resource_path = s.gcp_resource_path
|
||||
delivery_type = s.delivery_type
|
||||
subscription_id = s.subscription_id
|
||||
delivery_config = s.delivery_config
|
||||
is_flow = s.is_flow
|
||||
path = s.path
|
||||
enabled = s.enabled
|
||||
topic_id = s.topic_id
|
||||
can_write = canWrite(s.path, s.extra_perms, $userStore)
|
||||
} catch (error) {
|
||||
sendUserToast(`Could not load GCP Pub/Sub trigger: ${error.body}`, true)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateTrigger(): Promise<void> {
|
||||
try {
|
||||
is_loading = true
|
||||
if (delivery_type === 'push') {
|
||||
if (!delivery_config) {
|
||||
sendUserToast('Must set route path when delivery type is push', true)
|
||||
return
|
||||
}
|
||||
delivery_config.base_endpoint = `${window.location.origin}${base}`
|
||||
} else {
|
||||
delivery_config = undefined
|
||||
}
|
||||
if (edit) {
|
||||
await GcpTriggerService.updateGcpTrigger({
|
||||
workspace: $workspaceStore!,
|
||||
path: initialPath,
|
||||
requestBody: {
|
||||
gcp_resource_path,
|
||||
subscription_mode: {
|
||||
subscription_mode,
|
||||
subscription_id,
|
||||
delivery_type,
|
||||
delivery_config,
|
||||
base_endpoint: `${window.location.origin}${base}`
|
||||
},
|
||||
topic_id,
|
||||
path,
|
||||
script_path,
|
||||
enabled,
|
||||
is_flow
|
||||
}
|
||||
})
|
||||
sendUserToast(`GCP Pub/Sub trigger ${path} updated`)
|
||||
} else {
|
||||
await GcpTriggerService.createGcpTrigger({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
gcp_resource_path,
|
||||
subscription_mode: {
|
||||
subscription_mode,
|
||||
subscription_id,
|
||||
delivery_type,
|
||||
delivery_config: delivery_config,
|
||||
base_endpoint: `${window.location.origin}${base}`
|
||||
},
|
||||
topic_id,
|
||||
path,
|
||||
script_path,
|
||||
enabled: true,
|
||||
is_flow
|
||||
}
|
||||
})
|
||||
sendUserToast(`GCP Pub/Sub trigger ${path} created`)
|
||||
}
|
||||
|
||||
if (!$usedTriggerKinds.includes('gcp')) {
|
||||
$usedTriggerKinds = [...$usedTriggerKinds, 'gcp']
|
||||
}
|
||||
dispatch('update')
|
||||
drawer.closeDrawer()
|
||||
is_loading = false
|
||||
} catch (error) {
|
||||
is_loading = false
|
||||
sendUserToast(error.body, true)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Drawer size="800px" bind:this={drawer}>
|
||||
<DrawerContent
|
||||
title={edit
|
||||
? can_write
|
||||
? `Edit GCP Pub/Sub trigger ${initialPath}`
|
||||
: `GCP Pub/Sub trigger ${initialPath}`
|
||||
: 'New GCP Pub/Sub trigger'}
|
||||
on:close={drawer.closeDrawer}
|
||||
>
|
||||
<svelte:fragment slot="actions">
|
||||
{#if !drawerLoading && can_write}
|
||||
{#if edit}
|
||||
<div class="mr-8 center-center -mt-1">
|
||||
<Toggle
|
||||
disabled={!can_write}
|
||||
checked={enabled}
|
||||
options={{ right: 'enable', left: 'disable' }}
|
||||
on:change={async (e) => {
|
||||
sendUserToast(
|
||||
`${e.detail ? 'enabled' : 'disabled'} GCP Pub/Sub trigger ${initialPath}`
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<Button
|
||||
startIcon={{ icon: Save }}
|
||||
disabled={pathError != '' || emptyString(script_path) || !isValid || !can_write}
|
||||
on:click={updateTrigger}
|
||||
loading={is_loading}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
{#if drawerLoading}
|
||||
<div class="flex flex-col items-center justify-center h-full w-full">
|
||||
<Loader2 size="50" class="animate-spin" />
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col gap-5">
|
||||
<Alert title="Info" type="info">
|
||||
{#if edit}
|
||||
Changes can take up to 30 seconds to take effect.
|
||||
{:else}
|
||||
New GCP Pub/Sub trigger can take up to 30 seconds to start listening.
|
||||
{/if}
|
||||
</Alert>
|
||||
</div>
|
||||
<div class="flex flex-col gap-12 mt-6">
|
||||
<div class="flex flex-col gap-4">
|
||||
<Label label="Path">
|
||||
<Path
|
||||
bind:dirty={dirtyPath}
|
||||
bind:error={pathError}
|
||||
bind:path
|
||||
{initialPath}
|
||||
checkInitialPathExistence={!edit}
|
||||
namePlaceholder="gcp_trigger"
|
||||
kind="gcp_trigger"
|
||||
disabled={!can_write}
|
||||
/>
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<Section label="Runnable">
|
||||
<p class="text-xs mb-1 text-tertiary">
|
||||
Pick a script or flow to be triggered <Required required={true} />
|
||||
</p>
|
||||
<div class="flex flex-row mb-2">
|
||||
<ScriptPicker
|
||||
disabled={fixedScriptPath != '' || !can_write}
|
||||
initialPath={fixedScriptPath || initialScriptPath}
|
||||
kinds={['script']}
|
||||
allowFlow={true}
|
||||
bind:itemKind
|
||||
bind:scriptPath={script_path}
|
||||
allowRefresh
|
||||
/>
|
||||
{#if emptyString(script_path)}
|
||||
<Button
|
||||
btnClasses="ml-4 mt-2"
|
||||
color="dark"
|
||||
size="xs"
|
||||
href={itemKind === 'flow' ? '/flows/add?hub=68' : '/scripts/add?hub=hub%2F14251'}
|
||||
target="_blank">Create from template</Button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
<GcpTriggerEditorConfigSection
|
||||
bind:isValid
|
||||
bind:gcp_resource_path
|
||||
bind:subscription_id
|
||||
bind:delivery_type
|
||||
bind:delivery_config
|
||||
bind:topic_id
|
||||
bind:subscription_mode
|
||||
bind:path={path}
|
||||
cloud_subscription_id={subscription_id}
|
||||
create_update_subscription_id={subscription_id}
|
||||
{can_write}
|
||||
headless={true}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
@@ -0,0 +1,131 @@
|
||||
<script lang="ts">
|
||||
import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
|
||||
import { GcpTriggerService, type GcpTrigger } from '$lib/gen'
|
||||
|
||||
import { canWrite } from '$lib/utils'
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import { Alert } from '$lib/components/common'
|
||||
import Description from '$lib/components/Description.svelte'
|
||||
import type { TriggerContext } from '$lib/components/triggers'
|
||||
import TriggersEditorSection from '../TriggersEditorSection.svelte'
|
||||
import GcpTriggerEditor from './GcpTriggerEditor.svelte'
|
||||
export let isFlow: boolean
|
||||
export let path: string
|
||||
export let newItem: boolean = false
|
||||
export let isEditor: boolean = false
|
||||
export let canHavePreprocessor: boolean = false
|
||||
export let hasPreprocessor: boolean = false
|
||||
|
||||
let gcpTriggerEditor: GcpTriggerEditor
|
||||
let openForm = true
|
||||
let dontCloseOnLoad = false
|
||||
|
||||
$: path && loadTriggers()
|
||||
|
||||
const { triggersCount, selectedTrigger, defaultValues } =
|
||||
getContext<TriggerContext>('TriggerContext')
|
||||
|
||||
onMount(() => {
|
||||
if (
|
||||
defaultValues &&
|
||||
$selectedTrigger === 'gcp' &&
|
||||
Object.keys($defaultValues ?? {}).length > 0
|
||||
) {
|
||||
gcpTriggerEditor.openNew(isFlow, path, $defaultValues)
|
||||
defaultValues.set(undefined)
|
||||
}
|
||||
})
|
||||
|
||||
let gcpTriggers: (GcpTrigger & { canWrite: boolean })[] | undefined = undefined
|
||||
export async function loadTriggers() {
|
||||
try {
|
||||
gcpTriggers = (
|
||||
await GcpTriggerService.listGcpTriggers({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path,
|
||||
isFlow
|
||||
})
|
||||
).map((x) => {
|
||||
return { canWrite: canWrite(x.path, x.extra_perms!, $userStore), ...x }
|
||||
})
|
||||
$triggersCount = { ...($triggersCount ?? {}), gcp_count: gcpTriggers?.length }
|
||||
openForm = gcpTriggers?.length === 0 || dontCloseOnLoad
|
||||
} catch (e) {
|
||||
console.error('impossible to load GCP Pub/Sub triggers', e)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<GcpTriggerEditor
|
||||
on:update={() => {
|
||||
loadTriggers()
|
||||
}}
|
||||
bind:this={gcpTriggerEditor}
|
||||
/>
|
||||
|
||||
{#if !$enterpriseLicense}
|
||||
<Alert title="EE Only" type="warning" size="xs">
|
||||
GCP Pub/Sub triggers are an enterprise only feature.
|
||||
</Alert>
|
||||
{:else if isCloudHosted()}
|
||||
<Alert title="Not compatible with multi-tenant cloud" type="warning" size="xs">
|
||||
GCP Pub/Sub triggers are disabled in the multi-tenant cloud.
|
||||
</Alert>
|
||||
{:else}
|
||||
<div class="flex flex-col gap-4">
|
||||
<Description link="https://cloud.google.com/pubsub/docs?hl=en">
|
||||
GCP Pub/Sub triggers allow your scripts or flows to process messages from Google Cloud
|
||||
Pub/Sub in real time. Each trigger listens to a Pub/Sub subscription and executes a script or
|
||||
a flow when new messages are published to the corresponding topic.
|
||||
</Description>
|
||||
|
||||
{#if !newItem && gcpTriggers && gcpTriggers.length > 0}
|
||||
<Section label="GCP Pub/Sub">
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col divide-y pt-2">
|
||||
{#each gcpTriggers as gcpTrigger (gcpTrigger.path)}
|
||||
<div class="grid grid-cols-5 text-2xs items-center py-2">
|
||||
<div class="col-span-2 truncate">{gcpTrigger.path}</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
on:click={() => gcpTriggerEditor?.openEdit(gcpTrigger.path, isFlow)}
|
||||
class="px-2"
|
||||
>
|
||||
{#if gcpTrigger.canWrite}
|
||||
Edit
|
||||
{:else}
|
||||
View
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
{/if}
|
||||
|
||||
<TriggersEditorSection
|
||||
on:applyArgs
|
||||
on:saveTrigger={(e) => {
|
||||
gcpTriggerEditor?.openNew(isFlow, path, e.detail.config)
|
||||
}}
|
||||
on:addPreprocessor
|
||||
on:updateSchema
|
||||
on:testWithArgs
|
||||
cloudDisabled={false}
|
||||
triggerType="gcp"
|
||||
{isFlow}
|
||||
{path}
|
||||
{isEditor}
|
||||
{canHavePreprocessor}
|
||||
{hasPreprocessor}
|
||||
{newItem}
|
||||
{openForm}
|
||||
bind:showCapture={dontCloseOnLoad}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -4,12 +4,14 @@
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
|
||||
export let raw_string: boolean
|
||||
export let wrap_body: boolean
|
||||
export let wrap_body: boolean
|
||||
</script>
|
||||
|
||||
<Label label="Raw body" class="w-full">
|
||||
<svelte:fragment slot="header">
|
||||
<Tooltip>
|
||||
<Tooltip
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/http_routing#body-processing-options"
|
||||
>
|
||||
Provides the raw JSON payload as a string under the 'raw_string' key. Required for custom
|
||||
script authentication method and useful for signature verification or other advanced use
|
||||
cases.
|
||||
@@ -27,6 +29,7 @@
|
||||
<Label label="Wrap body" class="w-full">
|
||||
<svelte:fragment slot="header">
|
||||
<Tooltip
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/http_routing#body-processing-options"
|
||||
>Wraps the payload in an object under the 'body' key, useful for handling unknown payloads.</Tooltip
|
||||
>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
$: isValid = routeError === ''
|
||||
|
||||
$: fullRoute = getHttpRoute(route_path, workspaced_route, $workspaceStore ?? '')
|
||||
$: fullRoute = getHttpRoute('r', route_path, workspaced_route, $workspaceStore ?? '')
|
||||
|
||||
$: !http_method && (http_method = 'post')
|
||||
$: route_path === undefined && (route_path = '')
|
||||
@@ -203,7 +203,9 @@
|
||||
options={{
|
||||
right: 'Prefix with workspace',
|
||||
rightTooltip:
|
||||
'Prefixes the route with the workspace ID (e.g., {base_url}/api/r/{workspace_id}/{route}). Note: deploying the HTTP trigger to another workspace updates the route workspace prefix accordingly.'
|
||||
'Prefixes the route with the workspace ID (e.g., {base_url}/api/r/{workspace_id}/{route}). Note: deploying the HTTP trigger to another workspace updates the route workspace prefix accordingly.',
|
||||
rightDocumentationLink:
|
||||
'https://www.windmill.dev/docs/core_concepts/http_routing#workspace-prefix'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7,11 +7,12 @@ export const SIGNATURE_TEMPLATE_SCRIPT_HUB_PATH: string = `hub/${HUB_SCRIPT_ID}`
|
||||
export const SIGNATURE_TEMPLATE_FLOW_HUB_ID = '67'
|
||||
|
||||
export function getHttpRoute(
|
||||
route_prefix: string,
|
||||
route_path: string | undefined,
|
||||
workspaced_route: boolean,
|
||||
workspace_id: string
|
||||
workspace_id: string,
|
||||
) {
|
||||
return `${location.origin}${base}/api/r/${
|
||||
return `${location.origin}${base}/api/${route_prefix}/${
|
||||
isCloudHosted() || workspaced_route ? workspace_id + '/' : ''
|
||||
}${route_path ?? ''}`
|
||||
}
|
||||
|
||||
@@ -635,7 +635,9 @@ export const TS_PREPROCESSOR_SCRIPT_INTRO = `/**
|
||||
* The structure of \`wm_trigger\` and the main trigger arguments are specific to each trigger type:
|
||||
* - Webhook/HTTP: \`(wm_trigger: { kind: 'http' | 'webhook', http?: { ... } }, body_key_1: any, body_key_2: any, ...)\`
|
||||
* - Postgres: \`(wm_trigger: { kind: 'postgres' }, transaction_type: string, schema_name: string, table_name: string, row: any)\`
|
||||
* - WebSocket/Kafka/NATS/SQS/MQTT: \`(wm_trigger: { kind: 'websocket' | 'kafka' | 'nats' | 'sqs' | 'mqtt', [kind]: { ... } }, msg: string)\`
|
||||
* - WebSocket/Kafka/NATS/SQS: \`(wm_trigger: { kind: 'websocket' | 'kafka' | 'nats' | 'sqs', [kind]: { ... } }, msg: string)\`
|
||||
* - MQTT: \`(wm_trigger: { kind: 'mqtt', [kind]: { ... } }, payload: Array<number>)\`
|
||||
* - GCP: \`(wm_trigger: { kind: 'gcp', [kind]: { ... } }, payload: string)\`
|
||||
* - Email: \`(wm_trigger: { kind: 'email' }, raw_email: string, parsed_email: { ... })\`
|
||||
*
|
||||
* The returned object defines the parameter values passed to \`main()\`.
|
||||
@@ -655,7 +657,9 @@ export const TS_PREPROCESSOR_FLOW_INTRO = `/**
|
||||
* The structure of \`wm_trigger\` and the main trigger arguments are specific to each trigger type:
|
||||
* - Webhook/HTTP: \`(wm_trigger: { kind: 'http' | 'webhook', http?: { ... } }, body_key_1: any, body_key_2: any, ...)\`
|
||||
* - Postgres: \`(wm_trigger: { kind: 'postgres' }, transaction_type: string, schema_name: string, table_name: string, row: any)\`
|
||||
* - WebSocket/Kafka/NATS/SQS/MQTT: \`(wm_trigger: { kind: 'websocket' | 'kafka' | 'nats' | 'sqs' | 'mqtt', [kind]: { ... } }, msg: string)\`
|
||||
* - WebSocket/Kafka/NATS/SQS: \`(wm_trigger: { kind: 'websocket' | 'kafka' | 'nats' | 'sqs', [kind]: { ... } }, msg: string)\`
|
||||
* - MQTT: \`(wm_trigger: { kind: 'mqtt', [kind]: { ... } }, payload: Array<number>)\`
|
||||
* - GCP: \`(wm_trigger: { kind: 'gcp', [kind]: { ... } }, payload: string)\`*
|
||||
* - Email: \`(wm_trigger: { kind: 'email' }, raw_email: string, parsed_email: { ... })\`
|
||||
*
|
||||
* The returned object determines the parameter values passed to the flow.
|
||||
@@ -673,7 +677,7 @@ export const TS_PREPROCESSOR_MODULE_CODE = `export async function preprocessor(
|
||||
|
||||
// The trigger metadata
|
||||
wm_trigger: {
|
||||
kind: 'http' | 'email' | 'webhook' | 'websocket' | 'kafka' | 'nats' | 'postgres' | 'sqs' | 'mqtt',
|
||||
kind: 'http' | 'email' | 'webhook' | 'websocket' | 'kafka' | 'nats' | 'postgres' | 'sqs' | 'mqtt' | 'gcp',
|
||||
http?: {
|
||||
route: string // The route path, e.g. "/users/:id"
|
||||
path: string // The actual path called, e.g. "/users/123"
|
||||
@@ -722,6 +726,15 @@ export const TS_PREPROCESSOR_MODULE_CODE = `export async function preprocessor(
|
||||
subscription_identifiers?: Array<number>,
|
||||
content_type?: string
|
||||
}
|
||||
},
|
||||
gcp?: {
|
||||
message_id: string,
|
||||
subscription: string,
|
||||
ordering_key?: string,
|
||||
attributes?: Record<string, string>,
|
||||
delivery_type: "push" | "pull",
|
||||
headers?: Record<string, string>,
|
||||
publish_time?: string,
|
||||
}
|
||||
}
|
||||
) {
|
||||
@@ -769,7 +782,9 @@ export const PYTHON_PREPROCESSOR_SCRIPT_INTRO = `# Trigger preprocessor
|
||||
# The structure of \`wm_trigger\` and the main trigger arguments are specific to each trigger type:
|
||||
# - Webhook/HTTP: \`(wm_trigger: { kind: 'http' | 'webhook', http?: { ... } }, body_key_1: any, body_key_2: any, ...)\`
|
||||
# - Postgres: \`(wm_trigger: { kind: 'postgres' }, transaction_type: string, schema_name: string, table_name: string, row: any)\`
|
||||
# - WebSocket/Kafka/NATS/SQS/MQTT: \`(wm_trigger: { kind: 'websocket' | 'kafka' | 'nats' | 'sqs' | 'mqtt', [kind]: { ... } }, msg: string)\`
|
||||
# - WebSocket/Kafka/NATS/SQS: \`(wm_trigger: { kind: 'websocket' | 'kafka' | 'nats' | 'sqs', [kind]: { ... } }, msg: string)\`
|
||||
# - MQTT: \`(wm_trigger: { kind: 'mqtt', [kind]: { ... } }, payload: Array<number>)\`
|
||||
# - GCP: \`(wm_trigger: { kind: 'gcp', [kind]: { ... } }, payload: string)\`
|
||||
# - Email: \`(wm_trigger: { kind: 'email' }, raw_email: string, parsed_email: { ... })\`
|
||||
#
|
||||
# The returned object defines the parameter values passed to \`main()\`.
|
||||
@@ -787,7 +802,9 @@ export const PYTHON_PREPROCESSOR_FLOW_INTRO = `# Trigger preprocessor
|
||||
# plus trigger metadata in the \`wm_trigger\` object:
|
||||
# - Webhook/HTTP: \`(wm_trigger: { kind: 'http' | 'webhook', http?: { ... } }, body_key_1: any, body_key_2: any, ...)\`
|
||||
# - Postgres: \`(wm_trigger: { kind: 'postgres' }, transaction_type: string, schema_name: string, table_name: string, row: any)\`
|
||||
# - WebSocket/Kafka/NATS/SQS/MQTT: \`(wm_trigger: { kind: 'websocket' | 'kafka' | 'nats' | 'sqs' | 'mqtt', [kind]: { ... } }, msg: string)\`
|
||||
# - WebSocket/Kafka/NATS/SQS: \`(wm_trigger: { kind: 'websocket' | 'kafka' | 'nats' | 'sqs', [kind]: { ... } }, msg: string)\`
|
||||
# - MQTT: \`(wm_trigger: { kind: 'mqtt', [kind]: { ... } }, payload: Array<number>)\`
|
||||
# - GCP: \`(wm_trigger: { kind: 'gcp', [kind]: { ... } }, payload: string)\`
|
||||
# - Email: \`(wm_trigger: { kind: 'email' }, raw_email: string, parsed_email: { ... })\`
|
||||
#
|
||||
# The returned object determines the parameter values passed to the flow.
|
||||
@@ -848,14 +865,25 @@ class Mqtt(TypedDict):
|
||||
qos: int
|
||||
v5: MqttV5Properties | None
|
||||
|
||||
class Gcp(TypedDict):
|
||||
message_id: str
|
||||
subscription: str
|
||||
ordering_key: str | None
|
||||
attributes: dict[str, str] | None
|
||||
delivery_type: Literal["push", "pull"]
|
||||
headers: dict[str, str] | None
|
||||
publish_time: str | None
|
||||
|
||||
|
||||
class WmTrigger(TypedDict):
|
||||
kind: Literal["http", "email", "webhook", "websocket", "kafka", "nats", "postgres", "sqs", "mqtt"]
|
||||
kind: Literal["http", "email", "webhook", "websocket", "kafka", "nats", "postgres", "sqs", "mqtt", "gcp"]
|
||||
http: Http | None
|
||||
websocket: Websocket | None
|
||||
kafka: Kafka | None
|
||||
nats: Nats | None
|
||||
sqs: Sqs | None
|
||||
mqtt: Mqtt | None
|
||||
gcp: Gcp | None
|
||||
|
||||
def preprocessor(
|
||||
# Replace this comment with the parameters received from the trigger.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { minimatch } from 'minimatch'
|
||||
import {
|
||||
GcpTriggerService,
|
||||
HttpTriggerService,
|
||||
KafkaTriggerService,
|
||||
MqttTriggerService,
|
||||
@@ -8,9 +9,11 @@ import {
|
||||
ScheduleService,
|
||||
SqsTriggerService,
|
||||
WebsocketTriggerService,
|
||||
type NewGcpTrigger,
|
||||
type WorkspaceDeployUISettings
|
||||
} from './gen'
|
||||
import type { TriggerKind } from './components/triggers'
|
||||
import { base } from './base'
|
||||
|
||||
type DeployUIType = 'script' | 'flow' | 'app' | 'resource' | 'variable' | 'secret' | 'trigger'
|
||||
|
||||
@@ -75,7 +78,11 @@ export async function existsTrigger(
|
||||
return await PostgresTriggerService.existsPostgresTrigger(data)
|
||||
} else if (triggerKind === 'sqs') {
|
||||
return await SqsTriggerService.existsSqsTrigger(data)
|
||||
} else if (triggerKind === 'websockets') {
|
||||
}
|
||||
else if (triggerKind === 'gcp') {
|
||||
return await GcpTriggerService.existsGcpTrigger(data)
|
||||
}
|
||||
else if (triggerKind === 'websockets') {
|
||||
return await WebsocketTriggerService.existsWebsocketTrigger(data)
|
||||
} else if (triggerKind === 'nats') {
|
||||
return await NatsTriggerService.existsNatsTrigger(data)
|
||||
@@ -84,7 +91,7 @@ export async function existsTrigger(
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unexpected trigger kind ${triggerKind}. Allowed kinds are: routes, kafka, mqtt, postgres, sqs, websockets, nats, schedules.`
|
||||
`Unexpected trigger kind ${triggerKind}. Allowed kinds are: routes, kafka, mqtt, postgres, sqs, gcp, websockets, nats, schedules.`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -133,7 +140,36 @@ export async function getTriggersDeployData(kind: TriggerKind, path: string, wor
|
||||
createFn: NatsTriggerService.createNatsTrigger,
|
||||
updateFn: NatsTriggerService.updateNatsTrigger
|
||||
}
|
||||
} else if (kind === 'postgres') {
|
||||
}
|
||||
else if (kind === 'gcp') {
|
||||
const gcpTrigger = await GcpTriggerService.getGcpTrigger({
|
||||
workspace: workspace!,
|
||||
path: path
|
||||
})
|
||||
|
||||
gcpTrigger.subscription_id = ''
|
||||
if (gcpTrigger.delivery_config) {
|
||||
gcpTrigger.delivery_config.audience = ''
|
||||
}
|
||||
|
||||
const data: NewGcpTrigger = {
|
||||
subscription_mode: {
|
||||
subscription_mode: 'create_update',
|
||||
subscription_id: gcpTrigger.subscription_id,
|
||||
delivery_type: gcpTrigger.delivery_type,
|
||||
delivery_config: gcpTrigger.delivery_config,
|
||||
base_endpoint: `${window.location.origin}${base}`
|
||||
},
|
||||
...gcpTrigger
|
||||
}
|
||||
|
||||
return {
|
||||
data,
|
||||
createFn: GcpTriggerService.createGcpTrigger,
|
||||
updateFn: GcpTriggerService.updateGcpTrigger
|
||||
}
|
||||
}
|
||||
else if (kind === 'postgres') {
|
||||
const postgresTrigger = await PostgresTriggerService.getPostgresTrigger({
|
||||
workspace: workspace!,
|
||||
path: path
|
||||
@@ -285,7 +321,26 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace
|
||||
replication_slot_name,
|
||||
publication_name
|
||||
}
|
||||
} else if (kind === 'websockets') {
|
||||
}
|
||||
else if (kind === 'gcp') {
|
||||
const {
|
||||
enabled,
|
||||
script_path,
|
||||
is_flow,
|
||||
gcp_resource_path,
|
||||
} = await GcpTriggerService.getGcpTrigger({
|
||||
workspace: workspace!,
|
||||
path: path
|
||||
})
|
||||
|
||||
return {
|
||||
enabled,
|
||||
script_path,
|
||||
is_flow,
|
||||
gcp_resource_path,
|
||||
}
|
||||
}
|
||||
else if (kind === 'websockets') {
|
||||
const {
|
||||
enabled,
|
||||
script_path,
|
||||
@@ -456,7 +511,17 @@ export async function getTriggerDependency(kind: TriggerKind, path: string, work
|
||||
})
|
||||
|
||||
result = retrieveKindsValues({ resource_path: postgres_resource_path, script_path, is_flow })
|
||||
} else if (kind === 'websockets') {
|
||||
}
|
||||
else if (kind === 'gcp') {
|
||||
const { gcp_resource_path, script_path, is_flow } =
|
||||
await GcpTriggerService.getGcpTrigger({
|
||||
workspace: workspace!,
|
||||
path: path
|
||||
})
|
||||
|
||||
result = retrieveKindsValues({ resource_path: gcp_resource_path, script_path, is_flow })
|
||||
}
|
||||
else if (kind === 'websockets') {
|
||||
const { script_path, is_flow, url, initial_messages } =
|
||||
await WebsocketTriggerService.getWebsocketTrigger({
|
||||
workspace: workspace!,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user