diff --git a/backend/openapi.yaml b/backend/openapi.yaml index 310d14c38a6f..1dbf373ad6f7 100644 --- a/backend/openapi.yaml +++ b/backend/openapi.yaml @@ -3646,7 +3646,6 @@ components: resource: type: string nullable: true - required: - resource - type: object @@ -3658,11 +3657,60 @@ components: nullable: true required: - str + - type: object + properties: + object: + type: array + items: + type: object + properties: + key: + type: string + typ: + oneOf: + - type: string + enum: + [ + "float", + "int", + "bool", + "email", + "unknown", + "bytes", + "dict", + "datetime", + "sql", + ] + - type: object + properties: + str: {} + required: [str] + required: + - key + - typ + required: + - object - type: object properties: list: - type: string - enum: ["str", "float", "int", "email"] + oneOf: + - type: string + enum: + [ + "float", + "int", + "bool", + "email", + "unknown", + "bytes", + "dict", + "datetime", + "sql", + ] + - type: object + properties: + str: {} + required: [str] nullable: true required: - list diff --git a/backend/src/parser.rs b/backend/src/parser.rs index 781cc5697a03..1f6dea9e4d85 100644 --- a/backend/src/parser.rs +++ b/backend/src/parser.rs @@ -17,12 +17,9 @@ pub struct MainArgSignature { #[derive(Serialize, Clone)] #[serde(rename_all(serialize = "lowercase"))] -pub enum InnerTyp { - Str, - Int, - Float, - Bytes, - Email, +pub struct ObjectProperty { + pub key: String, + pub typ: Box, } #[derive(Serialize, Clone)] @@ -32,13 +29,13 @@ pub enum Typ { Int, Float, Bool, - Dict, - List(InnerTyp), + List(Box), Bytes, Datetime, Resource(String), Email, Sql, + Object(Vec), Unknown, } diff --git a/backend/src/parser_py.rs b/backend/src/parser_py.rs index d6beffade7ea..a81926437c26 100644 --- a/backend/src/parser_py.rs +++ b/backend/src/parser_py.rs @@ -15,7 +15,7 @@ use serde_json::json; use crate::{ error, - parser::{Arg, InnerTyp, MainArgSignature, Typ}, + parser::{Arg, MainArgSignature, Typ}, }; use rustpython_parser::{ @@ -67,8 +67,8 @@ pub fn parse_python_signature(code: &str) -> error::Result { "float" => Typ::Float, "int" => Typ::Int, "bool" => Typ::Bool, - "dict" => Typ::Dict, - "list" => Typ::List(InnerTyp::Str), + "dict" => Typ::Object(vec![]), + "list" => Typ::List(Box::new(Typ::Str(None))), "bytes" => Typ::Bytes, "datetime" => Typ::Datetime, "datetime.datetime" => Typ::Datetime, diff --git a/backend/src/parser_ts.rs b/backend/src/parser_ts.rs index 83751421b856..a87dc17d2cbd 100644 --- a/backend/src/parser_ts.rs +++ b/backend/src/parser_ts.rs @@ -8,14 +8,15 @@ use crate::{ error, - parser::{Arg, InnerTyp, MainArgSignature, Typ}, + parser::{Arg, MainArgSignature, ObjectProperty, Typ}, }; use swc_common::{sync::Lrc, FileName, SourceMap}; use swc_ecma_ast::{ - AssignPat, BindingIdent, Decl, ExportDecl, FnDecl, Ident, ModuleDecl, ModuleItem, Pat, Str, - TsArrayType, TsEntityName, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType, TsOptionalType, - TsType, TsTypeRef, TsUnionOrIntersectionType, TsUnionType, + AssignPat, BindingIdent, Decl, ExportDecl, Expr, FnDecl, Ident, ModuleDecl, ModuleItem, Pat, + Str, TsArrayType, TsEntityName, TsKeywordType, TsKeywordTypeKind, TsLit, TsLitType, + TsOptionalType, TsPropertySignature, TsType, TsTypeElement, TsTypeLit, TsTypeRef, + TsUnionOrIntersectionType, TsUnionType, }; use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsConfig}; @@ -113,11 +114,11 @@ fn binding_ident_to_arg(BindingIdent { id, type_ann }: &BindingIdent) -> (String } fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) { - //println!("{:?}", ts_type); + println!("{:?}", ts_type); match ts_type { TsType::TsKeywordType(t) => ( match t.kind { - TsKeywordTypeKind::TsObjectKeyword => Typ::Dict, + TsKeywordTypeKind::TsObjectKeyword => Typ::Object(vec![]), TsKeywordTypeKind::TsBooleanKeyword => Typ::Bool, TsKeywordTypeKind::TsBigIntKeyword => Typ::Int, TsKeywordTypeKind::TsNumberKeyword => Typ::Float, @@ -126,25 +127,32 @@ fn tstype_to_typ(ts_type: &TsType) -> (Typ, bool) { }, false, ), - // TODO: we can do better here and extract the inner type of array - TsType::TsArrayType(TsArrayType { elem_type, .. }) => { - ( - match &**elem_type { - TsType::TsTypeRef(TsTypeRef { - type_name: TsEntityName::Ident(Ident { sym, .. }), + TsType::TsTypeLit(TsTypeLit { members, .. }) => { + let properties = members + .into_iter() + .filter_map(|x| match x { + TsTypeElement::TsPropertySignature(TsPropertySignature { + key, + type_ann, .. - }) => 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), + }) => match (*key.to_owned(), type_ann) { + (Expr::Ident(Ident { sym, .. }), type_ann) => Some(ObjectProperty { + key: sym.to_string(), + typ: type_ann + .as_ref() + .map(|typ| Box::new(tstype_to_typ(&*typ.type_ann).0)) + .unwrap_or(Box::new(Typ::Unknown)), + }), + _ => None, }, - //TsType::TsKeywordType(()) - _ => Typ::List(InnerTyp::Str), - }, - false, - ) + _ => None, + }) + .collect(); + (Typ::Object(properties), false) + } + // TODO: we can do better here and extract the inner type of array + TsType::TsArrayType(TsArrayType { elem_type, .. }) => { + (Typ::List(Box::new(tstype_to_typ(&**elem_type).0)), false) } TsType::TsLitType(TsLitType { lit: TsLit::Str(Str { value, .. }), .. }) => { (Typ::Str(Some(vec![value.to_string()])), false) @@ -232,7 +240,8 @@ mod tests { export function main(test1?: string, test2: string = \"burkina\", test3: wmill.Resource<'postgres'>, b64: Base64, ls: Base64[], email: Email, literal: \"test\", literal_union: \"test\" | \"test2\", - opt_type?: string | null, opt_type_union: string | null, opt_type_union_union2: string | undefined) { + opt_type?: string | null, opt_type_union: string | null, opt_type_union_union2: string | undefined, + min_object: {a: string, b: number}) { console.log(42) } diff --git a/frontend/src/lib/common.ts b/frontend/src/lib/common.ts index e2a54549a261..340535824b21 100644 --- a/frontend/src/lib/common.ts +++ b/frontend/src/lib/common.ts @@ -11,6 +11,7 @@ export interface SchemaProperty { contentEncoding?: 'base64' | 'binary' format?: string items?: { type?: 'string' | 'number' | 'bytes', contentEncoding?: 'base64' }, + properties?: { [name: string]: SchemaProperty } } export type Schema = { diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index 6677c1f14510..410717ef30db 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -19,6 +19,8 @@ import ObjectTypeNarrowing from './ObjectTypeNarrowing.svelte' import ResourcePicker from './ResourcePicker.svelte' import StringTypeNarrowing from './StringTypeNarrowing.svelte' + import SchemaForm from './SchemaForm.svelte' + import type { Schema, SchemaProperty } from '$lib/common' export let label: string = '' export let value: any @@ -41,6 +43,7 @@ | { type?: 'string' | 'number' | 'bytes'; contentEncoding?: 'base64' } | undefined = undefined export let displayHeader = true + export let properties: { [name: string]: SchemaProperty } | undefined = undefined let seeEditable: boolean = enum_ != undefined || pattern != undefined const dispatch = createEventDispatcher() @@ -269,16 +272,25 @@ {:else if inputCat == 'resource-object'} {:else if inputCat == 'object'} -