mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 08:01:35 +00:00
* feat: give dbt its own editor with an explicitly refreshed model graph Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: bump ee ref for the agent-worker dbt editor graph Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: scope editor graph retention by principal, carry parse context, honor nlang Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: bump ee ref Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the dbt editor's model graph and log panel mounted across tabs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep the dbt_edge to dbt_node joins on an index-usable equality Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: poll a parse until the job ends, resolve the project key, correct the docs Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: surface a slow parse's job, bound poll failures, drop banned bindable defaults Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: hide the dbt Generated UI content, not only its tab Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: honor disabled Triggers in the dbt tab fallback, record permissioned_as Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: one dbt pane with the run drawn on the models, and a full-height script graph Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: move the dbt build arguments behind the Build button Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: trim the dbt editor toolbar and stop the graph asserting a cause it lacks Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: mark dbt as alpha in the language picker and announce it once Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: trim the dbt alpha notice Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * feat: give a selected dbt model the whole detail section, with a close that deselects Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: close the dbt detail panel by clicking away, and make its close obvious Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: cache the agent-worker dbt query, which needs the private feature to compile Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: never fall back to a settings tab the embedder disabled Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: preview dbt rows from the same project the graph was parsed from * feat: hide the script-kind selector for dbt projects * fix: pin a dbt row preview to the project its graph was parsed from * fix: pin a dbt row preview to the arguments its graph was parsed under * fix: keep dbt preview placeholders live while its vars stay pinned * fix: report a warehouse-less dbt parse's counts and flag stale preview args * fix: tell the pinned-vars case apart from a stale placeholder * chore: update ee-repo-ref to 59044635769f18f8ff5073236cfc7b5f41e917cc This commit updates the EE repository reference after PR #707 was merged in windmill-ee-private. Previous ee-repo-ref: 7e424384cdd4cef8653b55b04f17ad3f801bc50c New ee-repo-ref: 59044635769f18f8ff5073236cfc7b5f41e917cc Automated by sync-ee-ref workflow. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
285 lines
7.2 KiB
TypeScript
285 lines
7.2 KiB
TypeScript
import { get } from 'svelte/store'
|
|
import { base } from '$lib/base'
|
|
import type { Schema, SupportedLanguage } from './common'
|
|
import { FlowService, type Script, ScriptService, ScheduleService } from './gen'
|
|
import { hubBaseUrlStore, workspaceStore } from './stores'
|
|
import { getHubFlowIdFromPath } from './utils'
|
|
|
|
export function scriptLangToEditorLang(
|
|
lang:
|
|
| Script['language']
|
|
| 'bunnative'
|
|
| 'javascript'
|
|
| 'frontend'
|
|
| 'jsx'
|
|
| 'tsx'
|
|
| 'text'
|
|
| 'json'
|
|
| undefined
|
|
) {
|
|
if (lang == 'deno') {
|
|
return 'typescript'
|
|
} else if (lang == 'bun' || lang == 'bunnative' || lang == 'frontend' || lang == 'tsx') {
|
|
return 'typescript'
|
|
} else if (lang == 'nativets') {
|
|
return 'typescript'
|
|
} else if (lang == 'text') {
|
|
return 'text'
|
|
} else if (lang == 'javascript' || lang == 'jsx') {
|
|
return 'javascript'
|
|
} else if (lang == 'postgresql') {
|
|
return 'sql'
|
|
} else if (lang == 'mysql') {
|
|
return 'sql'
|
|
} else if (lang == 'bigquery') {
|
|
return 'sql'
|
|
} else if (lang == 'oracledb') {
|
|
return 'sql'
|
|
} else if (lang == 'snowflake') {
|
|
return 'sql'
|
|
} else if (lang == 'mssql') {
|
|
return 'sql'
|
|
} else if (lang == 'duckdb') {
|
|
return 'sql'
|
|
} else if (lang == 'python3') {
|
|
return 'python'
|
|
} else if (lang == 'bash') {
|
|
return 'shell'
|
|
} else if (lang == 'powershell') {
|
|
return 'powershell'
|
|
} else if (lang == 'php') {
|
|
return 'php'
|
|
} else if (lang == 'rust') {
|
|
return 'rust'
|
|
} else if (lang == 'graphql') {
|
|
return 'graphql'
|
|
} else if (lang == 'ansible') {
|
|
return 'yaml'
|
|
} else if (lang == 'csharp') {
|
|
return 'csharp'
|
|
} else if (lang == 'nu') {
|
|
return 'nu'
|
|
} else if (lang == 'java') {
|
|
return 'java'
|
|
} else if (lang == 'rlang') {
|
|
return 'r'
|
|
} else if (lang == 'dbt') {
|
|
// the script content is the YAML descriptor; the project's own files ride
|
|
// with it as its module bundle
|
|
return 'yaml'
|
|
// for related places search: ADD_NEW_LANG
|
|
} else if (lang == undefined) {
|
|
return 'typescript'
|
|
} else {
|
|
return lang
|
|
}
|
|
}
|
|
|
|
export function extToScriptLang(lang: string): 'bun' | 'python3' | undefined {
|
|
switch (lang) {
|
|
case 'ts':
|
|
return 'bun'
|
|
case 'py':
|
|
return 'python3'
|
|
}
|
|
return undefined
|
|
}
|
|
export type ScriptSchedule = {
|
|
summary: string | undefined
|
|
args: Record<string, any>
|
|
cron: string
|
|
timezone: string
|
|
enabled: boolean
|
|
}
|
|
|
|
// Load the schedule of a flow given its path and the workspace
|
|
export async function loadScriptSchedule(
|
|
path: string,
|
|
workspace: string
|
|
): Promise<ScriptSchedule | undefined> {
|
|
const existsSchedule = await ScheduleService.existsSchedule({
|
|
workspace,
|
|
path
|
|
})
|
|
|
|
if (!existsSchedule) {
|
|
return undefined
|
|
}
|
|
|
|
const schedule = await ScheduleService.getSchedule({
|
|
workspace,
|
|
path
|
|
})
|
|
|
|
return {
|
|
summary: schedule.summary ?? undefined,
|
|
enabled: schedule.enabled,
|
|
cron: schedule.schedule,
|
|
timezone: schedule.timezone,
|
|
args: schedule.args ?? {}
|
|
}
|
|
}
|
|
|
|
export async function loadSchemaFlow(
|
|
path: string,
|
|
// The acting workspace when the flow editor runs in an AI session; else the nav workspace.
|
|
workspace?: string
|
|
): Promise<Schema> {
|
|
const flow = await FlowService.getFlowByPath({
|
|
workspace: workspace ?? get(workspaceStore)!,
|
|
path: path ?? ''
|
|
})
|
|
return flow.schema as any
|
|
}
|
|
|
|
export function scriptPathToHref(path: string, hubBaseUrl: string): string {
|
|
if (path.startsWith('hub/')) {
|
|
return hubBaseUrl + '/from_version/' + path.substring(4)
|
|
} else {
|
|
return `${base}/scripts/get/${path}?workspace=${get(workspaceStore)}`
|
|
}
|
|
}
|
|
|
|
export function flowPathToHref(path: string, hubBaseUrl: string = get(hubBaseUrlStore)): string {
|
|
if (path.startsWith('hub/flows/')) {
|
|
const hubFlowId = getHubFlowIdFromPath(path)
|
|
return hubFlowId ? `${hubBaseUrl}/flows/${hubFlowId}` : hubBaseUrl
|
|
}
|
|
|
|
return `${base}/flows/get/${path}?workspace=${get(workspaceStore)}`
|
|
}
|
|
|
|
const scriptLanguagesArray: [SupportedLanguage | 'docker' | 'bunnative', string][] = [
|
|
['bun', 'TypeScript (Bun)'],
|
|
['python3', 'Python'],
|
|
['deno', 'TypeScript (Deno)'],
|
|
['bash', 'Bash'],
|
|
['go', 'Go'],
|
|
['nativets', 'REST'],
|
|
['bunnative', 'REST'],
|
|
['postgresql', 'PostgreSQL'],
|
|
['mysql', 'MySQL'],
|
|
['bigquery', 'BigQuery'],
|
|
['oracledb', 'Oracle Database'],
|
|
['snowflake', 'Snowflake'],
|
|
['mssql', 'MS SQL Server'],
|
|
['graphql', 'GraphQL'],
|
|
['powershell', 'PowerShell'],
|
|
['php', 'PHP'],
|
|
['rust', 'Rust'],
|
|
['ansible', 'Ansible'],
|
|
['csharp', 'C#'],
|
|
['docker', 'Docker'],
|
|
['nu', 'Nu'],
|
|
['java', 'Java'],
|
|
['duckdb', 'DuckDB'],
|
|
['ruby', 'Ruby'],
|
|
['rlang', 'R'],
|
|
['dbt', 'dbt']
|
|
// for related places search: ADD_NEW_LANG
|
|
]
|
|
/**
|
|
* Languages a MODULE-LESS script cannot be written in.
|
|
*
|
|
* A dbt script IS its module bundle — `dbt_project.yml` and the models — and a
|
|
* flow step's or an app's inline script is a raw body with nowhere to carry
|
|
* one, so choosing it there produces a job that fails when the worker looks for
|
|
* the project. A flow reaches dbt the same way it reaches any other script: by
|
|
* path, to a deployed one.
|
|
*/
|
|
const LANGS_NEEDING_MODULES = ['dbt']
|
|
|
|
/** `processLangs` for a surface whose scripts have no module bundle. */
|
|
export function processInlineLangs(selected: string | undefined, langs: string[]): string[] {
|
|
return processLangs(selected, langs).filter((l) => !LANGS_NEEDING_MODULES.includes(l))
|
|
}
|
|
|
|
export function processLangs(selected: string | undefined, langs: string[]): string[] {
|
|
if (selected === 'nativets') {
|
|
return langs
|
|
} else {
|
|
let ls = langs.filter((lang) => lang !== 'nativets')
|
|
|
|
//those languages are newer and may not be in the saved list
|
|
let nl = [
|
|
'bunnative',
|
|
'rust',
|
|
'ansible',
|
|
'csharp',
|
|
'nu',
|
|
'java',
|
|
'duckdb',
|
|
'ruby',
|
|
'rlang',
|
|
'dbt'
|
|
]
|
|
// for related places search: ADD_NEW_LANG
|
|
nl.forEach((lang) => {
|
|
if (!ls.includes(lang)) {
|
|
ls.push(lang)
|
|
}
|
|
})
|
|
return ls
|
|
}
|
|
}
|
|
|
|
export const defaultScriptLanguages = Object.fromEntries(scriptLanguagesArray)
|
|
|
|
export async function getScriptByPath(
|
|
path: string,
|
|
// The acting workspace when called from a session live editor; defaults to
|
|
// the navigation workspace for full-page callers.
|
|
workspace?: string
|
|
): Promise<{
|
|
content: string
|
|
language: SupportedLanguage
|
|
schema: any
|
|
description: string
|
|
tag: string | undefined
|
|
concurrent_limit: number | undefined
|
|
concurrency_time_window_s: number | undefined
|
|
lock?: string
|
|
created_at?: string
|
|
hash?: string
|
|
}> {
|
|
if (path.startsWith('hub/')) {
|
|
const { content, language, schema, lockfile } = await ScriptService.getHubScriptByPath({ path })
|
|
|
|
return {
|
|
content,
|
|
language: language as SupportedLanguage,
|
|
schema,
|
|
description: '',
|
|
tag: undefined,
|
|
concurrent_limit: undefined,
|
|
concurrency_time_window_s: undefined,
|
|
lock: lockfile
|
|
}
|
|
} else {
|
|
const script = await ScriptService.getScriptByPath({
|
|
workspace: workspace ?? get(workspaceStore)!,
|
|
path: path ?? ''
|
|
})
|
|
return {
|
|
content: script.content,
|
|
language: script.language,
|
|
schema: script.schema,
|
|
description: script.description,
|
|
tag: script.tag,
|
|
concurrent_limit: script.concurrent_limit,
|
|
concurrency_time_window_s: script.concurrency_time_window_s,
|
|
lock: script.lock,
|
|
hash: script.hash,
|
|
created_at: script.created_at
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function getLatestHashForScript(path: string, workspace?: string): Promise<string> {
|
|
const script = await ScriptService.getScriptByPath({
|
|
workspace: workspace ?? get(workspaceStore)!,
|
|
path: path ?? ''
|
|
})
|
|
return script.hash
|
|
}
|