Skip to content

Commit 72dfbe2

Browse files
committed
trustpub: Change PUT /api/v1/trusted_publishing/tokens endpoint to POST
`PUT` should be used for updates, not for resource creation. The conventional HTTP method for resource creation in REST APIs is `POST`. This also matches what PyPI is using.
1 parent a4b745f commit 72dfbe2

File tree

3 files changed

+20
-20
lines changed

3 files changed

+20
-20
lines changed

src/controllers/trustpub/tokens/exchange/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ mod tests;
2020

2121
/// Exchange an OIDC token for a temporary access token.
2222
#[utoipa::path(
23-
put,
23+
post,
2424
path = "/api/v1/trusted_publishing/tokens",
2525
request_body = inline(json::ExchangeRequest),
2626
tag = "trusted_publishing",

src/controllers/trustpub/tokens/exchange/tests.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ async fn test_happy_path() -> anyhow::Result<()> {
7272
let client = prepare().await?;
7373

7474
let body = default_claims().as_exchange_body()?;
75-
let response = client.put::<()>(URL, body).await;
75+
let response = client.post::<()>(URL, body).await;
7676
assert_snapshot!(response.status(), @"200 OK");
7777

7878
let json = response.json();
@@ -108,7 +108,7 @@ async fn test_happy_path_with_environment() -> anyhow::Result<()> {
108108
claims.environment = Some("prod".into());
109109

110110
let body = claims.as_exchange_body()?;
111-
let response = client.put::<()>(URL, body).await;
111+
let response = client.post::<()>(URL, body).await;
112112
assert_snapshot!(response.status(), @"200 OK");
113113

114114
Ok(())
@@ -122,7 +122,7 @@ async fn test_happy_path_with_ignored_environment() -> anyhow::Result<()> {
122122
claims.environment = Some("prod".into());
123123

124124
let body = claims.as_exchange_body()?;
125-
let response = client.put::<()>(URL, body).await;
125+
let response = client.post::<()>(URL, body).await;
126126
assert_snapshot!(response.status(), @"200 OK");
127127

128128
Ok(())
@@ -133,7 +133,7 @@ async fn test_broken_jwt() -> anyhow::Result<()> {
133133
let client = prepare().await?;
134134

135135
let body = serde_json::to_vec(&json!({ "jwt": "broken" }))?;
136-
let response = client.put::<()>(URL, body).await;
136+
let response = client.post::<()>(URL, body).await;
137137
assert_snapshot!(response.status(), @"400 Bad Request");
138138
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"Failed to decode JWT"}]}"#);
139139

@@ -154,7 +154,7 @@ async fn test_unsupported_issuer() -> anyhow::Result<()> {
154154
new_oidc_config(krate.id).insert(&mut conn).await?;
155155

156156
let body = default_claims().as_exchange_body()?;
157-
let response = client.put::<()>(URL, body).await;
157+
let response = client.post::<()>(URL, body).await;
158158
assert_snapshot!(response.status(), @"400 Bad Request");
159159
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"Unsupported JWT issuer"}]}"#);
160160

@@ -170,7 +170,7 @@ async fn test_missing_key_id() -> anyhow::Result<()> {
170170
let jwt = jsonwebtoken::encode(&Header::default(), &claims, &secret_key)?;
171171
let body = serde_json::to_vec(&json!({ "jwt": jwt }))?;
172172

173-
let response = client.put::<()>(URL, body).await;
173+
let response = client.post::<()>(URL, body).await;
174174
assert_snapshot!(response.status(), @"400 Bad Request");
175175
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"Missing JWT key ID"}]}"#);
176176

@@ -198,7 +198,7 @@ async fn test_unknown_key() -> anyhow::Result<()> {
198198
new_oidc_config(krate.id).insert(&mut conn).await?;
199199

200200
let body = default_claims().as_exchange_body()?;
201-
let response = client.put::<()>(URL, body).await;
201+
let response = client.post::<()>(URL, body).await;
202202
assert_snapshot!(response.status(), @"400 Bad Request");
203203
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"Invalid JWT key ID"}]}"#);
204204

@@ -226,7 +226,7 @@ async fn test_key_store_error() -> anyhow::Result<()> {
226226
new_oidc_config(krate.id).insert(&mut conn).await?;
227227

228228
let body = default_claims().as_exchange_body()?;
229-
let response = client.put::<()>(URL, body).await;
229+
let response = client.post::<()>(URL, body).await;
230230
assert_snapshot!(response.status(), @"500 Internal Server Error");
231231
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"Failed to load OIDC key set"}]}"#);
232232

