chore(release): secrets-mcp 0.4.0
Bump version for the N:N entry_secrets data model and related MCP/Web changes. Remove superseded SQL migration artifacts; rely on auto-migrate. Add structured errors, taxonomy normalization, and web i18n helpers. Made-with: Cursor
This commit is contained in:
111
crates/secrets-core/src/taxonomy.rs
Normal file
111
crates/secrets-core/src/taxonomy.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
fn normalize_token(input: &str) -> String {
|
||||
input.trim().to_lowercase().replace('_', "-")
|
||||
}
|
||||
|
||||
fn normalize_subtype_token(input: &str) -> String {
|
||||
normalize_token(input)
|
||||
}
|
||||
|
||||
fn map_legacy_entry_type(input: &str) -> Option<(&'static str, &'static str)> {
|
||||
match input {
|
||||
"log-ingestion-endpoint" => Some(("service", "log-ingestion")),
|
||||
"cloud-api" => Some(("service", "cloud-api")),
|
||||
"git-server" => Some(("service", "git")),
|
||||
"mqtt-broker" => Some(("service", "mqtt-broker")),
|
||||
"database" => Some(("service", "database")),
|
||||
"monitoring-dashboard" => Some(("service", "monitoring")),
|
||||
"dns-api" => Some(("service", "dns-api")),
|
||||
"notification-webhook" => Some(("service", "webhook")),
|
||||
"api-endpoint" => Some(("service", "api-endpoint")),
|
||||
"credential" | "credential-key" => Some(("service", "credential")),
|
||||
"key" => Some(("service", "credential")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalize entry `type` and optionally backfill `metadata.subtype` for legacy values.
|
||||
///
|
||||
/// This keeps backward compatibility:
|
||||
/// - stable primary types stay unchanged
|
||||
/// - known legacy long-tail types are mapped to `service` + `metadata.subtype`
|
||||
/// - unknown values are kept (normalized to kebab-case) instead of hard failing
|
||||
pub fn normalize_entry_type_and_metadata(
|
||||
entry_type: &str,
|
||||
metadata: &mut Map<String, Value>,
|
||||
) -> String {
|
||||
let original_raw = entry_type.trim();
|
||||
let normalized = normalize_token(original_raw);
|
||||
if normalized.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
|
||||
if let Some((mapped_type, mapped_subtype)) = map_legacy_entry_type(&normalized) {
|
||||
if !metadata.contains_key("subtype") {
|
||||
metadata.insert(
|
||||
"subtype".to_string(),
|
||||
Value::String(mapped_subtype.to_string()),
|
||||
);
|
||||
}
|
||||
if !metadata.contains_key("_original_type") && original_raw != mapped_type {
|
||||
metadata.insert(
|
||||
"_original_type".to_string(),
|
||||
Value::String(original_raw.to_string()),
|
||||
);
|
||||
}
|
||||
return mapped_type.to_string();
|
||||
}
|
||||
|
||||
if let Some(subtype) = metadata.get_mut("subtype")
|
||||
&& let Some(s) = subtype.as_str()
|
||||
{
|
||||
*subtype = Value::String(normalize_subtype_token(s));
|
||||
}
|
||||
|
||||
normalized
|
||||
}
|
||||
|
||||
/// Canonical secret type options for UI dropdowns.
|
||||
pub const SECRET_TYPE_OPTIONS: &[&str] = &[
|
||||
"text", "password", "token", "api-key", "ssh-key", "url", "phone", "id-card",
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
#[test]
|
||||
fn normalize_entry_type_maps_legacy_type_and_backfills_metadata() {
|
||||
let mut metadata = Map::new();
|
||||
let normalized = normalize_entry_type_and_metadata("git-server", &mut metadata);
|
||||
|
||||
assert_eq!(normalized, "service");
|
||||
assert_eq!(
|
||||
metadata.get("subtype"),
|
||||
Some(&Value::String("git".to_string()))
|
||||
);
|
||||
assert_eq!(
|
||||
metadata.get("_original_type"),
|
||||
Some(&Value::String("git-server".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_entry_type_normalizes_existing_subtype() {
|
||||
let mut metadata = Map::new();
|
||||
metadata.insert(
|
||||
"subtype".to_string(),
|
||||
Value::String("Cloud_API".to_string()),
|
||||
);
|
||||
|
||||
let normalized = normalize_entry_type_and_metadata("service", &mut metadata);
|
||||
|
||||
assert_eq!(normalized, "service");
|
||||
assert_eq!(
|
||||
metadata.get("subtype"),
|
||||
Some(&Value::String("cloud-api".to_string()))
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user