diff --git a/backend/openapi.yaml b/backend/openapi.yaml index 96c078de98..f876f10712 100644 --- a/backend/openapi.yaml +++ b/backend/openapi.yaml @@ -3119,13 +3119,20 @@ components: typ: oneOf: - type: string - enum: ["str", "float", "int", "bool", "unknown"] + enum: ["str", "float", "int", "bool", "email", "unknown"] - type: object properties: resource: type: string required: - resource + - type: object + properties: + list: + type: string + enum: ["str", "float", "int", "email"] + required: + - list has_default: type: boolean default: {} diff --git a/backend/src/parser.rs b/backend/src/parser.rs index 719f92de67..d391a03485 100644 --- a/backend/src/parser.rs +++ b/backend/src/parser.rs @@ -25,6 +25,16 @@ pub struct MainArgSignature { pub args: Vec, } +#[derive(Serialize, Clone)] +#[serde(rename_all(serialize = "lowercase"))] +pub enum InnerTyp { + Str, + Int, + Float, + Bytes, + Email, +} + #[derive(Serialize, Clone)] #[serde(rename_all(serialize = "lowercase"))] pub enum Typ { @@ -33,10 +43,11 @@ pub enum Typ { Float, Bool, Dict, - List, + List(InnerTyp), Bytes, Datetime, Resource(String), + Email, Unknown, } @@ -95,7 +106,7 @@ pub fn parse_python_signature(code: &str) -> error::Result { "int" => Typ::Int, "bool" => Typ::Bool, "dict" => Typ::Dict, - "list" => Typ::List, + "list" => Typ::List(InnerTyp::Str), "bytes" => Typ::Bytes, "datetime" => Typ::Datetime, "datetime.datetime" => Typ::Datetime, @@ -120,7 +131,7 @@ use swc_common::sync::Lrc; use swc_common::{FileName, SourceMap}; use swc_ecma_ast::{ AssignPat, BindingIdent, Decl, ExportDecl, FnDecl, Ident, ModuleDecl, ModuleItem, Pat, - TsEntityName, TsKeywordTypeKind, TsType, TsTypeRef, + TsArrayType, TsEntityName, TsKeywordTypeKind, TsType, TsTypeRef, }; use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig}; @@ -239,7 +250,28 @@ fn binding_ident_to_arg( _ => Typ::Unknown, }, // TODO: we can do better here and extract the inner type of array - TsType::TsArrayType(_) => Typ::List, + TsType::TsArrayType(TsArrayType { span: _, elem_type }) => { + match &**elem_type { + TsType::TsTypeRef(TsTypeRef { + span: _, + type_name: + TsEntityName::Ident(Ident { + span: _, + sym, + optional: _, + }), + type_params: _, + }) => match sym.to_string().as_str() { + "Base64" => Typ::List(InnerTyp::Bytes), + "Email" => Typ::List(InnerTyp::Email), + "bigint" => Typ::List(InnerTyp::Int), + "number" => Typ::List(InnerTyp::Float), + _ => Typ::List(InnerTyp::Str), + }, + //TsType::TsKeywordType(()) + _ => Typ::List(InnerTyp::Str), + } + } TsType::TsTypeRef(TsTypeRef { span: _, type_name, @@ -266,6 +298,8 @@ fn binding_ident_to_arg( }) .unwrap_or_else(|| "unknown".to_string()), ), + "Base64" => Typ::Bytes, + "Email" => Typ::Email, _ => Typ::Unknown, } } @@ -755,7 +789,7 @@ def main(): let code = " export function main(test1: string, test2: string = \"burkina\", - test3: wmill.Resource<'postgres'>, email: email_string) { + test3: wmill.Resource<'postgres'>, b64: Base64, ls: Base64[], email: Email) { console.log(42) } diff --git a/deno-client/index.ts b/deno-client/index.ts index bd40a8b609..cdd9d5258a 100644 --- a/deno-client/index.ts +++ b/deno-client/index.ts @@ -7,9 +7,8 @@ export { UserApi, WorkspaceApi } from './windmill-api/index.ts' -export type string_regex = String -export type string_email = String - +export type Email = string +export type Base64 = string export type Resource = {} /** diff --git a/frontend/src/common.ts b/frontend/src/common.ts index 762c5e2595..e2a54549a2 100644 --- a/frontend/src/common.ts +++ b/frontend/src/common.ts @@ -10,7 +10,7 @@ export interface SchemaProperty { enum?: string[] contentEncoding?: 'base64' | 'binary' format?: string - items?: { type?: 'string' | 'number' } + items?: { type?: 'string' | 'number' | 'bytes', contentEncoding?: 'base64' }, } export type Schema = { diff --git a/frontend/src/infer.ts b/frontend/src/infer.ts index 3ab7f97550..fb0034aeef 100644 --- a/frontend/src/infer.ts +++ b/frontend/src/infer.ts @@ -44,7 +44,7 @@ export async function inferArgs( } } -function argSigToJsonSchemaType(t: string | { resource: string }, s: SchemaProperty): void { +function argSigToJsonSchemaType(t: string | { resource: string } | { list: string }, s: SchemaProperty): void { if (t === 'int') { s.type = 'integer' } else if (t === 'float') { @@ -55,18 +55,23 @@ function argSigToJsonSchemaType(t: string | { resource: string }, s: SchemaPrope s.type = 'string' } else if (t === 'dict') { s.type = 'object' - } else if (t === 'list') { - s.type = 'array' } else if (t === 'bytes') { s.type = 'string' s.contentEncoding = 'base64' } else if (t === 'datetime') { s.type = 'string' s.format = 'date-time' - } else if (typeof t !== 'string' && t.resource != undefined) { + } else if (typeof t !== 'string' && `resource` in t) { s.type = 'object' s.format = `resource-${t.resource}` - } else { - s.type = undefined + } else if (typeof t !== 'string' && `list` in t) { + s.type = 'array' + if (t.list === 'int' || t.list === 'float') { + s.items = { type: 'number' } + } else if (t.list === 'bytes') { + s.items = { type: 'string', contentEncoding: 'base64' } + } else { + s.items = { type: 'string' } + } } } diff --git a/frontend/src/routes/components/ArgInput.svelte b/frontend/src/routes/components/ArgInput.svelte index 36a59f22dd..0f98554b5c 100644 --- a/frontend/src/routes/components/ArgInput.svelte +++ b/frontend/src/routes/components/ArgInput.svelte @@ -27,7 +27,8 @@ export let enum_: string[] | undefined = undefined export let disabled = false export let editableSchema = false - export let itemsType: { type?: 'string' | 'number' } | undefined = undefined + export let itemsType: { type?: 'string' | 'number'; contentEncoding?: string } | undefined = + undefined export let displayHeader = true let seeEditable: boolean = enum_ != undefined || pattern != undefined @@ -70,16 +71,16 @@ rawValue = JSON.stringify(value, null, 4) } - function fileChanged(e: any) { + function fileChanged(e: any, cb: (v: string | undefined) => void) { let t = e.target if (t && 'files' in t && t.files.length > 0) { let reader = new FileReader() reader.onload = (e: any) => { - value = e.target.result.split('base64,')[1] + cb(e.target.result.split('base64,')[1]) } reader.readAsDataURL(t.files[0]) } else { - value = undefined + cb(undefined) } } @@ -147,6 +148,9 @@ + {/if} @@ -192,6 +196,13 @@
{#if itemsType.type == 'number'} + {:else if itemsType.type == 'string' && itemsType.contentEncoding == 'base64'} + fileChanged(x, (val) => (v = val))} + multiple={false} + /> {:else} {/if} @@ -236,7 +247,12 @@ {:else if type == 'string' && format == 'date-time'} {:else if type == 'string' && contentEncoding == 'base64'} - + fileChanged(x, (val) => (value = val))} + multiple={false} + /> {:else if type == 'string' && format?.startsWith('resource')} import { faPlus } from '@fortawesome/free-solid-svg-icons' import type { Schema } from '../../common' - import { emptySchema } from '../../utils' + import { emptySchema, loadSchema } from '../../utils' import Icon from 'svelte-awesome' @@ -22,16 +22,13 @@ await Promise.all( flow.value.modules.map(async (x, i) => { if (x.value.path) { - const script = await ScriptService.getScriptByPath({ - workspace: $workspaceStore!, - path: x.value.path ?? '' - }) + const schema = await loadSchema(x.value.path ?? '') if ( - JSON.stringify(Object.keys(script.schema?.properties ?? {}).sort()) != + JSON.stringify(Object.keys(schema?.properties ?? {}).sort()) != JSON.stringify(Object.keys(x.input_transform).sort()) ) { let it = {} - Object.keys(script.schema?.properties ?? {}).map( + Object.keys(schema?.properties ?? {}).map( (x) => (it[x] = { type: 'static', @@ -40,7 +37,7 @@ ) schemaForms[i]?.setArgs(it) } - schemas[i] = script.schema ?? emptySchema() + schemas[i] = schema ?? emptySchema() } else { schemaForms[i]?.setArgs({}) schemas[i] = emptySchema() @@ -84,11 +81,7 @@ disabled={flow.value.modules.length == 0 || flow.value.modules[0].value.path == undefined} on:click={async () => { - const script = await ScriptService.getScriptByPath({ - workspace: $workspaceStore ?? '', - path: flow.value.modules[0].value.path ?? '' - }) - flow.schema = script.schema + flow.schema = await loadSchema(flow.value.modules[0].value.path ?? '') }} >Copy from step 1's schema diff --git a/frontend/src/routes/components/ModuleStep.svelte b/frontend/src/routes/components/ModuleStep.svelte index b2bdb98ce9..80448e4b4d 100644 --- a/frontend/src/routes/components/ModuleStep.svelte +++ b/frontend/src/routes/components/ModuleStep.svelte @@ -1,14 +1,11 @@