From fecc8f2effc0cbdcddb3a4ff0202eafb831306cf Mon Sep 17 00:00:00 2001 From: hugocasa Date: Wed, 7 Jan 2026 01:42:48 +0700 Subject: [PATCH] fix(schema): preserve user-defined JSON schema for Python list[dict] parameters (#7496) * fix(schema): preserve user-defined JSON schema for Python list[dict] Fixes issue where JSON schema properties manually defined in the UI are lost when saving Python scripts with list[dict] or untyped array parameters. Changes: - Preserve all items fields (properties, required, additionalProperties, etc.) - Preserve items.type instead of hardcoding "object" - Preserve type for untyped parameters using nullish coalescing - Add type safety check for items preservation The Python parser cannot infer object properties from list[dict] annotations. This fix preserves user-defined schema fields when parser cannot infer structure. Fixes #7209 * fix(schema): preserve all fields for untyped lists, not just properties Address bot feedback for consistency. The untyped list branch now preserves all user-defined fields (required, additionalProperties, enum, etc.) just like the record[] branch, instead of only preserving properties. This ensures users who define required fields or enum values for untyped list parameters don't lose that data on save. Related to #7209 * nits and publish --------- Co-authored-by: Devdatta Talele --- cli/.gitignore | 3 +- cli/windmill-utils-internal/package-lock.json | 4 +- cli/windmill-utils-internal/package.json | 4 +- .../src/parse/parse-schema.ts | 62 ++++++++++++++----- cli/windmill-utils-internal/tsconfig.json | 15 ++--- frontend/package-lock.json | 8 +-- frontend/package.json | 2 +- 7 files changed, 63 insertions(+), 35 deletions(-) diff --git a/cli/.gitignore b/cli/.gitignore index 74d3e102f5..e6c9bd2655 100644 --- a/cli/.gitignore +++ b/cli/.gitignore @@ -1,3 +1,4 @@ npm/ gen/ -node_modules/ \ No newline at end of file +node_modules/ +windmill-utils-internal/.npmrc \ No newline at end of file diff --git a/cli/windmill-utils-internal/package-lock.json b/cli/windmill-utils-internal/package-lock.json index db260b02dc..ddf3437925 100644 --- a/cli/windmill-utils-internal/package-lock.json +++ b/cli/windmill-utils-internal/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-utils-internal", - "version": "1.3.0", + "version": "1.3.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-utils-internal", - "version": "1.3.0", + "version": "1.3.2", "license": "Apache 2.0", "devDependencies": { "@types/node": "^24.2.0", diff --git a/cli/windmill-utils-internal/package.json b/cli/windmill-utils-internal/package.json index ff4418f79b..20f20a4c9a 100644 --- a/cli/windmill-utils-internal/package.json +++ b/cli/windmill-utils-internal/package.json @@ -1,6 +1,6 @@ { "name": "windmill-utils-internal", - "version": "1.3.1", + "version": "1.3.2", "description": "Internal utility functions for Windmill", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -21,4 +21,4 @@ "files": [ "dist/**/*" ] -} \ No newline at end of file +} diff --git a/cli/windmill-utils-internal/src/parse/parse-schema.ts b/cli/windmill-utils-internal/src/parse/parse-schema.ts index 9cd4a596b4..bb2e2078af 100644 --- a/cli/windmill-utils-internal/src/parse/parse-schema.ts +++ b/cli/windmill-utils-internal/src/parse/parse-schema.ts @@ -1,7 +1,10 @@ /** * Type alias for enum values - can be an array of strings or undefined */ -export type EnumType = string[] | { label: string; value: string }[] | undefined; +export type EnumType = + | string[] + | { label: string; value: string }[] + | undefined; /** * Represents a property in a JSON schema with various validation and display options @@ -40,6 +43,16 @@ export interface SchemaProperty { originalType?: string; } +const ITEMS_PRESERVED_FIELDS = [ + "properties", + "required", + "additionalProperties", + "enum", + "resourceType", + "contentEncoding", + "description", +] as (keyof SchemaProperty["items"])[]; + /** * Converts argument signature types to JSON schema properties. * This function handles various Windmill-specific types and converts them @@ -54,22 +67,22 @@ export function argSigToJsonSchemaType( | string | { resource: string | null } | { - list: - | (string | { name?: string; props?: { key: string; typ: any }[] }) - | { str: any } - | { object: { name?: string; props?: { key: string; typ: any }[] } } - | null; - } + list: + | (string | { name?: string; props?: { key: string; typ: any }[] }) + | { str: any } + | { object: { name?: string; props?: { key: string; typ: any }[] } } + | null; + } | { dynselect: string } | { dynmultiselect: string } | { str: string[] | null } | { object: { name?: string; props?: { key: string; typ: any }[] } } | { - oneof: { - label: string; - properties: { key: string; typ: any }[]; - }[]; - }, + oneof: { + label: string; + properties: { key: string; typ: any }[]; + }[]; + }, oldS: SchemaProperty ): void { const newS: SchemaProperty = { type: "" }; @@ -206,15 +219,34 @@ export function argSigToJsonSchemaType( } newS.items = { type: "object", properties: properties }; } else { - newS.items = { type: "object" }; + // Preserve ALL user-defined fields when parser cannot infer structure + newS.items = { type: oldS.items?.type || "object" }; + + if (oldS.items && typeof oldS.items === "object") { + ITEMS_PRESERVED_FIELDS.forEach((field) => { + if (oldS.items && oldS.items[field] !== undefined) { + newS.items![field] = oldS.items[field]; + } + }); + } } newS.originalType = "record[]"; } else { - newS.items = { type: "object" }; + // Preserve ALL user-defined fields for untyped lists (same as record[] branch) + newS.items = { type: oldS.items?.type || "object" }; + + if (oldS.items && typeof oldS.items === "object") { + ITEMS_PRESERVED_FIELDS.forEach((field) => { + if (oldS.items && oldS.items[field] !== undefined) { + newS.items![field] = oldS.items[field]; + } + }); + } newS.originalType = "object[]"; } } else { - newS.type = "object"; + // Preserve existing type when inference fails, default to "object" for undefined/null + newS.type = oldS.type ?? "object"; } const preservedFields = [ diff --git a/cli/windmill-utils-internal/tsconfig.json b/cli/windmill-utils-internal/tsconfig.json index 38f18728f4..35814c1f7a 100644 --- a/cli/windmill-utils-internal/tsconfig.json +++ b/cli/windmill-utils-internal/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "ES2022", - "module": "commonjs", + "module": "ES2022", "lib": ["ES2022"], "declaration": true, "outDir": "./dist", @@ -11,7 +11,7 @@ "noUnusedParameters": false, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "baseUrl": "./", "esModuleInterop": true, "experimentalDecorators": true, @@ -19,11 +19,6 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist" - ] -} \ No newline at end of file + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bb77f1b2a6..3b56a7fc1a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -86,7 +86,7 @@ "windmill-parser-wasm-ts": "1.593.0", "windmill-parser-wasm-yaml": "1.593.0", "windmill-sql-datatype-parser-wasm": "1.512.0", - "windmill-utils-internal": "^1.3.1", + "windmill-utils-internal": "^1.3.2", "xterm": "^5.3.0", "xterm-readline": "^1.1.2", "y-monaco": "^0.1.4", @@ -15893,9 +15893,9 @@ "integrity": "sha512-uHNL8F72/Tf96xF3hOHnPDjkEyqXw7fNjcPJiUhth9sTQkcwUIoJMOdwm8/cs+j9kKVRJ4tgNYMHEBLylazp6g==" }, "node_modules/windmill-utils-internal": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/windmill-utils-internal/-/windmill-utils-internal-1.3.1.tgz", - "integrity": "sha512-afRGUDcvaUfGu7FA6DD0xWECQiKnXADs0N4WyQQ+OvaloxZ4oQzdEpLnVab/m3T02hhs29ru4Ilrdxu3ozyT5Q==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/windmill-utils-internal/-/windmill-utils-internal-1.3.2.tgz", + "integrity": "sha512-sLE7sfmN3vBwPReqKorBrusnn83Qnm8vVFqh/7+kz8mf98ojMVDMgBginjlh9KBtVfWu9NhyjGS+WXRcnWalzA==", "license": "Apache 2.0" }, "node_modules/word-wrap": { diff --git a/frontend/package.json b/frontend/package.json index ece9c3ada4..593a348b8e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -158,7 +158,7 @@ "windmill-parser-wasm-ts": "1.593.0", "windmill-parser-wasm-yaml": "1.593.0", "windmill-sql-datatype-parser-wasm": "1.512.0", - "windmill-utils-internal": "^1.3.1", + "windmill-utils-internal": "^1.3.2", "xterm": "^5.3.0", "xterm-readline": "^1.1.2", "y-monaco": "^0.1.4",