fix: tolerate string app_id in GHES app config deserialization (#10923)

* fix: tolerate string app_id in GHES app config deserialization

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Rh73nHumzCbyd4Gwf6kw6

* fix: address review — strict app_id validation, drop dead variant

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Rh73nHumzCbyd4Gwf6kw6

* chore: update ee-repo-ref to b52c6471d517d979a9887f207a36347b1af376c8

This commit updates the EE repository reference after PR #767 was merged in windmill-ee-private.

Previous ee-repo-ref: ab2dc653719f9d65eb10964d1e2b5bc1b94d6535

New ee-repo-ref: b52c6471d517d979a9887f207a36347b1af376c8

Automated by sync-ee-ref workflow.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com>
This commit is contained in:
Alexander Petric
2026-09-01 23:31:43 +02:00
committed by GitHub
co-authored by Claude Fable 5 windmill-internal-app[bot]
parent 9074de25ea
commit af8ff38687
3 changed files with 69 additions and 2 deletions
+1 -1
View File
@@ -1 +1 @@
6efe7a73c745c2e1377a34498523c00d89010a3d
b52c6471d517d979a9887f207a36347b1af376c8
+57
View File
@@ -38,6 +38,25 @@ pub fn is_default<T: Default + std::cmp::PartialEq>(t: &T) -> bool {
&T::default() == t
}
pub fn maybe_number<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: FromStr + serde::Deserialize<'de>,
<T as FromStr>::Err: Display,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum NumericOrString<T> {
String(String),
RawT(T),
}
match NumericOrString::<T>::deserialize(deserializer)? {
NumericOrString::String(s) => T::from_str(&s).map_err(serde::de::Error::custom),
NumericOrString::RawT(i) => Ok(i),
}
}
pub fn maybe_number_opt<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
@@ -85,3 +104,41 @@ where
{
serde::Deserialize::deserialize(deserializer).map(Some)
}
#[cfg(test)]
mod tests {
use serde::Deserialize;
#[derive(Deserialize)]
struct WithMaybeNumber {
#[serde(deserialize_with = "super::maybe_number")]
n: i64,
}
#[test]
fn maybe_number_accepts_number() {
let v: WithMaybeNumber = serde_json::from_value(serde_json::json!({ "n": 12345 })).unwrap();
assert_eq!(v.n, 12345);
}
#[test]
fn maybe_number_accepts_string() {
let v: WithMaybeNumber =
serde_json::from_value(serde_json::json!({ "n": "12345" })).unwrap();
assert_eq!(v.n, 12345);
}
#[test]
fn maybe_number_rejects_non_numeric_string() {
assert!(
serde_json::from_value::<WithMaybeNumber>(serde_json::json!({ "n": "abc" })).is_err()
);
}
#[test]
fn maybe_number_rejects_null() {
assert!(
serde_json::from_value::<WithMaybeNumber>(serde_json::json!({ "n": null })).is_err()
);
}
}
@@ -283,7 +283,17 @@
placeholder: '12345',
disabled: fieldsDisabled
}}
bind:value={$values['github_enterprise_app'].app_id}
bind:value={
() => $values['github_enterprise_app'].app_id,
(v) => {
// The backend expects app_id as a positive integer (i64). Reject
// fractional/out-of-range values instead of truncating them, and store
// undefined (never a string or 0) so the config omits the key when unset.
const n = typeof v === 'string' ? Number(v.trim() || NaN) : (v ?? NaN)
$values['github_enterprise_app'].app_id =
Number.isSafeInteger(n) && n > 0 ? n : undefined
}
}
/>
</div>
<div class="flex flex-col gap-1">