support variables

This commit is contained in:
Diego Imbert
2025-07-07 17:03:45 +02:00
parent aaa924abec
commit 729350cb82
9 changed files with 189 additions and 77 deletions
@@ -1,6 +1,6 @@
CREATE TYPE ASSET_USAGE_KIND AS ENUM ('script', 'flow');
CREATE TYPE ASSET_ACCESS_TYPE AS ENUM ('r', 'w', 'rw');
CREATE TYPE ASSET_KIND AS ENUM ('s3object', 'resource');
CREATE TYPE ASSET_KIND AS ENUM ('s3object', 'resource', 'variable');
CREATE TABLE asset (
workspace_id VARCHAR(50) NOT NULL REFERENCES workspace(id) ON DELETE CASCADE ON UPDATE CASCADE,
@@ -53,23 +53,35 @@ impl AssetsFinder {
.or_else(|| {
node.func
.as_attribute_expr()
.and_then(|attr| attr.value.as_name_expr().and_then(|o| o.id.parse().ok()))
.and_then(|attr| attr.attr.parse().ok())
})
.ok_or(())?;
let (kind, access_type) = match ident.as_str() {
"get_resource" => (AssetKind::Resource, None),
"load_s3_file" => (AssetKind::S3Object, Some(R)),
"write_s3_file" => (AssetKind::S3Object, Some(W)),
let (kind, access_type, arg) = match ident.as_str() {
"load_s3_file" => (AssetKind::S3Object, Some(R), Arg::Pos(0)),
"load_s3_file_reader" => (AssetKind::S3Object, Some(R), Arg::Pos(0)),
"write_s3_file" => (AssetKind::S3Object, Some(W), Arg::Pos(0)),
"get_resource" => (AssetKind::Resource, None, Arg::Pos(0)),
"set_resource" => (AssetKind::Resource, Some(W), Arg::Named("path")),
"get_boto3_connection_settings" => (AssetKind::Resource, None, Arg::Pos(0)),
"get_polars_connection_settings" => (AssetKind::Resource, None, Arg::Pos(0)),
"get_duckdb_connection_settings" => (AssetKind::Resource, None, Arg::Pos(0)),
"get_variable" => (AssetKind::Variable, Some(R), Arg::Pos(0)),
"set_variable" => (AssetKind::Variable, Some(W), Arg::Pos(0)),
_ => return Err(()),
};
if node.args.len() < 1 {
return Err(());
}
let arg_val = match arg {
Arg::Pos(i) => node.args.get(i),
Arg::Named(name) => node
.keywords
.iter()
.find(|kw| kw.arg.as_deref() == Some(name))
.map(|kw| &kw.value),
};
match &node.args[0] {
Expr::Constant(ExprConstant { value: Constant::Str(value), .. }) => {
match arg_val {
Some(Expr::Constant(ExprConstant { value: Constant::Str(value), .. })) => {
let path = parse_asset_syntax(&value).map(|(_, p)| p).unwrap_or(&value);
self.assets
.push(ParseAssetsResult { kind, path: path.to_string(), access_type });
@@ -79,3 +91,8 @@ impl AssetsFinder {
Ok(())
}
}
enum Arg {
Pos(usize),
Named(&'static str),
}
@@ -74,17 +74,25 @@ impl AssetsFinder {
Some(Expr::Member(MemberExpr { prop: MemberProp::Ident(i), .. })) => i.sym.as_str(),
_ => return Err(()),
};
let (kind, access_type) = match ident {
"getResource" => (AssetKind::Resource, None),
"loadS3File" => (AssetKind::S3Object, Some(R)),
"writeS3File" => (AssetKind::S3Object, Some(W)),
let (kind, access_type, arg_pos) = match ident {
"loadS3File" => (AssetKind::S3Object, Some(R), 0),
"loadS3FileStream" => (AssetKind::S3Object, Some(R), 0),
"writeS3File" => (AssetKind::S3Object, Some(W), 0),
"getResource" => (AssetKind::Resource, None, 0),
"setResource" => (AssetKind::Resource, Some(W), 1),
"databaseUrlFromResource" => (AssetKind::Resource, None, 0),
"denoS3LightClientSettings" => (AssetKind::Resource, None, 0),
"duckdbConnectionSettings" => (AssetKind::Resource, None, 0),
"polarsConnectionSettings" => (AssetKind::Resource, None, 0),
"getVariable" => (AssetKind::Variable, Some(R), 0),
"setVariable" => (AssetKind::Variable, Some(W), 0),
_ => return Err(()),
};
if node.args.len() < 1 {
return Err(());
}
match node.args[0].expr.as_ref() {
Expr::Lit(Lit::Str(Str { value, .. })) => {
let arg_value = node.args.get(arg_pos);
match arg_value.map(|e| e.expr.as_ref()) {
Some(Expr::Lit(Lit::Str(Str { value, .. }))) => {
let path = parse_asset_syntax(&value).map(|(_, p)| p).unwrap_or(&value);
self.assets
.push(ParseAssetsResult { kind, path: path.to_string(), access_type });
@@ -15,6 +15,7 @@ use AssetUsageAccessType::*;
pub enum AssetKind {
S3Object,
Resource,
Variable,
}
#[derive(Serialize)]
@@ -56,6 +57,8 @@ pub fn parse_asset_syntax(s: &str) -> Option<(AssetKind, &str)> {
Some((AssetKind::Resource, &s[6..]))
} else if s.starts_with("$res:") {
Some((AssetKind::Resource, &s[5..]))
} else if s.starts_with("var://") {
Some((AssetKind::Variable, &s[6..]))
} else {
None
}
+91 -55
View File
@@ -4953,7 +4953,8 @@ paths:
/scripts_u/tokened_raw/{workspace}/{token}/{path}:
get:
summary: raw script by path with a token (mostly used by lsp to be used with
summary:
raw script by path with a token (mostly used by lsp to be used with
import maps to resolve scripts)
operationId: rawScriptByPathTokened
tags:
@@ -6754,7 +6755,8 @@ paths:
schema:
type: string
- name: branch_or_iteration_n
description: for branchall or loop, the iteration at which the flow should
description:
for branchall or loop, the iteration at which the flow should
restart
required: true
in: path
@@ -8472,7 +8474,7 @@ paths:
text/plain:
schema:
type: string
/w/{workspace}/openapi/download:
post:
summary: Download the OpenAPI v3.1 spec as a file
@@ -9827,7 +9829,6 @@ paths:
schema:
type: string
/w/{workspace}/gcp_triggers/subscriptions/delete/{path}:
delete:
summary: delete gcp trigger
@@ -9913,7 +9914,7 @@ paths:
application/json:
schema:
type: string
/w/{workspace}/postgres_triggers/is_valid_postgres_configuration/{path}:
get:
summary: check if postgres configuration is set to logical
@@ -11365,7 +11366,7 @@ paths:
postgres_trigger,
mqtt_trigger,
gcp_trigger,
sqs_trigger
sqs_trigger,
]
responses:
"200":
@@ -11409,7 +11410,7 @@ paths:
postgres_trigger,
mqtt_trigger,
gcp_trigger,
sqs_trigger
sqs_trigger,
]
requestBody:
description: acl to add
@@ -11464,7 +11465,7 @@ paths:
postgres_trigger,
mqtt_trigger,
gcp_trigger,
sqs_trigger
sqs_trigger,
]
requestBody:
description: acl to add
@@ -11843,7 +11844,8 @@ paths:
/w/{workspace}/job_helpers/duckdb_connection_settings:
post:
summary: Converts an S3 resource to the set of instructions necessary to connect
summary:
Converts an S3 resource to the set of instructions necessary to connect
DuckDB to an S3 bucket
operationId: duckdbConnectionSettings
tags:
@@ -11872,7 +11874,8 @@ paths:
type: string
/w/{workspace}/job_helpers/v2/duckdb_connection_settings:
post:
summary: Converts an S3 resource to the set of instructions necessary to connect
summary:
Converts an S3 resource to the set of instructions necessary to connect
DuckDB to an S3 bucket
operationId: duckdbConnectionSettingsV2
tags:
@@ -11908,7 +11911,8 @@ paths:
/w/{workspace}/job_helpers/polars_connection_settings:
post:
summary: Converts an S3 resource to the set of arguments necessary to connect
summary:
Converts an S3 resource to the set of arguments necessary to connect
Polars to an S3 bucket
operationId: polarsConnectionSettings
tags:
@@ -11952,7 +11956,8 @@ paths:
- client_kwargs
/w/{workspace}/job_helpers/v2/polars_connection_settings:
post:
summary: Converts an S3 resource to the set of arguments necessary to connect
summary:
Converts an S3 resource to the set of arguments necessary to connect
Polars to an S3 bucket
operationId: polarsConnectionSettingsV2
tags:
@@ -12029,7 +12034,8 @@ paths:
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: S3 resource path to use. If empty, the S3 resource defined in the
description:
S3 resource path to use. If empty, the S3 resource defined in the
workspace settings will be used
required: true
content:
@@ -12947,11 +12953,11 @@ paths:
summary: List all assets in the workspace
operationId: listAssets
tags:
- asset
- asset
parameters:
- $ref: '#/components/parameters/WorkspaceId'
- $ref: "#/components/parameters/WorkspaceId"
responses:
'200':
"200":
description: all assets in the workspace
content:
application/json:
@@ -12964,7 +12970,7 @@ paths:
path:
type: string
kind:
$ref: '#/components/schemas/AssetKind'
$ref: "#/components/schemas/AssetKind"
usages:
type: array
items:
@@ -12974,18 +12980,18 @@ paths:
path:
type: string
kind:
$ref: '#/components/schemas/AssetUsageKind'
$ref: "#/components/schemas/AssetUsageKind"
access_type:
$ref: '#/components/schemas/AssetUsageAccessType'
$ref: "#/components/schemas/AssetUsageAccessType"
/w/{workspace}/assets/list_by_usages:
post:
summary: List all assets used by given usages paths
operationId: listAssetsByUsage
tags:
- asset
- asset
parameters:
- $ref: '#/components/parameters/WorkspaceId'
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: list assets by usages
required: true
@@ -13004,9 +13010,9 @@ paths:
path:
type: string
kind:
$ref: '#/components/schemas/AssetUsageKind'
$ref: "#/components/schemas/AssetUsageKind"
responses:
'200':
"200":
description: all assets used by the given usage paths, in the same order
content:
application/json:
@@ -13021,9 +13027,9 @@ paths:
path:
type: string
kind:
$ref: '#/components/schemas/AssetKind'
$ref: "#/components/schemas/AssetKind"
access_type:
$ref: '#/components/schemas/AssetUsageAccessType'
$ref: "#/components/schemas/AssetUsageAccessType"
components:
securitySchemes:
@@ -13171,7 +13177,8 @@ components:
type: string
ParentJob:
name: parent_job
description: The parent job that is at the origin and responsible for the execution
description:
The parent job that is at the origin and responsible for the execution
of this script if any
in: query
schema:
@@ -13191,7 +13198,8 @@ components:
type: string
NewJobId:
name: job_id
description: The job id to assign to the created job. if missing, job is chosen
description:
The job id to assign to the created job. if missing, job is chosen
randomly using the ULID scheme. If a job id already exists in the queue
or as a completed job, the request to create one will fail (Bad Request)
in: query
@@ -13282,7 +13290,8 @@ components:
format: date-time
CreatedOrStartedAfter:
name: created_or_started_after
description: filter on created_at for non non started job and started_at otherwise
description:
filter on created_at for non non started job and started_at otherwise
after (exclusive) timestamp
in: query
schema:
@@ -13290,7 +13299,8 @@ components:
format: date-time
CreatedOrStartedAfterCompletedJob:
name: created_or_started_after_completed_jobs
description: filter on created_at for non non started job and started_at otherwise
description:
filter on created_at for non non started job and started_at otherwise
after (exclusive) timestamp but only for the completed jobs
in: query
schema:
@@ -13298,7 +13308,8 @@ components:
format: date-time
CreatedOrStartedBefore:
name: created_or_started_before
description: filter on created_at for non non started job and started_at otherwise
description:
filter on created_at for non non started job and started_at otherwise
before (inclusive) timestamp
in: query
schema:
@@ -13386,7 +13397,8 @@ components:
enum: [Create, Update, Delete, Execute]
JobKinds:
name: job_kinds
description: filter on job kind (values 'preview', 'script', 'dependencies', 'flow')
description:
filter on job kind (values 'preview', 'script', 'dependencies', 'flow')
separated by,
in: query
schema:
@@ -13481,7 +13493,19 @@ components:
AIProvider:
type: string
enum: [openai, azure_openai, anthropic, mistral, deepseek, googleai, groq, openrouter, togetherai, customai]
enum:
[
openai,
azure_openai,
anthropic,
mistral,
deepseek,
googleai,
groq,
openrouter,
togetherai,
customai,
]
AIProviderModel:
type: object
@@ -13548,7 +13572,7 @@ components:
alerts:
type: array
items:
$ref: '#/components/schemas/Alert'
$ref: "#/components/schemas/Alert"
Script:
type: object
@@ -13736,7 +13760,7 @@ components:
access_type:
type: string
enum: [r, w, rw]
required:
- path
- summary
@@ -13884,7 +13908,7 @@ components:
"singlescriptflow",
"flowscript",
"flownode",
"appscript"
"appscript",
]
schedule_path:
type: string
@@ -13991,7 +14015,7 @@ components:
"singlescriptflow",
"flowscript",
"flownode",
"appscript"
"appscript",
]
schedule_path:
type: string
@@ -14443,7 +14467,7 @@ components:
"bytes",
"dict",
"datetime",
"sql"
"sql",
]
- type: object
properties:
@@ -14483,7 +14507,7 @@ components:
"bytes",
"dict",
"datetime",
"sql"
"sql",
]
- type: object
properties:
@@ -14509,7 +14533,7 @@ components:
"bytes",
"dict",
"datetime",
"sql"
"sql",
]
- type: object
properties:
@@ -14562,7 +14586,7 @@ components:
csharp,
nu,
java,
duckdb
duckdb,
# for related places search: ADD_NEW_LANG
]
@@ -15037,7 +15061,7 @@ components:
- user_or_folder_regex_value
- path
- runnable_kind
OpenapiV3Info:
type: object
properties:
@@ -15617,7 +15641,6 @@ components:
- delivery_type
- subscription_mode
SubscriptionMode:
type: string
enum:
@@ -15625,7 +15648,6 @@ components:
- create_update
description: "The mode of subscription. 'existing' means using an existing GCP subscription, while 'create_update' involves creating or updating a new subscription."
GcpTriggerData:
type: object
properties:
@@ -15667,7 +15689,6 @@ components:
required:
- topic_id
DeleteGcpSubscription:
type: object
properties:
@@ -16663,7 +16684,14 @@ components:
properties:
type:
type: string
enum: ["S3Storage", "AzureBlobStorage", "AzureWorkloadIdentity", "S3AwsOidc", "GoogleCloudStorage"]
enum:
[
"S3Storage",
"AzureBlobStorage",
"AzureWorkloadIdentity",
"S3AwsOidc",
"GoogleCloudStorage",
]
s3_resource_path:
type: string
azure_blob_resource_path:
@@ -16680,7 +16708,13 @@ components:
type:
type: string
enum:
["S3Storage", "AzureBlobStorage", "AzureWorkloadIdentity", "S3AwsOidc", "GoogleCloudStorage"]
[
"S3Storage",
"AzureBlobStorage",
"AzureWorkloadIdentity",
"S3AwsOidc",
"GoogleCloudStorage",
]
s3_resource_path:
type: string
azure_blob_resource_path:
@@ -17070,7 +17104,8 @@ components:
CaptureTriggerKind:
type: string
enum: [webhook, http, websocket, kafka, email, nats, postgres, sqs, mqtt, gcp]
enum:
[webhook, http, websocket, kafka, email, nats, postgres, sqs, mqtt, gcp]
Capture:
type: object
@@ -17271,24 +17306,25 @@ components:
AssetUsageKind:
type: string
enum:
- script
- flow
- script
- flow
AssetUsageAccessType:
type: string
enum:
- r
- w
- rw
- r
- w
- rw
AssetKind:
type: string
enum:
- s3object
- resource
- s3object
- resource
- variable
Asset:
type: object
properties:
path:
type: string
kind:
$ref: '#/components/schemas/AssetKind'
$ref: "#/components/schemas/AssetKind"
required: [path, kind]
+2
View File
@@ -10,6 +10,7 @@ use crate::{error, scripts::ScriptLang};
pub enum AssetKind {
S3Object,
Resource,
Variable,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone, Hash, Eq, sqlx::Type)]
@@ -75,6 +76,7 @@ impl From<windmill_parser::asset_parser::AssetKind> for AssetKind {
match kind {
windmill_parser::asset_parser::AssetKind::S3Object => AssetKind::S3Object,
windmill_parser::asset_parser::AssetKind::Resource => AssetKind::Resource,
windmill_parser::asset_parser::AssetKind::Variable => AssetKind::Variable,
}
}
}
+3 -1
View File
@@ -12,9 +12,11 @@ export type AssetWithAccessType = Asset & { access_type?: AssetUsageAccessType }
export function formatAsset(asset: Asset): string {
switch (asset.kind) {
case 'resource':
return `$res:${asset.path}`
return `res://${asset.path}`
case 's3object':
return `s3://${asset.path}`
case 'variable':
return `var://${asset.path}`
}
}
@@ -1,7 +1,9 @@
<script lang="ts">
import { Pyramid } from 'lucide-svelte'
import type { AssetKind } from '../assets/lib'
import AssetResIcon from './AssetResIcon.svelte'
import AssetS3Icon from './AssetS3Icon.svelte'
import AssetVarIcon from './AssetVarIcon.svelte'
interface Props {
size?: string
@@ -17,4 +19,8 @@
<AssetS3Icon {fill} width={size} height={size} class={className} />
{:else if assetKind == 'resource'}
<AssetResIcon {fill} width={size} height={size} class={className} />
{:else}{/if}
{:else if assetKind == 'variable'}
<AssetVarIcon {fill} width={size} height={size} class={className} />
{:else}
<Pyramid {fill} {size} class={'fill-none ' + className} />
{/if}
@@ -0,0 +1,38 @@
<script lang="ts">
interface Props {
height?: string
width?: string
fill?: string
class?: string
}
let { height = '24px', width = '24px', fill = 'black', class: className = '' }: Props = $props()
</script>
<svg
{width}
{height}
class={className}
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_1_2)">
<path
d="M11.1313 0.0107422C11.4064 0.0294475 11.6761 0.101564 11.9214 0.229492C12.1352 0.341109 12.3269 0.490166 12.4868 0.668945L12.6362 0.857422L12.6392 0.861328L18.9614 10H16.5288L11.9966 3.44824V19.583L12.0005 19.5801V21.8271C11.6792 21.9413 11.3401 22.0019 10.9966 22.002C10.476 22.002 9.96358 21.867 9.51122 21.6094L9.51024 21.6084L1.00048 16.748L0.99169 16.7441L0.992666 16.7432C0.75521 16.6045 0.54853 16.4182 0.386221 16.1963C0.223969 15.9743 0.108388 15.7214 0.0483305 15.4531C-0.0117126 15.1847 -0.0155885 14.9067 0.0366117 14.6367C0.0888149 14.3668 0.196342 14.1104 0.352041 13.8838L0.353995 13.8818L9.35399 0.861328L9.35595 0.857422C9.54024 0.593468 9.78632 0.378356 10.0718 0.229492C10.3164 0.101992 10.5847 0.0296674 10.8589 0.0107422C10.9037 0.00458076 10.95 0 10.9966 0C11.0422 4.34648e-05 11.0873 0.00479479 11.1313 0.0107422ZM1.9995 15.0156L9.99657 19.583V3.44824L1.9995 15.0156Z"
{fill}
stroke="none"
/>
<path
d="M17.5 10.9004C17.6325 10.9004 17.7577 10.9577 17.8486 11.0566C17.9391 11.1552 17.9892 11.2869 17.9893 11.4229V12.0283H20.0928C20.2252 12.0284 20.3505 12.0857 20.4414 12.1846C20.532 12.2832 20.5811 12.4157 20.5811 12.5518C20.5809 12.6876 20.5318 12.8195 20.4414 12.918C20.3505 13.0168 20.2252 13.0742 20.0928 13.0742H17.9893V15.9766H18.7959C19.4097 15.9766 19.9965 16.2428 20.4277 16.7119C20.8587 17.1808 21.0996 17.8149 21.0996 18.4746C21.0995 19.1343 20.8587 19.7685 20.4277 20.2373C19.9965 20.7063 19.4096 20.9717 18.7959 20.9717H17.9893V21.5771C17.9892 21.7131 17.9391 21.8448 17.8486 21.9434C17.7577 22.0423 17.6325 22.0996 17.5 22.0996C17.3675 22.0996 17.2423 22.0423 17.1514 21.9434C17.0609 21.8448 17.0108 21.7131 17.0107 21.5771V20.9717H14.3887C14.2562 20.9716 14.1309 20.9143 14.04 20.8154C13.9495 20.7168 13.9004 20.5843 13.9004 20.4482C13.9005 20.3124 13.9496 20.1805 14.04 20.082C14.1309 19.9832 14.2562 19.9258 14.3887 19.9258H17.0107V17.0234H16.2041C15.5903 17.0234 15.0035 16.7572 14.5723 16.2881C14.1413 15.8192 13.9004 15.1851 13.9004 14.5254C13.9005 13.8657 14.1413 13.2315 14.5723 12.7627C15.0035 12.2937 15.5904 12.0283 16.2041 12.0283H17.0107V11.4229C17.0108 11.2869 17.0609 11.1552 17.1514 11.0566C17.2423 10.9577 17.3675 10.9004 17.5 10.9004ZM17.9893 19.9258H18.7959C19.1445 19.9258 19.4811 19.775 19.7305 19.5039C19.9801 19.2323 20.122 18.862 20.1221 18.4746C20.1221 18.0871 19.9802 17.717 19.7305 17.4453C19.481 17.174 19.1446 17.0234 18.7959 17.0234H17.9893V19.9258ZM16.2041 13.0742C15.8555 13.0742 15.5189 13.225 15.2695 13.4961C15.0199 13.7677 14.878 14.138 14.8779 14.5254C14.8779 14.9129 15.0198 15.283 15.2695 15.5547C15.519 15.826 15.8554 15.9766 16.2041 15.9766H17.0107V13.0742H16.2041Z"
{fill}
stroke={fill}
stroke-width="0.2"
/>
</g>
<defs>
<clipPath id="clip0_1_2">
<rect width="22" height="22" fill="white" />
</clipPath>
</defs>
</svg>