mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-23 08:00:45 +00:00
fix: hide draft only items in pickers and from ops (#3977)
* fix: hide draft only scripts to ops and in pickers/flow builder * fix: default to hiding draft only scripts * fix: hide script drafts from ops * fix: generalize to hide draft only/no main func by default * fix: archived openapi nit
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -13,4 +13,5 @@ pub struct ListAppQuery {
|
||||
pub starred_only: Option<bool>,
|
||||
pub path_exact: Option<String>,
|
||||
pub path_start: Option<String>,
|
||||
pub include_draft_only: Option<bool>,
|
||||
}
|
||||
|
||||
@@ -551,6 +551,7 @@ pub struct ListFlowQuery {
|
||||
pub order_by: Option<String>,
|
||||
pub order_desc: Option<bool>,
|
||||
pub starred_only: Option<bool>,
|
||||
pub include_draft_only: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn add_virtual_items_if_necessary(modules: &mut Vec<FlowModule>) {
|
||||
|
||||
@@ -336,7 +336,8 @@ pub struct ListScriptQuery {
|
||||
pub is_template: Option<bool>,
|
||||
pub kinds: Option<String>,
|
||||
pub starred_only: Option<bool>,
|
||||
pub hide_without_main: Option<bool>,
|
||||
pub include_without_main: Option<bool>,
|
||||
pub include_draft_only: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn to_i64(s: &str) -> crate::error::Result<i64> {
|
||||
|
||||
@@ -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,
|
||||
|
||||
+2
-1
@@ -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);
|
||||
|
||||
+4
-1
@@ -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);
|
||||
|
||||
+9
-1
@@ -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);
|
||||
|
||||
@@ -72,11 +72,12 @@
|
||||
|
||||
let nbDisplayed = 15
|
||||
|
||||
async function loadScripts(hideWithoutMain: boolean): Promise<void> {
|
||||
async function loadScripts(includeWithoutMain: boolean): Promise<void> {
|
||||
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<void> {
|
||||
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 @@
|
||||
<div>
|
||||
<span class="text-sm font-semibold">Filters</span>
|
||||
<div class="flex flex-col gap-2 mt-2">
|
||||
<Toggle size="xs" bind:checked={archived} options={{ right: 'Show archived' }} />
|
||||
<Toggle size="xs" bind:checked={archived} options={{ right: 'Only archived' }} />
|
||||
{#if $userStore && !$userStore.operator}
|
||||
<Toggle
|
||||
size="xs"
|
||||
bind:checked={hideWithoutMain}
|
||||
options={{ right: 'Hide without main function' }}
|
||||
bind:checked={includeWithoutMain}
|
||||
options={{ right: 'Include without main function' }}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -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)}
|
||||
<Item
|
||||
{item}
|
||||
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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ?? '',
|
||||
|
||||
Reference in New Issue
Block a user