Use a Regex in insta dynamic redaction to replace Uuids with [uuid]

(cherry picked from commit f8b8c6ab71a28052cf9b271ca8aa5d4175f9e8f9)
Signed-off-by: Martin Tzvetanov Grigorov <mgrigorov@apache.org>
This commit is contained in:
Martin Tzvetanov Grigorov 2025-05-26 13:39:15 +03:00
parent 34d58f35c8
commit f54b57e5be
No known key found for this signature in database
GPG Key ID: 3194FD8C1AE300EF
5 changed files with 1399 additions and 1281 deletions

2646
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -15,3 +15,4 @@ license.workspace = true
insta = { version = "=1.39.0", features = ["json", "redactions"] }
md5 = "0.7.0"
once_cell = "1.20"
regex-lite = "0.1.6"

View File

@ -4,9 +4,17 @@ use std::path::{Path, PathBuf};
use std::sync::Mutex;
pub use insta;
use insta::internals::{Content, ContentPath};
use once_cell::sync::Lazy;
use regex_lite::{Regex, RegexBuilder};
static SNAPSHOT_NAMES: Lazy<Mutex<HashMap<PathBuf, usize>>> = Lazy::new(Mutex::default);
/// A regex to match UUIDs in messages, specifically looking for the UUID v4 format
static UUID_IN_MESSAGE_RE: Lazy<Regex> = Lazy::new(|| {
RegexBuilder::new(r"(?<before>.*)([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})(?<after>.*)")
.case_insensitive(true)
.build().unwrap()
});
/// Return the md5 hash of the given string
pub fn hash_snapshot(snap: &str) -> String {
@ -26,6 +34,19 @@ pub fn default_snapshot_settings_for_test<'a>(
let filename = path.file_name().unwrap().to_str().unwrap();
settings.set_omit_expression(true);
fn uuid_in_message_redaction(content: Content, _content_path: ContentPath) -> Content {
match &content {
Content::String(s) => {
let uuid_replaced = UUID_IN_MESSAGE_RE.replace_all(s, "$before[uuid]$after");
Content::String(uuid_replaced.to_string())
}
_ => content,
}
}
settings.add_dynamic_redaction(".message", uuid_in_message_redaction);
settings.add_dynamic_redaction(".error.message", uuid_in_message_redaction);
let test_name = test_name.strip_suffix("::{{closure}}").unwrap_or(test_name);
let test_name = test_name.rsplit("::").next().unwrap().to_owned();

View File

@ -64,7 +64,7 @@ pub enum MeilisearchHttpError {
#[error(transparent)]
IndexScheduler(#[from] index_scheduler::Error),
#[error("{}", match .index_name {
Some(name) if !name.is_empty() => format!("Index `{}`: {error}", MeilisearchHttpError::index_name(name)),
Some(name) if !name.is_empty() => format!("Index `{}`: {error}", name),
_ => format!("{error}")
})]
Milli { error: milli::Error, index_name: Option<String> },
@ -84,14 +84,6 @@ impl MeilisearchHttpError {
pub(crate) fn from_milli(error: milli::Error, index_name: Option<String>) -> Self {
Self::Milli { error, index_name }
}
fn index_name(index_name: &str) -> &str {
if let Ok(_) = uuid::Uuid::parse_str(index_name) {
"[uuid]"
} else {
index_name
}
}
}
impl ErrorCode for MeilisearchHttpError {

View File

@ -708,7 +708,7 @@ async fn filter_invalid_attribute_array() {
|response, code| {
snapshot!(response, @r###"
{
"message": "Index `test`: Attribute `many` is not filterable. Available filterable attribute patterns are: `title`.\n1:5 many = Glass",
"message": "Index `[uuid]`: Attribute `many` is not filterable. Available filterable attribute patterns are: `title`.\n1:5 many = Glass",
"code": "invalid_search_filter",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"