mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-24 08:01:38 +00:00
optional starred info on get item + update name of user instance (#4295)
This commit is contained in:
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE password SET name = $1 WHERE email = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "aa5bdf0ab8781cf984711ae4f4351eb3bd5621ae081a418f1d4dbcb8359a8adb"
|
||||
}
|
||||
@@ -2675,7 +2675,7 @@ async fn test_flow_lock_all(db: Pool<Postgres>) {
|
||||
in_test_worker(&db, listen_first_job, port).await;
|
||||
|
||||
let modules = client
|
||||
.get_flow_by_path("test-workspace", "g/all/flow_lock_all")
|
||||
.get_flow_by_path("test-workspace", "g/all/flow_lock_all", None)
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
|
||||
@@ -342,6 +342,8 @@ paths:
|
||||
properties:
|
||||
is_super_admin:
|
||||
type: boolean
|
||||
name:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: user updated
|
||||
@@ -3924,6 +3926,10 @@ paths:
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
- name: with_starred_info
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: script details
|
||||
@@ -4059,6 +4065,10 @@ paths:
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptHash"
|
||||
- name: with_starred_info
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: script details
|
||||
@@ -4515,6 +4525,10 @@ paths:
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
- name: with_starred_info
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: flow details
|
||||
@@ -4932,6 +4946,10 @@ paths:
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/ScriptPath"
|
||||
- name: with_starred_info
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: app details
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
resources::get_resource_value_interpolated_internal,
|
||||
users::{require_owner_of_path, OptAuthed},
|
||||
utils::WithStarredInfoQuery,
|
||||
variables::encrypt,
|
||||
webhook_util::{WebhookMessage, WebhookShared},
|
||||
HTTP_CLIENT,
|
||||
@@ -116,6 +117,8 @@ pub struct AppWithLastVersion {
|
||||
pub created_by: String,
|
||||
pub created_at: chrono::DateTime<chrono::Utc>,
|
||||
pub extra_perms: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub starred: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, FromRow)]
|
||||
@@ -316,20 +319,44 @@ async fn get_app(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Query(query): Query<WithStarredInfoQuery>,
|
||||
) -> JsonResult<AppWithLastVersion> {
|
||||
let path = path.to_path();
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let app_o = sqlx::query_as::<_, AppWithLastVersion>(
|
||||
"SELECT app.id, app.path, app.summary, app.versions, app.policy,
|
||||
app.extra_perms, app_version.value,
|
||||
app_version.created_at, app_version.created_by from app, app_version
|
||||
WHERE app.path = $1 AND app.workspace_id = $2 AND app_version.id = app.versions[array_upper(app.versions, 1)]",
|
||||
)
|
||||
.bind(path.to_owned())
|
||||
.bind(&w_id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?;
|
||||
let app_o = if query.with_starred_info.unwrap_or(false) {
|
||||
sqlx::query_as::<_, AppWithLastVersion>(
|
||||
"SELECT app.id, app.path, app.summary, app.versions, app.policy,
|
||||
app.extra_perms, app_version.value,
|
||||
app_version.created_at, app_version.created_by, favorite.path IS NOT NULL as starred
|
||||
FROM app
|
||||
JOIN app_version
|
||||
ON app_version.id = app.versions[array_upper(app.versions, 1)]
|
||||
LEFT JOIN favorite
|
||||
ON favorite.favorite_kind = 'app'
|
||||
AND favorite.workspace_id = app.workspace_id
|
||||
AND favorite.path = app.path
|
||||
AND favorite.usr = $3
|
||||
WHERE app.path = $1 AND app.workspace_id = $2",
|
||||
)
|
||||
.bind(path.to_owned())
|
||||
.bind(&w_id)
|
||||
.bind(&authed.username)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query_as::<_, AppWithLastVersion>(
|
||||
"SELECT app.id, app.path, app.summary, app.versions, app.policy,
|
||||
app.extra_perms, app_version.value,
|
||||
app_version.created_at, app_version.created_by, NULL as starred
|
||||
FROM app, app_version
|
||||
WHERE app.path = $1 AND app.workspace_id = $2 AND app_version.id = app.versions[array_upper(app.versions, 1)]",
|
||||
)
|
||||
.bind(path.to_owned())
|
||||
.bind(&w_id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?
|
||||
};
|
||||
tx.commit().await?;
|
||||
|
||||
let app = not_found_if_none(app_o, "App", path)?;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
use crate::utils::WithStarredInfoQuery;
|
||||
use crate::{
|
||||
db::DB,
|
||||
schedule::clear_schedule,
|
||||
@@ -876,13 +877,31 @@ async fn get_flow_by_path(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Query(query): Query<WithStarredInfoQuery>,
|
||||
) -> JsonResult<Flow> {
|
||||
let path = path.to_path();
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let flow_o =
|
||||
let flow_o = if query.with_starred_info.unwrap_or(false) {
|
||||
sqlx::query_as::<_, Flow>(
|
||||
"SELECT flow.workspace_id, flow.path, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by
|
||||
"SELECT flow.workspace_id, flow.path, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, favorite.path IS NOT NULL as starred
|
||||
FROM flow
|
||||
LEFT JOIN favorite
|
||||
ON favorite.favorite_kind = 'flow'
|
||||
AND favorite.workspace_id = flow.workspace_id
|
||||
AND favorite.path = flow.path
|
||||
AND favorite.usr = $3
|
||||
LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]
|
||||
WHERE flow.path = $1 AND flow.workspace_id = $2"
|
||||
)
|
||||
.bind(path)
|
||||
.bind(w_id)
|
||||
.bind(&authed.username)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query_as::<_, Flow>(
|
||||
"SELECT flow.workspace_id, flow.path, flow.summary, flow.description, flow.archived, flow.extra_perms, flow.draft_only, flow.dedicated_worker, flow.tag, flow.ws_error_handler_muted, flow.timeout, flow.visible_to_runner_only, flow_version.schema, flow_version.value, flow_version.created_at as edited_at, flow_version.created_by as edited_by, NULL as starred
|
||||
FROM flow
|
||||
LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]
|
||||
WHERE flow.path = $1 AND flow.workspace_id = $2"
|
||||
@@ -890,7 +909,8 @@ async fn get_flow_by_path(
|
||||
.bind(path)
|
||||
.bind(w_id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?;
|
||||
.await?
|
||||
};
|
||||
tx.commit().await?;
|
||||
|
||||
let flow = not_found_if_none(flow_o, "Flow", path)?;
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
schedule::clear_schedule,
|
||||
users::{maybe_refresh_folders, require_owner_of_path, AuthCache},
|
||||
utils::WithStarredInfoQuery,
|
||||
webhook_util::{WebhookMessage, WebhookShared},
|
||||
HTTP_CLIENT,
|
||||
};
|
||||
@@ -526,7 +527,7 @@ async fn create_script_internal<'c>(
|
||||
)));
|
||||
};
|
||||
|
||||
let ps = get_script_by_hash_internal(tx.transaction_mut(), &w_id, p_hash).await?;
|
||||
let ps = get_script_by_hash_internal(tx.transaction_mut(), &w_id, p_hash, None).await?;
|
||||
|
||||
if ps.path != ns.path {
|
||||
require_owner_of_path(&authed, &ps.path)?;
|
||||
@@ -826,19 +827,40 @@ async fn get_script_by_path(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
Query(query): Query<WithStarredInfoQuery>,
|
||||
) -> JsonResult<Script> {
|
||||
let path = path.to_path();
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let script_o = sqlx::query_as::<_, Script>(
|
||||
"SELECT * FROM script WHERE path = $1 AND workspace_id = $2 \
|
||||
AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \
|
||||
workspace_id = $2)",
|
||||
)
|
||||
.bind(path)
|
||||
.bind(w_id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?;
|
||||
let script_o = if query.with_starred_info.unwrap_or(false) {
|
||||
sqlx::query_as::<_, Script>(
|
||||
"SELECT s.*, favorite.path IS NOT NULL as starred
|
||||
FROM script s
|
||||
LEFT JOIN favorite
|
||||
ON favorite.favorite_kind = 'script'
|
||||
AND favorite.workspace_id = s.workspace_id
|
||||
AND favorite.path = s.path
|
||||
AND favorite.usr = $3
|
||||
WHERE s.path = $1
|
||||
AND s.workspace_id = $2
|
||||
AND s.created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND workspace_id = $2)",
|
||||
)
|
||||
.bind(path)
|
||||
.bind(w_id)
|
||||
.bind(&authed.username)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query_as::<_, Script>(
|
||||
"SELECT *, NULL as starred FROM script WHERE path = $1 AND workspace_id = $2 \
|
||||
AND created_at = (SELECT max(created_at) FROM script WHERE path = $1 AND \
|
||||
workspace_id = $2)",
|
||||
)
|
||||
.bind(path)
|
||||
.bind(w_id)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?
|
||||
};
|
||||
tx.commit().await?;
|
||||
|
||||
let script = not_found_if_none(script_o, "Script", path)?;
|
||||
@@ -1096,13 +1118,33 @@ async fn get_script_by_hash_internal<'c>(
|
||||
db: &mut Transaction<'c, Postgres>,
|
||||
workspace_id: &str,
|
||||
hash: &ScriptHash,
|
||||
with_starred_info_for_username: Option<&str>,
|
||||
) -> Result<Script> {
|
||||
let script_o =
|
||||
sqlx::query_as::<_, Script>("SELECT * FROM script WHERE hash = $1 AND workspace_id = $2")
|
||||
.bind(hash)
|
||||
.bind(workspace_id)
|
||||
.fetch_optional(&mut **db)
|
||||
.await?;
|
||||
let script_o = if let Some(username) = with_starred_info_for_username {
|
||||
sqlx::query_as::<_, Script>(
|
||||
"SELECT s.*, favorite.path IS NOT NULL as starred
|
||||
FROM script s
|
||||
LEFT JOIN favorite
|
||||
ON favorite.favorite_kind = 'script'
|
||||
AND favorite.workspace_id = s.workspace_id
|
||||
AND favorite.path = s.path
|
||||
AND favorite.usr = $1
|
||||
WHERE s.hash = $2 AND s.workspace_id = $3",
|
||||
)
|
||||
.bind(&username)
|
||||
.bind(hash)
|
||||
.bind(workspace_id)
|
||||
.fetch_optional(&mut **db)
|
||||
.await?
|
||||
} else {
|
||||
sqlx::query_as::<_, Script>(
|
||||
"SELECT *, NULL as starred FROM script WHERE hash = $1 AND workspace_id = $2",
|
||||
)
|
||||
.bind(hash)
|
||||
.bind(workspace_id)
|
||||
.fetch_optional(&mut **db)
|
||||
.await?
|
||||
};
|
||||
|
||||
let script = not_found_if_none(script_o, "Script", hash.to_string())?;
|
||||
Ok(script)
|
||||
@@ -1111,9 +1153,23 @@ async fn get_script_by_hash_internal<'c>(
|
||||
async fn get_script_by_hash(
|
||||
Extension(db): Extension<DB>,
|
||||
Path((w_id, hash)): Path<(String, ScriptHash)>,
|
||||
Query(query): Query<WithStarredInfoQuery>,
|
||||
Extension(authed): Extension<ApiAuthed>,
|
||||
) -> JsonResult<Script> {
|
||||
let mut tx = db.begin().await?;
|
||||
let r = get_script_by_hash_internal(&mut tx, &w_id, &hash).await?;
|
||||
let r = get_script_by_hash_internal(
|
||||
&mut tx,
|
||||
&w_id,
|
||||
&hash,
|
||||
query.with_starred_info.and_then(|x| {
|
||||
if x {
|
||||
Some(authed.username.as_str())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Json(r))
|
||||
@@ -1127,7 +1183,7 @@ async fn raw_script_by_hash(
|
||||
let hash = ScriptHash(to_i64(hash_str.strip_suffix(".ts").ok_or_else(|| {
|
||||
Error::BadRequest("Raw script path must end with .ts".to_string())
|
||||
})?)?);
|
||||
let r = get_script_by_hash_internal(&mut tx, &w_id, &hash).await?;
|
||||
let r = get_script_by_hash_internal(&mut tx, &w_id, &hash, None).await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(r.content)
|
||||
|
||||
@@ -756,6 +756,7 @@ pub struct DeclineInvite {
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditUser {
|
||||
pub is_super_admin: Option<bool>,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -1703,6 +1704,16 @@ async fn update_user(
|
||||
.await?;
|
||||
}
|
||||
|
||||
if let Some(n) = eu.name {
|
||||
sqlx::query_scalar!(
|
||||
"UPDATE password SET name = $1 WHERE email = $2",
|
||||
n,
|
||||
&email_to_update
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use sqlx::{Postgres, Transaction};
|
||||
use windmill_common::{
|
||||
auth::is_super_admin_email,
|
||||
@@ -14,6 +15,11 @@ use windmill_common::{
|
||||
DB,
|
||||
};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WithStarredInfoQuery {
|
||||
pub with_starred_info: Option<bool>,
|
||||
}
|
||||
|
||||
pub async fn require_super_admin(db: &DB, email: &str) -> error::Result<()> {
|
||||
let is_admin = is_super_admin_email(db, email).await?;
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ pub struct Flow {
|
||||
pub schema: Option<Schema>,
|
||||
pub extra_perms: serde_json::Value,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub starred: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub draft_only: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub dedicated_worker: Option<bool>,
|
||||
|
||||
@@ -160,6 +160,8 @@ pub struct Script {
|
||||
pub kind: ScriptKind,
|
||||
pub tag: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub starred: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub draft_only: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub envs: Option<Vec<String>>,
|
||||
|
||||
Reference in New Issue
Block a user