optional starred info on get item + update name of user instance (#4295)

This commit is contained in:
HugoCasa
2024-08-27 21:58:51 +02:00
committed by GitHub
parent e0831777a1
commit bc5a7d0519
10 changed files with 189 additions and 32 deletions
@@ -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"
}
+1 -1
View File
@@ -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()
+18
View File
@@ -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
+37 -10
View File
@@ -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)?;
+23 -3
View File
@@ -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)?;
+74 -18
View File
@@ -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)
+11
View File
@@ -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,
+6
View File
@@ -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?;
+2
View File
@@ -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>,
+2
View File
@@ -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>>,