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 <devtalele0@gmail.com>
This commit is contained in:
hugocasa
2026-01-07 01:42:48 +07:00
committed by GitHub
parent 62bb11e297
commit 7877999f3d
7 changed files with 63 additions and 35 deletions

3
cli/.gitignore vendored
View File

@@ -1,3 +1,4 @@
npm/
gen/
node_modules/
node_modules/
windmill-utils-internal/.npmrc

View File

@@ -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",

View File

@@ -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/**/*"
]
}
}

View File

@@ -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 = [

View File

@@ -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"
]
}
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -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": {

View File

@@ -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",