fix(templates): mcpToolPermissions persisté via create/update_template — désérialisation par alias préférés (mcpToolPermissions > defaultMcpToolPermissions > defaultMcpToolPolicy) sur les DTO Create/UpdateTemplateRequestDto — #244 (QA verte)

This commit is contained in:
2026-08-24 11:46:14 +02:00
parent 592f248762
commit 69129a95ad
3 changed files with 135 additions and 23 deletions

View File

@ -4281,8 +4281,7 @@ impl From<UpdateTemplateOutput> for TemplateDto {
}
/// Request DTO for `create_template`.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
#[derive(Debug, Clone)]
pub struct CreateTemplateRequestDto {
/// Display name.
pub name: String,
@ -4291,20 +4290,54 @@ pub struct CreateTemplateRequestDto {
/// Default runtime profile id for agents created from this template.
pub default_profile_id: String,
/// Optional default agent name for agents created from this template.
#[serde(default)]
pub default_agent_name: Option<String>,
/// Optional agent-level system permission override for created agents.
#[serde(default)]
pub default_system_permissions: Option<SystemPermissionSet>,
/// Optional agent-level MCP tool policy override for created agents.
#[serde(
default,
rename = "defaultMcpToolPermissions",
alias = "defaultMcpToolPolicy"
)]
pub default_mcp_tool_policy: Option<McpToolPolicy>,
}
impl<'de> Deserialize<'de> for CreateTemplateRequestDto {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut value = Value::deserialize(deserializer)?;
let object = value
.as_object_mut()
.ok_or_else(|| serde::de::Error::custom("expected create_template object"))?;
let name = take_required_string(object, "name").map_err(serde::de::Error::custom)?;
let content = take_required_string(object, "content").map_err(serde::de::Error::custom)?;
let default_profile_id =
take_required_string(object, "defaultProfileId").map_err(serde::de::Error::custom)?;
let default_agent_name = take_optional_value::<String>(object, "defaultAgentName")
.map_err(serde::de::Error::custom)?
.flatten();
let default_system_permissions =
take_optional_value::<SystemPermissionSet>(object, "defaultSystemPermissions")
.map_err(serde::de::Error::custom)?
.flatten();
let default_mcp_tool_policy = take_optional_value_preferred_aliases::<McpToolPolicy>(
object,
&[
"mcpToolPermissions",
"defaultMcpToolPermissions",
"defaultMcpToolPolicy",
],
)
.map_err(serde::de::Error::custom)?
.flatten();
Ok(Self {
name,
content,
default_profile_id,
default_agent_name,
default_system_permissions,
default_mcp_tool_policy,
})
}
}
impl CreateTemplateRequestDto {
/// Converts to the use-case input, parsing the profile id.
///
@ -4366,10 +4399,13 @@ impl<'de> Deserialize<'de> for UpdateTemplateRequestDto {
let default_system_permissions =
take_optional_value::<SystemPermissionSet>(object, "defaultSystemPermissions")
.map_err(serde::de::Error::custom)?;
let default_mcp_tool_policy = take_optional_value_alias::<McpToolPolicy>(
let default_mcp_tool_policy = take_optional_value_preferred_aliases::<McpToolPolicy>(
object,
"defaultMcpToolPermissions",
"defaultMcpToolPolicy",
&[
"mcpToolPermissions",
"defaultMcpToolPermissions",
"defaultMcpToolPolicy",
],
)
.map_err(serde::de::Error::custom)?;
Ok(Self {
@ -4434,23 +4470,25 @@ where
}
}
fn take_optional_value_alias<T>(
fn take_optional_value_preferred_aliases<T>(
object: &mut serde_json::Map<String, Value>,
primary_key: &str,
legacy_key: &str,
keys: &[&str],
) -> Result<Option<Option<T>>, String>
where
T: DeserializeOwned,
{
let primary = take_optional_value(object, primary_key)?;
let legacy = take_optional_value(object, legacy_key)?;
match (primary, legacy) {
(Some(value), None) | (None, Some(value)) => Ok(Some(value)),
(None, None) => Ok(None),
(Some(_), Some(_)) => Err(format!(
"{primary_key} and {legacy_key} cannot both be provided"
)),
for key in keys {
let parsed = take_optional_value(object, key)?;
if parsed.is_some() {
for ignored_key in keys {
if ignored_key != key {
object.remove(*ignored_key);
}
}
return Ok(parsed);
}
}
Ok(None)
}
/// Parses a template-id string (UUID) coming from the frontend.