fix: inline get_draft query field instead of flattening

This commit is contained in:
Diego Imbert
2026-06-02 12:34:25 +02:00
parent 32a78be6dc
commit 9e6053adf5
4 changed files with 46 additions and 45 deletions
+12 -11
View File
@@ -20,10 +20,8 @@ use windmill_api_auth::{
};
use windmill_common::workspaces::{check_deploy_rules, RuleCheckResult};
use windmill_common::{
user_drafts::{
fetch_draft_only, maybe_overlay_draft, UserDraftItemKind, WithDraftOverlay, WithDraftQuery,
},
utils::{WithStarredInfoQuery, HTTP_CLIENT},
user_drafts::{fetch_draft_only, maybe_overlay_draft, UserDraftItemKind, WithDraftOverlay},
utils::HTTP_CLIENT,
webhook::{WebhookMessage, WebhookShared},
DB,
};
@@ -1368,12 +1366,15 @@ async fn get_deployment_status(
Ok(Json(deployment_status))
}
// Fields inlined rather than flattened from WithStarredInfoQuery /
// WithDraftQuery — see the same comment on `GetScriptByPathQuery` in
// scripts.rs: axum's `serde_urlencoded` query extractor doesn't preserve
// the "true"/"false" → bool conversion through `#[serde(flatten)]`.
#[derive(Deserialize)]
struct GetFlowByPathQuery {
#[serde(flatten)]
starred: WithStarredInfoQuery,
#[serde(flatten)]
draft: WithDraftQuery,
with_starred_info: Option<bool>,
#[serde(default)]
get_draft: bool,
}
async fn get_flow_by_path(
@@ -1386,7 +1387,7 @@ async fn get_flow_by_path(
let path = path.to_path();
check_scopes(&authed, || format!("flows:read:{}", path))?;
let mut tx = user_db.begin(&authed).await?;
let flow_o = if query.starred.with_starred_info.unwrap_or(false) {
let flow_o = if query.with_starred_info.unwrap_or(false) {
sqlx::query_as::<_, FlowWithStarred>(
r#"
SELECT
@@ -1478,12 +1479,12 @@ async fn get_flow_by_path(
&authed.email,
UserDraftItemKind::Flow,
path,
query.draft.get_draft,
query.get_draft,
flow,
)
.await?
}
None if query.draft.get_draft => {
None if query.get_draft => {
fetch_draft_only(&db, &w_id, &authed.email, UserDraftItemKind::Flow, path)
.await?
.ok_or_else(|| {
+14 -10
View File
@@ -12,9 +12,7 @@ use windmill_api_auth::{
check_scopes, maybe_refresh_folders, require_owner_of_path, ApiAuthed,
};
use windmill_common::{
user_drafts::{
fetch_draft_only, maybe_overlay_draft, UserDraftItemKind, WithDraftOverlay, WithDraftQuery,
},
user_drafts::{fetch_draft_only, maybe_overlay_draft, UserDraftItemKind, WithDraftOverlay},
utils::{BulkDeleteRequest, WithStarredInfoQuery, HTTP_CLIENT},
webhook::{WebhookMessage, WebhookShared},
workspaces::{check_deploy_rules, RuleCheckResult},
@@ -1640,12 +1638,18 @@ pub async fn pick_hub_script_by_path(
Ok::<_, Error>((status_code, headers, response))
}
// NOTE: inlined fields rather than `#[serde(flatten)]` on
// `WithStarredInfoQuery` / `WithDraftQuery`. axum's default query
// extractor uses `serde_urlencoded`, and flatten there routes through an
// internal map that drops type info — so `?get_draft=true` arrives as a
// `String` and fails the inner `bool` deserializer. Inlining keeps the
// fields on the top-level struct so `serde_urlencoded`'s own bool
// adapter sees the value directly.
#[derive(Deserialize)]
struct GetScriptByPathQuery {
#[serde(flatten)]
starred: WithStarredInfoQuery,
#[serde(flatten)]
draft: WithDraftQuery,
with_starred_info: Option<bool>,
#[serde(default)]
get_draft: bool,
}
#[axum::debug_handler]
@@ -1660,7 +1664,7 @@ async fn get_script_by_path(
check_scopes(&authed, || format!("scripts:read:{}", path))?;
let mut tx = user_db.begin(&authed).await?;
let script_o = if query.starred.with_starred_info.unwrap_or(false) {
let script_o = if query.with_starred_info.unwrap_or(false) {
sqlx::query_as::<_, ScriptWithStarred<ScriptRunnableSettingsHandle>>(
"SELECT s.*, favorite.path IS NOT NULL as starred
FROM script s
@@ -1707,12 +1711,12 @@ async fn get_script_by_path(
&authed.email,
UserDraftItemKind::Script,
path,
query.draft.get_draft,
query.get_draft,
script,
)
.await?
}
None if query.draft.get_draft => {
None if query.get_draft => {
fetch_draft_only(&db, &w_id, &authed.email, UserDraftItemKind::Script, path)
.await?
.ok_or_else(|| {
+12 -20
View File
@@ -12,7 +12,7 @@ use crate::{
db::{ApiAuthed, DB},
jobs::RunJobQuery,
users::{require_owner_of_path, require_path_read_access_for_preview, OptAuthed},
utils::{check_scopes, WithStarredInfoQuery},
utils::check_scopes,
webhook_util::{WebhookMessage, WebhookShared},
HTTP_CLIENT,
};
@@ -58,9 +58,7 @@ use windmill_common::{
get_payload_tag_from_prefixed_path, resolve_delete_after_secs, schedule_job_deletion,
JobPayload, RawCode,
},
user_drafts::{
fetch_draft_only, maybe_overlay_draft, UserDraftItemKind, WithDraftOverlay, WithDraftQuery,
},
user_drafts::{fetch_draft_only, maybe_overlay_draft, UserDraftItemKind, WithDraftOverlay},
users::username_to_permissioned_as,
utils::{
http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, require_admin,
@@ -548,12 +546,15 @@ async fn get_raw_app_data(
// Ok(Json(version))
// }
// Fields inlined rather than flattened from WithStarredInfoQuery /
// WithDraftQuery — see the same comment on `GetScriptByPathQuery` in
// scripts.rs: axum's `serde_urlencoded` query extractor doesn't preserve
// the "true"/"false" → bool conversion through `#[serde(flatten)]`.
#[derive(Deserialize)]
struct GetAppQuery {
#[serde(flatten)]
starred: WithStarredInfoQuery,
#[serde(flatten)]
draft: WithDraftQuery,
with_starred_info: Option<bool>,
#[serde(default)]
get_draft: bool,
/// When no deployed app exists at this path and `get_draft` is set,
/// `raw_app` picks which draft kind to look up (`raw_app` or `app`).
/// Ignored when a deployed row exists — the row's own `raw_app`
@@ -574,7 +575,7 @@ async fn get_app(
check_scopes(&authed, || format!("apps:read:{}", path))?;
let mut tx = user_db.begin(&authed).await?;
let app_o = if query.starred.with_starred_info.unwrap_or(false) {
let app_o = if query.with_starred_info.unwrap_or(false) {
sqlx::query_as::<_, AppWithLastVersionAndStarred>(
"SELECT app.id, app.path, app.summary, app.versions, app.policy, app.custom_path,
app.extra_perms, app_version.value,
@@ -622,18 +623,9 @@ async fn get_app(
} else {
UserDraftItemKind::App
};
maybe_overlay_draft(
&db,
&w_id,
&authed.email,
kind,
path,
query.draft.get_draft,
app,
)
.await?
maybe_overlay_draft(&db, &w_id, &authed.email, kind, path, query.get_draft, app).await?
}
None if query.draft.get_draft => {
None if query.get_draft => {
let kind = if query.raw_app.unwrap_or(false) {
UserDraftItemKind::RawApp
} else {
+8 -4
View File
@@ -35,7 +35,7 @@ use windmill_common::{
db::{DbWithOptAuthed, UserDB},
error::{Error, JsonResult, Result},
scripts::ScriptHash,
user_drafts::{maybe_overlay_draft, UserDraftItemKind, WithDraftOverlay, WithDraftQuery},
user_drafts::{maybe_overlay_draft, UserDraftItemKind, WithDraftOverlay},
utils::{not_found_if_none, paginate, Pagination, StripPath, WarnAfterExt},
variables::{
build_crypt, get_reserved_variables, ContextualVariable, CreateVariable, ListableVariable,
@@ -204,12 +204,16 @@ async fn list_variables(
Ok(Json(rows))
}
// `get_draft` inlined rather than flattened from WithDraftQuery — see
// the same comment on `GetScriptByPathQuery` in scripts.rs: axum's
// `serde_urlencoded` query extractor doesn't preserve the "true"/"false"
// → bool conversion through `#[serde(flatten)]`.
#[derive(Deserialize)]
struct GetVariableQuery {
decrypt_secret: Option<bool>,
include_encrypted: Option<bool>,
#[serde(flatten)]
draft: WithDraftQuery,
#[serde(default)]
get_draft: bool,
}
async fn get_variable(
@@ -311,7 +315,7 @@ async fn get_variable(
&authed.email,
UserDraftItemKind::Variable,
path,
q.draft.get_draft,
q.get_draft,
r,
)
.await?;