diff --git a/backend/.sqlx/query-644335d376b6554ab222d25dfa8722234661f2456b469f21b175e8607584614e.json b/backend/.sqlx/query-644335d376b6554ab222d25dfa8722234661f2456b469f21b175e8607584614e.json new file mode 100644 index 0000000000..a1e4915e40 --- /dev/null +++ b/backend/.sqlx/query-644335d376b6554ab222d25dfa8722234661f2456b469f21b175e8607584614e.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT path, app_version.value from app LEFT JOIN app_version ON app_version.id = versions[array_upper(versions, 1)] WHERE workspace_id = $1 LIMIT $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "value", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "644335d376b6554ab222d25dfa8722234661f2456b469f21b175e8607584614e" +} diff --git a/backend/.sqlx/query-a2bc43114a2fb17fb62af19c34d8d5787ea304d1e8ef51f2c3d7b9882fdd0108.json b/backend/.sqlx/query-a2bc43114a2fb17fb62af19c34d8d5787ea304d1e8ef51f2c3d7b9882fdd0108.json new file mode 100644 index 0000000000..378580ab86 --- /dev/null +++ b/backend/.sqlx/query-a2bc43114a2fb17fb62af19c34d8d5787ea304d1e8ef51f2c3d7b9882fdd0108.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT path, content from script WHERE workspace_id = $1 AND archived = false LIMIT $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "content", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "a2bc43114a2fb17fb62af19c34d8d5787ea304d1e8ef51f2c3d7b9882fdd0108" +} diff --git a/backend/.sqlx/query-efbd9dc28ab5e53d070684b2e99d945872791171a1c8cef6088ea662f3b2cebb.json b/backend/.sqlx/query-efbd9dc28ab5e53d070684b2e99d945872791171a1c8cef6088ea662f3b2cebb.json new file mode 100644 index 0000000000..ac3b3a656c --- /dev/null +++ b/backend/.sqlx/query-efbd9dc28ab5e53d070684b2e99d945872791171a1c8cef6088ea662f3b2cebb.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT path, value from flow WHERE workspace_id = $1 LIMIT $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "value", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + false, + false + ] + }, + "hash": "efbd9dc28ab5e53d070684b2e99d945872791171a1c8cef6088ea662f3b2cebb" +} diff --git a/backend/.sqlx/query-fed842c14aa37998da2b3cfafc71f7364132ea1e40e687aa84c3d02399e3bfb5.json b/backend/.sqlx/query-fed842c14aa37998da2b3cfafc71f7364132ea1e40e687aa84c3d02399e3bfb5.json new file mode 100644 index 0000000000..78c0158757 --- /dev/null +++ b/backend/.sqlx/query-fed842c14aa37998da2b3cfafc71f7364132ea1e40e687aa84c3d02399e3bfb5.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT path, value from resource WHERE workspace_id = $1 LIMIT $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "value", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "fed842c14aa37998da2b3cfafc71f7364132ea1e40e687aa84c3d02399e3bfb5" +} diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index d97ee85185..8007e4f0ce 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -2135,6 +2135,31 @@ paths: items: $ref: "#/components/schemas/ListableResource" + /w/{workspace}/resources/list_search: + get: + summary: list resources for search + operationId: listSearchResource + tags: + - resource + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: resource list + content: + application/json: + schema: + type: array + items: + type: object + properties: + path: + type: string + value: {} + required: + - path + - value + /w/{workspace}/resources/list_names/{name}: get: summary: list resource names @@ -2557,6 +2582,33 @@ paths: required: - id + /w/{workspace}/scripts/list_search: + get: + summary: list scripts for search + operationId: listSearchScript + tags: + - script + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: script list + content: + application/json: + schema: + type: array + items: + type: object + properties: + path: + type: string + content: + type: string + required: + - path + - content + + /w/{workspace}/scripts/list: get: summary: list all available scripts @@ -3202,6 +3254,31 @@ paths: items: type: string + /w/{workspace}/flows/list_search: + get: + summary: list flows for search + operationId: listSearchFlow + tags: + - flow + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: flow list + content: + application/json: + schema: + type: array + items: + type: object + properties: + path: + type: string + value: {} + required: + - path + - value + /w/{workspace}/flows/list: get: summary: list all available flows @@ -3505,6 +3582,31 @@ paths: schema: type: string + /w/{workspace}/apps/list_search: + get: + summary: list apps for search + operationId: listSearchApp + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + responses: + "200": + description: app list + content: + application/json: + schema: + type: array + items: + type: object + properties: + path: + type: string + value: {} + required: + - path + - value + /w/{workspace}/apps/list: get: summary: list all available apps diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index 370b093f7c..391349fc4a 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -43,6 +43,7 @@ use windmill_queue::{push, PushIsolationLevel, QueueTransaction}; pub fn workspaced_service() -> Router { Router::new() .route("/list", get(list_apps)) + .route("/list_search", get(list_search_apps)) .route("/get/p/*path", get(get_app)) .route("/get/draft/*path", get(get_app_w_draft)) .route("/secret_of/*path", get(get_secret_id)) @@ -160,6 +161,38 @@ pub struct EditApp { pub policy: Option, } +#[derive(Serialize, FromRow)] +pub struct SearchApp { + path: String, + value: serde_json::Value, +} +async fn list_search_apps( + authed: ApiAuthed, + Path(w_id): Path, + Extension(user_db): Extension, +) -> JsonResult> { + let mut tx = user_db.begin(&authed).await?; + + #[cfg(feature = "enterprise")] + let n = 1000; + + #[cfg(not(feature = "enterprise"))] + let n = 3; + + let rows = sqlx::query_as!( + SearchApp, + "SELECT path, app_version.value from app LEFT JOIN app_version ON app_version.id = versions[array_upper(versions, 1)] WHERE workspace_id = $1 LIMIT $2", + &w_id, + n + ) + .fetch_all(&mut *tx) + .await? + .into_iter() + .collect::>(); + tx.commit().await?; + Ok(Json(rows)) +} + async fn list_apps( authed: ApiAuthed, Extension(user_db): Extension, diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index 3617114cf5..39178bb284 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -24,7 +24,7 @@ use hyper::StatusCode; use serde::{Deserialize, Serialize}; use sql_builder::prelude::*; use sql_builder::SqlBuilder; -use sqlx::{Postgres, Transaction}; +use sqlx::{FromRow, Postgres, Transaction}; use windmill_audit::{audit_log, ActionKind}; use windmill_common::{ db::UserDB, @@ -42,6 +42,7 @@ use windmill_queue::{push, schedule::push_scheduled_job, PushIsolationLevel, Que pub fn workspaced_service() -> Router { Router::new() .route("/list", get(list_flows)) + .route("/list_search", get(list_search_flows)) .route("/create", post(create_flow)) .route("/update/*path", post(update_flow)) .route("/archive/*path", post(archive_flow_by_path)) @@ -58,6 +59,38 @@ pub fn global_service() -> Router { .route("/hub/get/:id", get(get_hub_flow_by_id)) } +#[derive(Serialize, FromRow)] +pub struct SearchFlow { + path: String, + value: serde_json::Value, +} +async fn list_search_flows( + authed: ApiAuthed, + Path(w_id): Path, + Extension(user_db): Extension, +) -> JsonResult> { + let mut tx = user_db.begin(&authed).await?; + + #[cfg(feature = "enterprise")] + let n = 1000; + + #[cfg(not(feature = "enterprise"))] + let n = 3; + + let rows = sqlx::query_as!( + SearchFlow, + "SELECT path, value from flow WHERE workspace_id = $1 LIMIT $2", + &w_id, + n + ) + .fetch_all(&mut *tx) + .await? + .into_iter() + .collect::>(); + tx.commit().await?; + Ok(Json(rows)) +} + async fn list_flows( authed: ApiAuthed, Extension(user_db): Extension, diff --git a/backend/windmill-api/src/resources.rs b/backend/windmill-api/src/resources.rs index a5f91d1496..aa698db650 100644 --- a/backend/windmill-api/src/resources.rs +++ b/backend/windmill-api/src/resources.rs @@ -31,6 +31,7 @@ use windmill_common::{ pub fn workspaced_service() -> Router { Router::new() .route("/list", get(list_resources)) + .route("/list_search", get(list_search_resources)) .route("/list_names/:type", get(list_names)) .route("/get/*path", get(get_resource)) .route("/exists/*path", get(exists_resource)) @@ -144,6 +145,37 @@ async fn list_names( Ok(Json(rows)) } +#[derive(Serialize, FromRow)] +pub struct SearchResource { + path: String, + value: serde_json::Value, +} +async fn list_search_resources( + authed: ApiAuthed, + Path(w_id): Path, + Extension(user_db): Extension, +) -> JsonResult> { + let mut tx = user_db.begin(&authed).await?; + #[cfg(feature = "enterprise")] + let n = 1000; + + #[cfg(not(feature = "enterprise"))] + let n = 3; + + let rows = sqlx::query_as!( + SearchResource, + "SELECT path, value from resource WHERE workspace_id = $1 LIMIT $2", + &w_id, + n + ) + .fetch_all(&mut *tx) + .await? + .into_iter() + .collect::>(); + tx.commit().await?; + Ok(Json(rows)) +} + async fn list_resources( authed: ApiAuthed, Query(lq): Query, diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index 4ea6e5452c..b1bab3405b 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -96,6 +96,7 @@ pub fn global_unauthed_service() -> Router { pub fn workspaced_service() -> Router { Router::new() .route("/list", get(list_scripts)) + .route("/list_search", get(list_search_scripts)) .route("/create", post(create_script)) .route("/archive/p/*path", post(archive_script_by_path)) .route("/get/draft/*path", get(get_script_by_path_w_draft)) @@ -111,6 +112,37 @@ pub fn workspaced_service() -> Router { .route("/list_paths", get(list_paths)) } +#[derive(Serialize, FromRow)] +pub struct SearchScript { + path: String, + content: String, +} +async fn list_search_scripts( + authed: ApiAuthed, + Path(w_id): Path, + Extension(user_db): Extension, +) -> JsonResult> { + let mut tx = user_db.begin(&authed).await?; + #[cfg(feature = "enterprise")] + let n = 1000; + + #[cfg(not(feature = "enterprise"))] + let n = 10; + + let rows = sqlx::query_as!( + SearchScript, + "SELECT path, content from script WHERE workspace_id = $1 AND archived = false LIMIT $2", + &w_id, + n + ) + .fetch_all(&mut *tx) + .await? + .into_iter() + .collect::>(); + tx.commit().await?; + Ok(Json(rows)) +} + async fn list_scripts( authed: ApiAuthed, Extension(user_db): Extension, diff --git a/frontend/src/lib/components/ContentSearch.svelte b/frontend/src/lib/components/ContentSearch.svelte new file mode 100644 index 0000000000..2d394aaffd --- /dev/null +++ b/frontend/src/lib/components/ContentSearch.svelte @@ -0,0 +1,346 @@ + + + { + return s.content + }} + bind:filteredItems={filteredScriptItems} +/> + + { + return JSON.stringify(s.value, null, 4) + }} + bind:filteredItems={filteredResourceItems} +/> + + { + return JSON.stringify(s.value, null, 4) + }} + bind:filteredItems={filteredFlowItems} +/> + + { + return JSON.stringify(s.value, null, 4) + }} + bind:filteredItems={filteredAppItems} +/> + +{#if isOpen} + +
+
{ + isOpen = false + }} + > +
+
Search by content
+
+ +
+
+
+
+
+ + + + + + + +
+
+ + + +
+
+
+
Searching among
{#if scripts}{scripts?.length}{:else} + + {/if}
+ scripts, +
{#if resources}{resources?.length}{:else} + + {/if}
+ resources, +
{#if flows}{flows?.length}{:else} + + {/if}
+ flows, +
{#if apps}{apps?.length}{:else} + + {/if}
+ apps
+
+ +
+ {#if !enterpriseLicense} + + Without EE, content search will only search among 10 scripts, 3 flows, 3 apps and 3 + resources. + +
+ {/if} + + {#if search.length > 0} +
+ {#if (searchKind == 'all' || searchKind == 'scripts') && filteredScriptItems.length > 0} + {#each filteredScriptItems as item} +
+ +
+
{@html item.marked}
+
+
+ +
+
+
+
+ {/each} + {/if} + {#if (searchKind == 'all' || searchKind == 'resources') && filteredResourceItems.length > 0} + {#each filteredResourceItems as item} +
+
Resource: {item.path}
+
+
{@html item.marked}
+
+
+ {/each} + {/if} + {#if (searchKind == 'all' || searchKind == 'flows') && filteredFlowItems.length > 0} + {#each filteredFlowItems as item} +
+ +
+
{@html item.marked}
+
+
+ +
+
+
+
+ {/each} + {/if} + {#if (searchKind == 'all' || searchKind == 'apps') && filteredAppItems.length > 0} + {#each filteredAppItems as item} +
+ +
+
{@html item.marked}
+
+
+ {/each} + {/if} +
+ {:else} +
+
+
Empty Search Filter
+
Start writing, search everywhere a path is referenced for instance
+
+
+ {/if} +
+
+{/if} diff --git a/frontend/src/lib/components/home/ItemsList.svelte b/frontend/src/lib/components/home/ItemsList.svelte index 40c1492142..f5cb5d5fcd 100644 --- a/frontend/src/lib/components/home/ItemsList.svelte +++ b/frontend/src/lib/components/home/ItemsList.svelte @@ -1,6 +1,6 @@ +
@@ -348,6 +352,13 @@
+