review-classifier: keep the stored value out of a failed read's text
This commit is contained in:
parent
10a6f92244
commit
8cdaf433a9
1 changed files with 33 additions and 2 deletions
|
|
@ -188,7 +188,7 @@ impl KeySource for Store {
|
||||||
|
|
||||||
/// `e` and each of its sources, joined by `: `. The store client's errors
|
/// `e` and each of its sources, joined by `: `. The store client's errors
|
||||||
/// carry only a generic message at the top; the URL and the failure kind sit
|
/// carry only a generic message at the top; the URL and the failure kind sit
|
||||||
/// below it. Neither holds a token or key value.
|
/// below it. For login errors only: they hold URLs and error kinds, not values.
|
||||||
fn chain(e: &dyn std::error::Error) -> String {
|
fn chain(e: &dyn std::error::Error) -> String {
|
||||||
let mut out = e.to_string();
|
let mut out = e.to_string();
|
||||||
let mut next = e.source();
|
let mut next = e.source();
|
||||||
|
|
@ -208,6 +208,13 @@ fn login_failure(what: &str, e: &dyn std::error::Error) -> String {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The run failure for a store read that failed with `e`. Shows `e` alone:
|
||||||
|
/// a parse error's sources quote the stored value, and the failure text ends
|
||||||
|
/// up in a review comment.
|
||||||
|
fn read_failure(path: &str, e: &dyn std::error::Error) -> String {
|
||||||
|
format!("reading secret/{path}: {e}")
|
||||||
|
}
|
||||||
|
|
||||||
/// The key `field` takes from the object at `path`, trimmed; `None` when
|
/// The key `field` takes from the object at `path`, trimmed; `None` when
|
||||||
/// nothing or a blank is stored.
|
/// nothing or a blank is stored.
|
||||||
async fn read_key<T: serde::de::DeserializeOwned>(
|
async fn read_key<T: serde::de::DeserializeOwned>(
|
||||||
|
|
@ -221,7 +228,7 @@ async fn read_key<T: serde::de::DeserializeOwned>(
|
||||||
let stored: Option<T> = store
|
let stored: Option<T> = store
|
||||||
.read_optional(path)
|
.read_optional(path)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| format!("reading secret/{path}: {}", chain(&e)))?;
|
.map_err(|e| read_failure(path, &e))?;
|
||||||
Ok(stored
|
Ok(stored
|
||||||
.map(field)
|
.map(field)
|
||||||
.map(|key| key.trim().to_owned())
|
.map(|key| key.trim().to_owned())
|
||||||
|
|
@ -418,6 +425,30 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A parse failure whose source quotes the stored value.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Unparsable(serde_json::Error);
|
||||||
|
|
||||||
|
impl std::fmt::Display for Unparsable {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("error parsing the response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Unparsable {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
Some(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_failure_leaves_out_the_stored_value() {
|
||||||
|
let cause = serde_json::from_str::<u32>("\"hunter2\"").unwrap_err();
|
||||||
|
assert!(cause.to_string().contains("hunter2"), "control");
|
||||||
|
let text = read_failure("p", &Unparsable(cause));
|
||||||
|
assert_eq!(text, "reading secret/p: error parsing the response");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn chain_renders_every_level() {
|
fn chain_renders_every_level() {
|
||||||
let e = Level(
|
let e = Level(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue