Skip to content

Commit 14f8b62

Browse files
committed
handle changed error responses from crates.io API
1 parent fb30bb0 commit 14f8b62

File tree

1 file changed

+64
-8
lines changed

1 file changed

+64
-8
lines changed

src/web/releases.rs

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
},
1515
BuildQueue, Config, InstanceMetrics,
1616
};
17-
use anyhow::{anyhow, Context as _, Result};
17+
use anyhow::{anyhow, bail, Context as _, Result};
1818
use axum::{
1919
extract::{Extension, Path, Query},
2020
response::{IntoResponse, Response as AxumResponse},
@@ -141,10 +141,15 @@ async fn get_search_results(
141141
config: &Config,
142142
query_params: &str,
143143
) -> Result<SearchResult, anyhow::Error> {
144+
#[derive(Deserialize)]
145+
struct CratesIoError {
146+
detail: String,
147+
}
144148
#[derive(Deserialize)]
145149
struct CratesIoSearchResult {
146-
crates: Vec<CratesIoCrate>,
147-
meta: CratesIoMeta,
150+
crates: Option<Vec<CratesIoCrate>>,
151+
meta: Option<CratesIoMeta>,
152+
errors: Option<Vec<CratesIoError>>,
148153
}
149154
#[derive(Deserialize, Debug)]
150155
struct CratesIoCrate {
@@ -186,7 +191,7 @@ async fn get_search_results(
186191
}
187192
});
188193

189-
let releases: CratesIoSearchResult = retry_async(
194+
let response: CratesIoSearchResult = retry_async(
190195
|| async {
191196
Ok(HTTP_CLIENT
192197
.get(url.clone())
@@ -200,9 +205,21 @@ async fn get_search_results(
200205
.json()
201206
.await?;
202207

208+
if let Some(errors) = response.errors {
209+
let messages: Vec<_> = errors.into_iter().map(|e| e.detail).collect();
210+
bail!("got error from crates.io: {}", messages.join("\n"));
211+
}
212+
213+
let Some(crates) = response.crates else {
214+
bail!("missing releases in crates.io response");
215+
};
216+
217+
let Some(meta) = response.meta else {
218+
bail!("missing metadata in crates.io response");
219+
};
220+
203221
let names = Arc::new(
204-
releases
205-
.crates
222+
crates
206223
.into_iter()
207224
.map(|krate| krate.name)
208225
.collect::<Vec<_>>(),
@@ -261,8 +278,8 @@ async fn get_search_results(
261278
.cloned()
262279
.collect(),
263280
executed_query,
264-
prev_page: releases.meta.prev_page,
265-
next_page: releases.meta.next_page,
281+
prev_page: meta.prev_page,
282+
next_page: meta.next_page,
266283
})
267284
}
268285

@@ -1034,6 +1051,45 @@ mod tests {
10341051
})
10351052
}
10361053

1054+
#[test]
1055+
fn crates_io_errors_as_status_code_200() {
1056+
wrapper(|env| {
1057+
let mut crates_io = mockito::Server::new();
1058+
env.override_config(|config| {
1059+
config.crates_io_api_call_retries = 0;
1060+
config.registry_api_host = crates_io.url().parse().unwrap();
1061+
});
1062+
1063+
let _m = crates_io
1064+
.mock("GET", "/api/v1/crates")
1065+
.match_query(Matcher::AllOf(vec![
1066+
Matcher::UrlEncoded("q".into(), "doesnt_matter_here".into()),
1067+
Matcher::UrlEncoded("per_page".into(), "30".into()),
1068+
]))
1069+
.with_status(200)
1070+
.with_header("content-type", "application/json")
1071+
.with_body(
1072+
json!({
1073+
"errors": [
1074+
{ "detail": "error name 1" },
1075+
{ "detail": "error name 2" },
1076+
]
1077+
})
1078+
.to_string(),
1079+
)
1080+
.create();
1081+
1082+
let response = env
1083+
.frontend()
1084+
.get("/releases/search?query=doesnt_matter_here")
1085+
.send()?;
1086+
assert_eq!(response.status(), 500);
1087+
1088+
assert!(response.text()?.contains("error name 1\nerror name 2"));
1089+
Ok(())
1090+
})
1091+
}
1092+
10371093
#[test_case(StatusCode::NOT_FOUND)]
10381094
#[test_case(StatusCode::INTERNAL_SERVER_ERROR)]
10391095
#[test_case(StatusCode::BAD_GATEWAY)]

0 commit comments

Comments
 (0)