Skip to content

Commit 9a6d16f

Browse files
authored
Add support for message query parameter when deleting crates (#10342)
1 parent c87df21 commit 9a6d16f

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

src/controllers/krate/delete.rs

+48-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::models::{NewDeletedCrate, Rights};
66
use crate::schema::{crate_downloads, crates, dependencies};
77
use crate::util::errors::{custom, AppResult, BoxedAppError};
88
use crate::worker::jobs;
9+
use axum::extract::rejection::QueryRejection;
10+
use axum::extract::{FromRequestParts, Query};
911
use bigdecimal::ToPrimitive;
1012
use chrono::{TimeDelta, Utc};
1113
use crates_io_database::schema::deleted_crates;
@@ -19,6 +21,19 @@ use http::StatusCode;
1921
const DOWNLOADS_PER_MONTH_LIMIT: u64 = 500;
2022
const AVAILABLE_AFTER: TimeDelta = TimeDelta::hours(24);
2123

24+
#[derive(Debug, Deserialize, FromRequestParts, utoipa::IntoParams)]
25+
#[from_request(via(Query), rejection(QueryRejection))]
26+
#[into_params(parameter_in = Query)]
27+
pub struct DeleteQueryParams {
28+
message: Option<String>,
29+
}
30+
31+
impl DeleteQueryParams {
32+
pub fn message(&self) -> Option<&str> {
33+
self.message.as_deref().filter(|m| !m.is_empty())
34+
}
35+
}
36+
2237
/// Delete a crate.
2338
///
2439
/// The crate is immediately deleted from the database, and with a small delay
@@ -31,12 +46,17 @@ const AVAILABLE_AFTER: TimeDelta = TimeDelta::hours(24);
3146
#[utoipa::path(
3247
delete,
3348
path = "/api/v1/crates/{name}",
34-
params(CratePath),
49+
params(CratePath, DeleteQueryParams),
3550
security(("cookie" = [])),
3651
tag = "crates",
3752
responses((status = 200, description = "Successful Response")),
3853
)]
39-
pub async fn delete_crate(path: CratePath, parts: Parts, app: AppState) -> AppResult<StatusCode> {
54+
pub async fn delete_crate(
55+
path: CratePath,
56+
params: DeleteQueryParams,
57+
parts: Parts,
58+
app: AppState,
59+
) -> AppResult<StatusCode> {
4060
let mut conn = app.db_write().await?;
4161

4262
// Check that the user is authenticated
@@ -96,6 +116,7 @@ pub async fn delete_crate(path: CratePath, parts: Parts, app: AppState) -> AppRe
96116
.deleted_at(&deleted_at)
97117
.deleted_by(user.id)
98118
.available_at(&available_at)
119+
.maybe_message(params.message())
99120
.build();
100121

101122
diesel::insert_into(deleted_crates::table)
@@ -200,12 +221,36 @@ mod tests {
200221
use crate::models::OwnerKind;
201222
use crate::tests::builders::{DependencyBuilder, PublishBuilder};
202223
use crate::tests::util::{RequestHelper, Response, TestApp};
224+
use axum::RequestPartsExt;
203225
use crates_io_database::schema::crate_owners;
204226
use diesel_async::AsyncPgConnection;
205-
use http::StatusCode;
227+
use http::{Request, StatusCode};
206228
use insta::assert_snapshot;
207229
use serde_json::json;
208230

231+
#[tokio::test]
232+
async fn test_query_params() -> anyhow::Result<()> {
233+
let check = |uri| async move {
234+
let request = Request::builder().uri(uri).body(())?;
235+
let (mut parts, _) = request.into_parts();
236+
Ok::<_, anyhow::Error>(parts.extract::<DeleteQueryParams>().await?)
237+
};
238+
239+
let params = check("/api/v1/crates/foo").await?;
240+
assert_none!(params.message);
241+
242+
let params = check("/api/v1/crates/foo?").await?;
243+
assert_none!(params.message);
244+
245+
let params = check("/api/v1/crates/foo?message=").await?;
246+
assert_eq!(assert_some!(params.message), "");
247+
248+
let params = check("/api/v1/crates/foo?message=hello%20world").await?;
249+
assert_eq!(assert_some!(params.message), "hello world");
250+
251+
Ok(())
252+
}
253+
209254
#[tokio::test(flavor = "multi_thread")]
210255
async fn test_happy_path_new_crate() -> anyhow::Result<()> {
211256
let (app, anon, user) = TestApp::full().with_user().await;

src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap

+8
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,14 @@ expression: response.json()
492492
"schema": {
493493
"type": "string"
494494
}
495+
},
496+
{
497+
"in": "query",
498+
"name": "message",
499+
"required": false,
500+
"schema": {
501+
"type": "string"
502+
}
495503
}
496504
],
497505
"responses": {

0 commit comments

Comments
 (0)