Skip to content

Commit 7d0c82f

Browse files
committed
Allow semver in status paths
1 parent 8b388a7 commit 7d0c82f

File tree

2 files changed

+82
-87
lines changed

2 files changed

+82
-87
lines changed

src/web/mod.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,6 @@ impl MatchSemver {
116116
| MatchSemver::Latest((v, i)) => (v, i),
117117
}
118118
}
119-
120-
/// If the matched version was an exact match to a semver version, returns the
121-
/// version string and id for the query. If the lookup required a semver match, returns
122-
/// `VersionNotFound`.
123-
fn exact_version_only(self) -> Result<(String, i32), AxumNope> {
124-
let MatchSemver::Exact(details) = self else { return Err(AxumNope::VersionNotFound) };
125-
Ok(details)
126-
}
127119
}
128120

129121
/// Checks the database for crate releases that match the given name and version.

src/web/status.rs

Lines changed: 82 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,41 @@ use axum::{
1414
pub(crate) async fn status_handler(
1515
Path((name, req_version)): Path<(String, String)>,
1616
Extension(pool): Extension<Pool>,
17-
) -> AxumResult<impl IntoResponse> {
18-
let (_, id) = match_version_axum(&pool, &name, Some(&req_version))
19-
.await?
20-
.exact_name_only()?
21-
.exact_version_only()?;
22-
23-
let rustdoc_status: bool = spawn_blocking({
24-
move || {
25-
Ok(pool
26-
.get()?
27-
.query_one(
28-
"SELECT releases.rustdoc_status
29-
FROM releases
30-
WHERE releases.id = $1
31-
",
32-
&[&id],
33-
)?
34-
.get("rustdoc_status"))
35-
}
36-
})
37-
.await?;
38-
39-
Ok((
17+
) -> impl IntoResponse {
18+
(
4019
Extension(CachePolicy::NoStoreMustRevalidate),
4120
[(ACCESS_CONTROL_ALLOW_ORIGIN, "*")],
42-
Json(serde_json::json!({ "doc_status": rustdoc_status })),
43-
))
21+
// We use an async block to emulate a try block so that we can apply the above CORS header
22+
// and cache policy to both successful and failed responses
23+
async move {
24+
let (version, id) = match_version_axum(&pool, &name, Some(&req_version))
25+
.await?
26+
.exact_name_only()?
27+
.into_parts();
28+
29+
let rustdoc_status: bool = spawn_blocking({
30+
move || {
31+
Ok(pool
32+
.get()?
33+
.query_one(
34+
"SELECT releases.rustdoc_status
35+
FROM releases
36+
WHERE releases.id = $1
37+
",
38+
&[&id],
39+
)?
40+
.get("rustdoc_status"))
41+
}
42+
})
43+
.await?;
44+
45+
AxumResult::Ok(Json(serde_json::json!({
46+
"version": version,
47+
"doc_status": rustdoc_status,
48+
})))
49+
}
50+
.await,
51+
)
4452
}
4553

4654
#[cfg(test)]
@@ -50,93 +58,88 @@ mod tests {
5058
web::cache::CachePolicy,
5159
};
5260
use reqwest::StatusCode;
61+
use test_case::test_case;
5362

54-
#[test]
55-
fn success() {
63+
#[test_case("latest")]
64+
#[test_case("0.1")]
65+
#[test_case("0.1.0")]
66+
fn status(version: &str) {
5667
wrapper(|env| {
5768
env.fake_release().name("foo").version("0.1.0").create()?;
5869

59-
let response = env.frontend().get("/crate/foo/0.1.0/status.json").send()?;
70+
let response = env
71+
.frontend()
72+
.get(&format!("/crate/foo/{version}/status.json"))
73+
.send()?;
6074
assert_cache_control(&response, CachePolicy::NoStoreMustRevalidate, &env.config());
6175
assert_eq!(response.headers()["access-control-allow-origin"], "*");
76+
assert_eq!(response.status(), StatusCode::OK);
6277
let value: serde_json::Value = serde_json::from_str(&response.text()?)?;
6378

64-
assert_eq!(value, serde_json::json!({"doc_status": true}));
79+
assert_eq!(
80+
value,
81+
serde_json::json!({
82+
"version": "0.1.0",
83+
"doc_status": true,
84+
})
85+
);
6586

6687
Ok(())
6788
});
6889
}
6990

70-
#[test]
71-
fn failure() {
91+
#[test_case("latest")]
92+
#[test_case("0.1")]
93+
#[test_case("0.1.0")]
94+
fn failure(version: &str) {
7295
wrapper(|env| {
7396
env.fake_release()
7497
.name("foo")
7598
.version("0.1.0")
7699
.build_result_failed()
77100
.create()?;
78101

79-
let response = env.frontend().get("/crate/foo/0.1.0/status.json").send()?;
102+
let response = env
103+
.frontend()
104+
.get(&format!("/crate/foo/{version}/status.json"))
105+
.send()?;
80106
assert_cache_control(&response, CachePolicy::NoStoreMustRevalidate, &env.config());
81107
assert_eq!(response.headers()["access-control-allow-origin"], "*");
108+
assert_eq!(response.status(), StatusCode::OK);
82109
let value: serde_json::Value = serde_json::from_str(&response.text()?)?;
83110

84-
assert_eq!(value, serde_json::json!({"doc_status": false}));
111+
assert_eq!(
112+
value,
113+
serde_json::json!({
114+
"version": "0.1.0",
115+
"doc_status": false,
116+
})
117+
);
85118

86119
Ok(())
87120
});
88121
}
89122

90-
#[test]
91-
fn crate_version_not_found() {
123+
// crate not found
124+
#[test_case("bar", "0.1")]
125+
#[test_case("bar", "0.1.0")]
126+
// version not found
127+
#[test_case("foo", "0.2")]
128+
#[test_case("foo", "0.2.0")]
129+
// invalid semver
130+
#[test_case("foo", "0,1")]
131+
#[test_case("foo", "0,1,0")]
132+
fn not_found(krate: &str, version: &str) {
92133
wrapper(|env| {
93134
env.fake_release().name("foo").version("0.1.0").create()?;
94135

95-
let response = env.frontend().get("/crate/foo/0.2.0/status.json").send()?;
96-
assert!(response
97-
.url()
98-
.as_str()
99-
.ends_with("/crate/foo/0.2.0/status.json"));
100-
assert_eq!(response.status(), StatusCode::NOT_FOUND);
101-
Ok(())
102-
});
103-
}
104-
105-
#[test]
106-
fn invalid_semver() {
107-
wrapper(|env| {
108-
env.fake_release().name("foo").version("0.1.0").create()?;
109-
110-
let response = env.frontend().get("/crate/foo/0,1,0/status.json").send()?;
111-
assert!(response
112-
.url()
113-
.as_str()
114-
.ends_with("/crate/foo/0,1,0/status.json"));
115-
assert_eq!(response.status(), StatusCode::NOT_FOUND);
116-
Ok(())
117-
});
118-
}
119-
120-
/// We only support asking for the status of exact versions
121-
#[test]
122-
fn no_semver() {
123-
wrapper(|env| {
124-
env.fake_release().name("foo").version("0.1.0").create()?;
125-
126-
let response = env.frontend().get("/crate/foo/latest/status.json").send()?;
127-
assert!(response
128-
.url()
129-
.as_str()
130-
.ends_with("/crate/foo/latest/status.json"));
131-
assert_eq!(response.status(), StatusCode::NOT_FOUND);
132-
133-
let response = env.frontend().get("/crate/foo/0.1/status.json").send()?;
134-
assert!(response
135-
.url()
136-
.as_str()
137-
.ends_with("/crate/foo/0.1/status.json"));
136+
let response = env
137+
.frontend()
138+
.get(&format!("/crate/{krate}/{version}/status.json"))
139+
.send()?;
140+
assert_cache_control(&response, CachePolicy::NoStoreMustRevalidate, &env.config());
141+
assert_eq!(response.headers()["access-control-allow-origin"], "*");
138142
assert_eq!(response.status(), StatusCode::NOT_FOUND);
139-
140143
Ok(())
141144
});
142145
}

0 commit comments

Comments
 (0)