diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 93a6017605..c36d408ab0 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -3502,17 +3502,24 @@ paths: - name: show_archived description: | (default false) - show also the archived files. + show only the archived files. when multiple archived hash share the same path, only the ones with the latest create_at are ed. in: query schema: type: boolean - - name: hide_without_main + - name: include_without_main description: | (default false) - hide the scripts without an exported main function + include scripts without an exported main function + in: query + schema: + type: boolean + - name: include_draft_only + description: | + (default false) + include scripts that have no deployed version in: query schema: type: boolean @@ -4261,7 +4268,7 @@ paths: - name: show_archived description: | (default false) - show also the archived files. + show only the archived files. when multiple archived hash share the same path, only the ones with the latest create_at are displayed. in: query @@ -4274,6 +4281,13 @@ paths: in: query schema: type: boolean + - name: include_draft_only + description: | + (default false) + include items that have no deployed version + in: query + schema: + type: boolean responses: "200": description: All flow @@ -4627,6 +4641,13 @@ paths: in: query schema: type: boolean + - name: include_draft_only + description: | + (default false) + include items that have no deployed version + in: query + schema: + type: boolean responses: "200": description: All apps diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index 522c16263b..675178a660 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -287,6 +287,10 @@ async fn list_apps( sqlb.and_where_eq("app.path", "?".bind(path_exact)); } + if !lq.include_draft_only.unwrap_or(false) || authed.is_operator { + sqlb.and_where("app.draft_only IS NOT TRUE"); + } + let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; let rows = sqlx::query_as::<_, ListableApp>(&sql) diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index dd3cbb3cbd..ecf7faac1f 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -155,6 +155,10 @@ async fn list_flows( sqlb.and_where_is_not_null("favorite.path"); } + if !lq.include_draft_only.unwrap_or(false) || authed.is_operator { + sqlb.and_where("o.draft_only IS NOT TRUE"); + } + let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; let rows = sqlx::query_as::<_, ListableFlow>(&sql) diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index c883359e24..8656ace619 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -226,10 +226,14 @@ async fn list_scripts( .limit(per_page) .clone(); - if authed.is_operator || lq.hide_without_main.unwrap_or(false) { + if !lq.include_without_main.unwrap_or(false) || authed.is_operator { sqlb.and_where("o.no_main_func IS NOT TRUE"); } + if !lq.include_draft_only.unwrap_or(false) || authed.is_operator { + sqlb.and_where("draft_only IS NOT TRUE"); + } + if lq.show_archived.unwrap_or(false) { sqlb.and_where_eq( "o.created_at", diff --git a/backend/windmill-common/src/apps.rs b/backend/windmill-common/src/apps.rs index 26218c6c66..fb7499203b 100644 --- a/backend/windmill-common/src/apps.rs +++ b/backend/windmill-common/src/apps.rs @@ -13,4 +13,5 @@ pub struct ListAppQuery { pub starred_only: Option, pub path_exact: Option, pub path_start: Option, + pub include_draft_only: Option, } diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index f9583cf99a..37473b333a 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -551,6 +551,7 @@ pub struct ListFlowQuery { pub order_by: Option, pub order_desc: Option, pub starred_only: Option, + pub include_draft_only: Option, } pub fn add_virtual_items_if_necessary(modules: &mut Vec) { diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs index de055f1827..bd1f2b0abc 100644 --- a/backend/windmill-common/src/scripts.rs +++ b/backend/windmill-common/src/scripts.rs @@ -336,7 +336,8 @@ pub struct ListScriptQuery { pub is_template: Option, pub kinds: Option, pub starred_only: Option, - pub hide_without_main: Option, + pub include_without_main: Option, + pub include_draft_only: Option, } pub fn to_i64(s: &str) -> crate::error::Result { diff --git a/backend/windmill-worker/src/windmill-client.js b/backend/windmill-worker/src/windmill-client.js index ea13a6e43c..64281fede9 100644 --- a/backend/windmill-worker/src/windmill-client.js +++ b/backend/windmill-worker/src/windmill-client.js @@ -6621,8 +6621,8 @@ var ScriptService = class { * are * ed. * - * @param data.hideWithoutMain (default false) - * hide the scripts without an exported main function + * @param data.includeWithoutMain (default false) + * include scripts without an exported main function * * @param data.isTemplate (default regardless) * if true show only the templates @@ -6656,7 +6656,7 @@ var ScriptService = class { last_parent_hash: data.lastParentHash, parent_hash: data.parentHash, show_archived: data.showArchived, - hide_without_main: data.hideWithoutMain, + includeWithoutMain: data.includeWithoutMain, is_template: data.isTemplate, kinds: data.kinds, starred_only: data.starredOnly, diff --git a/cli/apps.ts b/cli/apps.ts index 73912bf981..d9d172654b 100644 --- a/cli/apps.ts +++ b/cli/apps.ts @@ -104,7 +104,7 @@ export async function pushApp( } } -async function list(opts: GlobalOptions) { +async function list(opts: GlobalOptions & { includeDraftOnly?: boolean }) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -116,6 +116,7 @@ async function list(opts: GlobalOptions) { workspace: workspace.workspaceId, page, perPage, + includeDraftOnly: opts.includeDraftOnly ?? false, }); page += 1; total.push(...res); diff --git a/cli/flow.ts b/cli/flow.ts index cb11133206..28e4f5911f 100644 --- a/cli/flow.ts +++ b/cli/flow.ts @@ -139,7 +139,9 @@ async function push(opts: Options, filePath: string, remotePath: string) { log.info(colors.bold.underline.green("Flow pushed")); } -async function list(opts: GlobalOptions & { showArchived?: boolean }) { +async function list( + opts: GlobalOptions & { showArchived?: boolean; includeDraftOnly?: boolean } +) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -152,6 +154,7 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) { page, perPage, showArchived: opts.showArchived ?? false, + includeDraftOnly: opts.includeDraftOnly ?? false, }); page += 1; total.push(...res); diff --git a/cli/script.ts b/cli/script.ts index 02442c7b98..8f52d72e8c 100644 --- a/cli/script.ts +++ b/cli/script.ts @@ -440,7 +440,13 @@ export function removeExtensionToPath(path: string): string { throw new Error("Invalid extension: " + path); } -async function list(opts: GlobalOptions & { showArchived?: boolean }) { +async function list( + opts: GlobalOptions & { + showArchived?: boolean; + includeWithoutMain?: boolean; + includeDraftOnly?: boolean; + } +) { const workspace = await resolveWorkspace(opts); await requireLogin(opts); @@ -453,6 +459,8 @@ async function list(opts: GlobalOptions & { showArchived?: boolean }) { page, perPage, showArchived: opts.showArchived ?? false, + includeWithoutMain: opts.includeWithoutMain ?? false, + includeDraftOnly: opts.includeDraftOnly ?? true, }); page += 1; total.push(...res); diff --git a/frontend/src/lib/components/home/ItemsList.svelte b/frontend/src/lib/components/home/ItemsList.svelte index 39876d44b9..6e60e052ef 100644 --- a/frontend/src/lib/components/home/ItemsList.svelte +++ b/frontend/src/lib/components/home/ItemsList.svelte @@ -72,11 +72,12 @@ let nbDisplayed = 15 - async function loadScripts(hideWithoutMain: boolean): Promise { + async function loadScripts(includeWithoutMain: boolean): Promise { const loadedScripts = await ScriptService.listScripts({ workspace: $workspaceStore!, showArchived: archived ? true : undefined, - hideWithoutMain: hideWithoutMain ? true : undefined + includeWithoutMain: includeWithoutMain ? true : undefined, + includeDraftOnly: true }) scripts = loadedScripts.map((script: Script) => { @@ -92,7 +93,8 @@ flows = ( await FlowService.listFlows({ workspace: $workspaceStore!, - showArchived: archived ? true : undefined + showArchived: archived ? true : undefined, + includeDraftOnly: true }) ).map((x: Flow) => { return { @@ -107,15 +109,17 @@ } async function loadApps(): Promise { - apps = (await AppService.listApps({ workspace: $workspaceStore! })).map((app: ListableApp) => { - return { - canWrite: - canWrite(app.path!, app.extra_perms!, $userStore) && - app.workspace_id == $workspaceStore && - !$userStore?.operator, - ...app + apps = (await AppService.listApps({ workspace: $workspaceStore!, includeDraftOnly: true })).map( + (app: ListableApp) => { + return { + canWrite: + canWrite(app.path!, app.extra_perms!, $userStore) && + app.workspace_id == $workspaceStore && + !$userStore?.operator, + ...app + } } - }) + ) loading = false } @@ -201,7 +205,7 @@ $: { if ($userStore && $workspaceStore) { - loadScripts(hideWithoutMain) + loadScripts(includeWithoutMain) loadFlows() if (!archived) { loadApps() @@ -274,14 +278,16 @@ const TREE_VIEW_SETTING_NAME = 'treeView' const FILTER_USER_FOLDER_SETTING_NAME = 'filterUserFolders' - const HIDE_WITHOUT_MAIN_SETTING = 'hideWithoutMain' + const INCLUDE_WITHOUT_MAIN_SETTING_NAME = 'includeWithoutMain' let treeView = getLocalSetting(TREE_VIEW_SETTING_NAME) == 'true' let filterUserFolders = getLocalSetting(FILTER_USER_FOLDER_SETTING_NAME) == 'true' - let hideWithoutMain = getLocalSetting(HIDE_WITHOUT_MAIN_SETTING) == 'true' + let includeWithoutMain = getLocalSetting(INCLUDE_WITHOUT_MAIN_SETTING_NAME) + ? getLocalSetting(INCLUDE_WITHOUT_MAIN_SETTING_NAME) == 'true' + : true $: storeLocalSetting(TREE_VIEW_SETTING_NAME, treeView ? 'true' : undefined) $: storeLocalSetting(FILTER_USER_FOLDER_SETTING_NAME, filterUserFolders ? 'true' : undefined) - $: storeLocalSetting(HIDE_WITHOUT_MAIN_SETTING, hideWithoutMain ? 'true' : undefined) + $: storeLocalSetting(INCLUDE_WITHOUT_MAIN_SETTING_NAME, includeWithoutMain ? 'true' : undefined) let contentSearch: ContentSearch @@ -436,12 +442,12 @@
Filters
- + {#if $userStore && !$userStore.operator} {/if}
@@ -493,12 +499,12 @@ {nbDisplayed} {collapseAll} isSearching={filter !== ''} - on:scriptChanged={() => loadScripts(hideWithoutMain)} + on:scriptChanged={() => loadScripts(includeWithoutMain)} on:flowChanged={loadFlows} on:appChanged={loadApps} on:rawAppChanged={loadRawApps} on:reload={() => { - loadScripts(hideWithoutMain) + loadScripts(includeWithoutMain) loadFlows() loadApps() loadRawApps() @@ -510,12 +516,12 @@ {#each (items ?? []).slice(0, nbDisplayed) as item (item.type + '/' + item.path)} loadScripts(hideWithoutMain)} + on:scriptChanged={() => loadScripts(includeWithoutMain)} on:flowChanged={loadFlows} on:appChanged={loadApps} on:rawAppChanged={loadRawApps} on:reload={() => { - loadScripts(hideWithoutMain) + loadScripts(includeWithoutMain) loadFlows() loadApps() loadRawApps() diff --git a/frontend/src/lib/windmill_fetch.d.ts.txt b/frontend/src/lib/windmill_fetch.d.ts.txt index cef24769c0..7140c89545 100644 --- a/frontend/src/lib/windmill_fetch.d.ts.txt +++ b/frontend/src/lib/windmill_fetch.d.ts.txt @@ -6017,10 +6017,10 @@ export type ListScriptsData = { firstParentHash?: string; /** * (default false) - * hide the scripts without an exported main function + * include scripts without an exported main function * */ - hideWithoutMain?: boolean; + includeWithoutMain?: boolean; /** * (default regardless) * if true show only the templates @@ -10651,10 +10651,10 @@ export type $OpenApiTs = { firstParentHash?: string; /** * (default false) - * hide the scripts without an exported main function + * include scripts without an exported main function * */ - hideWithoutMain?: boolean; + includeWithoutMain?: boolean; /** * (default regardless) * if true show only the templates @@ -15268,8 +15268,8 @@ export declare class ScriptService { * are * ed. * - * @param data.hideWithoutMain (default false) - * hide the scripts without an exported main function + * @param data.includeWithoutMain (default false) + * include scripts without an exported main function * * @param data.isTemplate (default regardless) * if true show only the templates diff --git a/frontend/src/routes/(root)/(logged)/+layout.svelte b/frontend/src/routes/(root)/(logged)/+layout.svelte index e39c9b4dd0..8614337f49 100644 --- a/frontend/src/routes/(root)/(logged)/+layout.svelte +++ b/frontend/src/routes/(root)/(logged)/+layout.svelte @@ -136,7 +136,8 @@ async function loadFavorites() { const scripts = await ScriptService.listScripts({ workspace: $workspaceStore ?? '', - starredOnly: true + starredOnly: true, + includeWithoutMain: true }) const flows = await FlowService.listFlows({ workspace: $workspaceStore ?? '',