mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 16:02:19 +00:00
refactor: simplify script draft field preservation
This commit is contained in:
@@ -52,6 +52,7 @@ vi.mock('$lib/gen', async () => {
|
||||
...actual,
|
||||
ScriptService: wrapService(actual.ScriptService, {
|
||||
existsScriptByPath: vi.fn(async () => false),
|
||||
getScriptByPath: vi.fn(),
|
||||
listScripts: vi.fn(async () => [])
|
||||
}),
|
||||
FlowService: wrapService(actual.FlowService, {
|
||||
@@ -122,6 +123,7 @@ vi.mock('$lib/gen', async () => {
|
||||
import { globalTools, prepareGlobalUserMessage } from './core'
|
||||
import { deleteGlobalDraft, listGlobalDrafts } from './userDraftAdapter'
|
||||
import { UserDraft, __resetUserDraftForTesting } from '$lib/userDraft.svelte'
|
||||
import { ScriptService } from '$lib/gen'
|
||||
import type { Tool, ToolCallbacks } from '../shared'
|
||||
|
||||
const WORKSPACE = 'global-core-test'
|
||||
@@ -336,6 +338,75 @@ describe('global AI tools', () => {
|
||||
expect(localStorage.getItem(`userdraft/w/${WORKSPACE}/script/f/scripts/live-ai`)).not.toBeNull()
|
||||
})
|
||||
|
||||
it('preserves NewScript metadata from existing scripts without copying backend read fields', async () => {
|
||||
vi.mocked(ScriptService.existsScriptByPath).mockResolvedValueOnce(true)
|
||||
vi.mocked(ScriptService.getScriptByPath).mockResolvedValueOnce({
|
||||
workspace_id: WORKSPACE,
|
||||
hash: 'old-hash',
|
||||
path: 'f/scripts/existing',
|
||||
parent_hashes: ['older-hash'],
|
||||
summary: 'Existing script',
|
||||
description: 'Existing description',
|
||||
content: 'export async function main() { return 1 }',
|
||||
created_by: 'admin',
|
||||
created_at: '2026-01-01T00:00:00Z',
|
||||
archived: false,
|
||||
deleted: false,
|
||||
extra_perms: {},
|
||||
is_template: true,
|
||||
language: 'bun',
|
||||
kind: 'script',
|
||||
starred: true,
|
||||
has_draft: true,
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
}
|
||||
},
|
||||
concurrent_limit: 3,
|
||||
cache_ttl: 30,
|
||||
cache_ignore_s3_path: true,
|
||||
has_preprocessor: true,
|
||||
assets: [{ path: 'asset.txt', kind: 's3object' }],
|
||||
labels: ['important']
|
||||
} as any)
|
||||
|
||||
await callGlobalTool('write_script', {
|
||||
path: 'f/scripts/existing',
|
||||
summary: 'AI update',
|
||||
language: 'python3',
|
||||
content: 'def main():\n return 2'
|
||||
})
|
||||
|
||||
const draft = UserDraft.get<any>('script', 'f/scripts/existing', { workspace: WORKSPACE })
|
||||
expect(draft).toMatchObject({
|
||||
path: 'f/scripts/existing',
|
||||
parent_hash: 'old-hash',
|
||||
summary: 'AI update',
|
||||
description: 'Existing description',
|
||||
content: 'def main():\n return 2',
|
||||
language: 'python3',
|
||||
is_template: true,
|
||||
schema: {
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
}
|
||||
},
|
||||
concurrent_limit: 3,
|
||||
cache_ttl: 30,
|
||||
cache_ignore_s3_path: true,
|
||||
has_preprocessor: true,
|
||||
assets: [{ path: 'asset.txt', kind: 's3object' }],
|
||||
labels: ['important']
|
||||
})
|
||||
expect(draft).not.toHaveProperty('hash')
|
||||
expect(draft).not.toHaveProperty('workspace_id')
|
||||
expect(draft).not.toHaveProperty('created_by')
|
||||
expect(draft).not.toHaveProperty('starred')
|
||||
expect(draft).not.toHaveProperty('extra_perms')
|
||||
})
|
||||
|
||||
it('deleteGlobalDraft clears both persisted storage and any live handle state', async () => {
|
||||
const handle = UserDraft.use<any>('script', 'f/scripts/delete-live', { workspace: WORKSPACE })
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
VariableService,
|
||||
WebsocketTriggerService
|
||||
} from '$lib/gen'
|
||||
import { $ScriptLang } from '$lib/gen/schemas.gen'
|
||||
import { $NewScript, $ScriptLang } from '$lib/gen/schemas.gen'
|
||||
import type {
|
||||
AppWithLastVersion,
|
||||
Flow,
|
||||
@@ -1720,65 +1720,37 @@ function cloneIfDefined<T>(value: T | undefined): T | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
function buildScriptDraftFromBase(
|
||||
base: Partial<NewScript>,
|
||||
args: { path: string; summary?: string; language: ScriptLang; content: string },
|
||||
overrides: Partial<NewScript> = {}
|
||||
): NewScript {
|
||||
const draft: NewScript = {
|
||||
path: args.path,
|
||||
summary: args.summary ?? base.summary ?? '',
|
||||
description: base.description ?? '',
|
||||
content: args.content,
|
||||
schema: cloneIfDefined(base.schema) ?? emptySchema(),
|
||||
is_template: base.is_template ?? false,
|
||||
language: args.language,
|
||||
kind: base.kind ?? 'script'
|
||||
}
|
||||
const newScriptKeys = new Set(Object.keys($NewScript.properties) as Array<keyof NewScript>)
|
||||
|
||||
const optionalKeys: (keyof NewScript)[] = [
|
||||
'parent_hash',
|
||||
'lock',
|
||||
'tag',
|
||||
'draft_only',
|
||||
'envs',
|
||||
'concurrent_limit',
|
||||
'concurrency_time_window_s',
|
||||
'cache_ttl',
|
||||
'cache_ignore_s3_path',
|
||||
'dedicated_worker',
|
||||
'ws_error_handler_muted',
|
||||
'priority',
|
||||
'restart_unless_cancelled',
|
||||
'timeout',
|
||||
'delete_after_secs',
|
||||
'deployment_message',
|
||||
'concurrency_key',
|
||||
'debounce_key',
|
||||
'debounce_delay_s',
|
||||
'debounce_args_to_accumulate',
|
||||
'max_total_debouncing_time',
|
||||
'max_total_debounces_amount',
|
||||
'visible_to_runner_only',
|
||||
'auto_kind',
|
||||
'codebase',
|
||||
'has_preprocessor',
|
||||
'on_behalf_of_email',
|
||||
'preserve_on_behalf_of',
|
||||
'assets',
|
||||
'modules',
|
||||
'labels'
|
||||
]
|
||||
|
||||
for (const key of optionalKeys) {
|
||||
const cloned = cloneIfDefined(base[key] as never)
|
||||
function pickNewScriptFields(base: Partial<NewScript> & Partial<Script>): Partial<NewScript> {
|
||||
const draft: Partial<NewScript> = {}
|
||||
for (const key of Object.keys(base) as Array<keyof NewScript>) {
|
||||
if (!newScriptKeys.has(key)) continue
|
||||
const cloned = cloneIfDefined(base[key])
|
||||
if (cloned !== undefined) {
|
||||
;(draft as Record<string, unknown>)[key] = cloned
|
||||
}
|
||||
}
|
||||
return draft
|
||||
}
|
||||
|
||||
function buildScriptDraftFromBase(
|
||||
base: Partial<NewScript> & Partial<Script>,
|
||||
args: { path: string; summary?: string; language: ScriptLang; content: string },
|
||||
overrides: Partial<NewScript> = {}
|
||||
): NewScript {
|
||||
const draftBase = pickNewScriptFields(base)
|
||||
|
||||
return {
|
||||
...draft,
|
||||
...draftBase,
|
||||
path: args.path,
|
||||
summary: args.summary ?? draftBase.summary ?? '',
|
||||
description: draftBase.description ?? '',
|
||||
content: args.content,
|
||||
schema: draftBase.schema ?? emptySchema(),
|
||||
language: args.language,
|
||||
kind: draftBase.kind ?? 'script',
|
||||
is_template: draftBase.is_template ?? false,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user