@@ -241,7 +241,7 @@ async fn test_invalid_audience() -> anyhow::Result<()> {
241241
claims.aud = "invalid-audience".into();
242242

243243
let body = claims.as_exchange_body()?;
244-
let response = client.put::<()>(URL, body).await;
244+
let response = client.post::<()>(URL, body).await;
245245
assert_snapshot!(response.status(), @"400 Bad Request");
246246
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"Failed to decode JWT"}]}"#);
247247

@@ -256,11 +256,11 @@ async fn test_token_reuse() -> anyhow::Result<()> {
256256
let body = default_claims().as_exchange_body()?;
257257

258258
// The first exchange should succeed
259-
let response = client.put::<()>(URL, body.clone()).await;
259+
let response = client.post::<()>(URL, body.clone()).await;
260260
assert_snapshot!(response.status(), @"200 OK");
261261

262262
// The second exchange should fail
263-
let response = client.put::<()>(URL, body).await;
263+
let response = client.post::<()>(URL, body).await;
264264
assert_snapshot!(response.status(), @"400 Bad Request");
265265
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"JWT has already been used"}]}"#);
266266

@@ -275,7 +275,7 @@ async fn test_invalid_repository() -> anyhow::Result<()> {
275275
claims.repository = "what?".into();
276276

277277
let body = claims.as_exchange_body()?;
278-
let response = client.put::<()>(URL, body).await;
278+
let response = client.post::<()>(URL, body).await;
279279
assert_snapshot!(response.status(), @"400 Bad Request");
280280
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"Unexpected `repository` value"}]}"#);
281281

@@ -290,7 +290,7 @@ async fn test_invalid_workflow() -> anyhow::Result<()> {
290290
claims.workflow_ref = "what?".into();
291291

292292
let body = claims.as_exchange_body()?;
293-
let response = client.put::<()>(URL, body).await;
293+
let response = client.post::<()>(URL, body).await;
294294
assert_snapshot!(response.status(), @"400 Bad Request");
295295
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"Unexpected `workflow_ref` value"}]}"#);
296296

@@ -305,7 +305,7 @@ async fn test_invalid_owner_id() -> anyhow::Result<()> {
305305
claims.repository_owner_id = "what?".into();
306306

307307
let body = claims.as_exchange_body()?;
308-
let response = client.put::<()>(URL, body).await;
308+
let response = client.post::<()>(URL, body).await;
309309
assert_snapshot!(response.status(), @"400 Bad Request");
310310
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"Unexpected `repository_owner_id` value"}]}"#);
311311

@@ -320,7 +320,7 @@ async fn test_missing_config() -> anyhow::Result<()> {
320320
.await;
321321

322322
let body = default_claims().as_exchange_body()?;
323-
let response = client.put::<()>(URL, body).await;
323+
let response = client.post::<()>(URL, body).await;
324324
assert_snapshot!(response.status(), @"400 Bad Request");
325325
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"No matching Trusted Publishing config found"}]}"#);
326326

@@ -332,7 +332,7 @@ async fn test_missing_environment() -> anyhow::Result<()> {
332332
let client = prepare_with_config(|c| c.environment = Some("prod")).await?;
333333

334334
let body = default_claims().as_exchange_body()?;
335-
let response = client.put::<()>(URL, body).await;
335+
let response = client.post::<()>(URL, body).await;
336336
assert_snapshot!(response.status(), @"400 Bad Request");
337337
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"No matching Trusted Publishing config found"}]}"#);
338338

@@ -347,7 +347,7 @@ async fn test_wrong_environment() -> anyhow::Result<()> {
347347
claims.environment = Some("not-prod".into());
348348

349349
let body = claims.as_exchange_body()?;
350-
let response = client.put::<()>(URL, body).await;
350+
let response = client.post::<()>(URL, body).await;
351351
assert_snapshot!(response.status(), @"400 Bad Request");
352352
assert_snapshot!(response.json(), @r#"{"errors":[{"detail":"No matching Trusted Publishing config found"}]}"#);
353353

@@ -369,7 +369,7 @@ async fn test_case_insensitive() -> anyhow::Result<()> {
369369
.build();
370370

371371
let body = claims.as_exchange_body()?;
372-
let response = client.put::<()>(URL, body).await;
372+
let response = client.post::<()>(URL, body).await;
373373
assert_snapshot!(response.status(), @"200 OK");
374374

375375
Ok(())

src/snapshots/crates_io__openapi__tests__openapi_snapshot-2.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4328,7 +4328,7 @@ expression: response.json()
43284328
"trusted_publishing"
43294329
]
43304330
},
4331-
"put": {
4331+
"post": {
43324332
"operationId": "exchange_trustpub_token",
43334333
"requestBody": {
43344334
"content": {

0 commit comments

Comments
 (0)