Skip to content

Commit d8888c6

Browse files
authored
controllers/token: Remove last remaining spawn_blocking() call (#9988)
1 parent 57e3bb4 commit d8888c6

File tree

6 files changed

+163
-158
lines changed

6 files changed

+163
-158
lines changed

src/controllers/token.rs

Lines changed: 69 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::views::EncodableApiTokenWithToken;
66
use crate::app::AppState;
77
use crate::auth::AuthCheck;
88
use crate::models::token::{CrateScope, EndpointScope};
9-
use crate::tasks::spawn_blocking;
109
use crate::util::diesel::prelude::*;
1110
use crate::util::errors::{bad_request, AppResult};
1211
use axum::extract::{Path, Query};
@@ -17,7 +16,7 @@ use axum_extra::response::ErasedJson;
1716
use chrono::NaiveDateTime;
1817
use diesel::data_types::PgInterval;
1918
use diesel::dsl::{now, IntervalDsl};
20-
use diesel_async::async_connection_wrapper::AsyncConnectionWrapper;
19+
use diesel_async::RunQueryDsl;
2120
use http::request::Parts;
2221
use http::StatusCode;
2322

@@ -42,8 +41,6 @@ pub async fn list(
4241
Query(params): Query<GetParams>,
4342
req: Parts,
4443
) -> AppResult<ErasedJson> {
45-
use diesel_async::RunQueryDsl;
46-
4744
let mut conn = app.db_read_prefer_primary().await?;
4845
let auth = AuthCheck::only_cookie().check(&req, &mut conn).await?;
4946
let user = auth.user();
@@ -91,89 +88,85 @@ pub async fn new(
9188

9289
let mut conn = app.db_write().await?;
9390
let auth = AuthCheck::default().check(&parts, &mut conn).await?;
94-
spawn_blocking(move || {
95-
use diesel::RunQueryDsl;
96-
97-
let conn: &mut AsyncConnectionWrapper<_> = &mut conn.into();
9891

99-
if auth.api_token_id().is_some() {
100-
return Err(bad_request(
101-
"cannot use an API token to create a new API token",
102-
));
103-
}
92+
if auth.api_token_id().is_some() {
93+
return Err(bad_request(
94+
"cannot use an API token to create a new API token",
95+
));
96+
}
10497

105-
let user = auth.user();
98+
let user = auth.user();
10699

107-
let max_token_per_user = 500;
108-
let count: i64 = ApiToken::belonging_to(user).count().get_result(conn)?;
109-
if count >= max_token_per_user {
110-
return Err(bad_request(format!(
111-
"maximum tokens per user is: {max_token_per_user}"
112-
)));
113-
}
100+
let max_token_per_user = 500;
101+
let count: i64 = ApiToken::belonging_to(user)
102+
.count()
103+
.get_result(&mut conn)
104+
.await?;
105+
if count >= max_token_per_user {
106+
return Err(bad_request(format!(
107+
"maximum tokens per user is: {max_token_per_user}"
108+
)));
109+
}
114110

115-
let crate_scopes = new
116-
.api_token
117-
.crate_scopes
118-
.map(|scopes| {
119-
scopes
120-
.into_iter()
121-
.map(CrateScope::try_from)
122-
.collect::<Result<Vec<_>, _>>()
123-
})
124-
.transpose()
125-
.map_err(|_err| bad_request("invalid crate scope"))?;
126-
127-
let endpoint_scopes = new
128-
.api_token
129-
.endpoint_scopes
130-
.map(|scopes| {
131-
scopes
132-
.into_iter()
133-
.map(|scope| EndpointScope::try_from(scope.as_bytes()))
134-
.collect::<Result<Vec<_>, _>>()
135-
})
136-
.transpose()
137-
.map_err(|_err| bad_request("invalid endpoint scope"))?;
138-
139-
let recipient = user.email(conn)?;
140-
141-
let api_token = ApiToken::insert_with_scopes(
142-
conn,
143-
user.id,
144-
&new.api_token.name,
145-
crate_scopes,
146-
endpoint_scopes,
147-
new.api_token.expired_at,
148-
)?;
149-
150-
if let Some(recipient) = recipient {
151-
let email = NewTokenEmail {
152-
token_name: &new.api_token.name,
153-
user_name: &user.gh_login,
154-
domain: &app.emails.domain,
155-
};
156-
157-
// At this point the token has been created so failing to send the
158-
// email should not cause an error response to be returned to the
159-
// caller.
160-
let email_ret = app.emails.send(&recipient, email);
161-
if let Err(e) = email_ret {
162-
error!("Failed to send token creation email: {e}")
163-
}
111+
let crate_scopes = new
112+
.api_token
113+
.crate_scopes
114+
.map(|scopes| {
115+
scopes
116+
.into_iter()
117+
.map(CrateScope::try_from)
118+
.collect::<Result<Vec<_>, _>>()
119+
})
120+
.transpose()
121+
.map_err(|_err| bad_request("invalid crate scope"))?;
122+
123+
let endpoint_scopes = new
124+
.api_token
125+
.endpoint_scopes
126+
.map(|scopes| {
127+
scopes
128+
.into_iter()
129+
.map(|scope| EndpointScope::try_from(scope.as_bytes()))
130+
.collect::<Result<Vec<_>, _>>()
131+
})
132+
.transpose()
133+
.map_err(|_err| bad_request("invalid endpoint scope"))?;
134+
135+
let recipient = user.async_email(&mut conn).await?;
136+
137+
let api_token = ApiToken::insert_with_scopes(
138+
&mut conn,
139+
user.id,
140+
&new.api_token.name,
141+
crate_scopes,
142+
endpoint_scopes,
143+
new.api_token.expired_at,
144+
)
145+
.await?;
146+
147+
if let Some(recipient) = recipient {
148+
let email = NewTokenEmail {
149+
token_name: &new.api_token.name,
150+
user_name: &user.gh_login,
151+
domain: &app.emails.domain,
152+
};
153+
154+
// At this point the token has been created so failing to send the
155+
// email should not cause an error response to be returned to the
156+
// caller.
157+
let email_ret = app.emails.async_send(&recipient, email).await;
158+
if let Err(e) = email_ret {
159+
error!("Failed to send token creation email: {e}")
164160
}
161+
}
165162

166-
let api_token = EncodableApiTokenWithToken::from(api_token);
163+
let api_token = EncodableApiTokenWithToken::from(api_token);
167164

168-
Ok(json!({ "api_token": api_token }))
169-
})
170-
.await
165+
Ok(json!({ "api_token": api_token }))
171166
}
172167

173168
/// Handles the `GET /me/tokens/:id` route.
174169
pub async fn show(app: AppState, Path(id): Path<i32>, req: Parts) -> AppResult<ErasedJson> {
175-
use diesel_async::RunQueryDsl;
176-
177170
let mut conn = app.db_write().await?;
178171
let auth = AuthCheck::default().check(&req, &mut conn).await?;
179172
let user = auth.user();
@@ -188,8 +181,6 @@ pub async fn show(app: AppState, Path(id): Path<i32>, req: Parts) -> AppResult<E
188181

189182
/// Handles the `DELETE /me/tokens/:id` route.
190183
pub async fn revoke(app: AppState, Path(id): Path<i32>, req: Parts) -> AppResult<ErasedJson> {
191-
use diesel_async::RunQueryDsl;
192-
193184
let mut conn = app.db_write().await?;
194185
let auth = AuthCheck::default().check(&req, &mut conn).await?;
195186
let user = auth.user();
@@ -203,8 +194,6 @@ pub async fn revoke(app: AppState, Path(id): Path<i32>, req: Parts) -> AppResult
203194

204195
/// Handles the `DELETE /tokens/current` route.
205196
pub async fn revoke_current(app: AppState, req: Parts) -> AppResult<Response> {
206-
use diesel_async::RunQueryDsl;
207-
208197
let mut conn = app.db_write().await?;
209198
let auth = AuthCheck::default().check(&req, &mut conn).await?;
210199
let api_token_id = auth

src/models/token.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
mod scopes;
22

33
use chrono::NaiveDateTime;
4-
use diesel_async::AsyncPgConnection;
4+
use diesel::dsl::now;
5+
use diesel::prelude::*;
6+
use diesel_async::scoped_futures::ScopedFutureExt;
7+
use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl};
58

69
pub use self::scopes::{CrateScope, EndpointScope};
710
use crate::models::User;
811
use crate::schema::api_tokens;
9-
use crate::util::diesel::prelude::*;
10-
use crate::util::diesel::Conn;
1112
use crate::util::rfc3339;
1213
use crate::util::token::{HashedToken, PlainToken};
1314

@@ -35,20 +36,22 @@ pub struct ApiToken {
3536

3637
impl ApiToken {
3738
/// Generates a new named API token for a user
38-
pub fn insert(conn: &mut impl Conn, user_id: i32, name: &str) -> QueryResult<CreatedApiToken> {
39-
Self::insert_with_scopes(conn, user_id, name, None, None, None)
39+
pub async fn insert(
40+
conn: &mut AsyncPgConnection,
41+
user_id: i32,
42+
name: &str,
43+
) -> QueryResult<CreatedApiToken> {
44+
Self::insert_with_scopes(conn, user_id, name, None, None, None).await
4045
}
4146

42-
pub fn insert_with_scopes(
43-
conn: &mut impl Conn,
47+
pub async fn insert_with_scopes(
48+
conn: &mut AsyncPgConnection,
4449
user_id: i32,
4550
name: &str,
4651
crate_scopes: Option<Vec<CrateScope>>,
4752
endpoint_scopes: Option<Vec<EndpointScope>>,
4853
expired_at: Option<NaiveDateTime>,
4954
) -> QueryResult<CreatedApiToken> {
50-
use diesel::RunQueryDsl;
51-
5255
let token = PlainToken::generate();
5356

5457
let model: ApiToken = diesel::insert_into(api_tokens::table)
@@ -61,7 +64,8 @@ impl ApiToken {
6164
api_tokens::expired_at.eq(expired_at),
6265
))
6366
.returning(ApiToken::as_returning())
64-
.get_result(conn)?;
67+
.get_result(conn)
68+
.await?;
6569

6670
Ok(CreatedApiToken {
6771
plaintext: token,
@@ -73,10 +77,6 @@ impl ApiToken {
7377
conn: &mut AsyncPgConnection,
7478
token: &HashedToken,
7579
) -> QueryResult<ApiToken> {
76-
use diesel::{dsl::now, update};
77-
use diesel_async::scoped_futures::ScopedFutureExt;
78-
use diesel_async::{AsyncConnection, RunQueryDsl};
79-
8080
let tokens = api_tokens::table
8181
.filter(api_tokens::revoked.eq(false))
8282
.filter(
@@ -91,7 +91,7 @@ impl ApiToken {
9191
let token = conn
9292
.transaction(|conn| {
9393
async move {
94-
update(tokens)
94+
diesel::update(tokens)
9595
.set(api_tokens::last_used_at.eq(now.nullable()))
9696
.returning(ApiToken::as_returning())
9797
.get_result(conn)

src/tests/routes/me/tokens/create.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ async fn create_token_no_name() {
4242
#[tokio::test(flavor = "multi_thread")]
4343
async fn create_token_exceeded_tokens_per_user() {
4444
let (app, _, user) = TestApp::init().with_user().await;
45-
let mut conn = app.db_conn();
45+
let mut conn = app.async_db_conn().await;
4646
let id = user.as_model().id;
4747

4848
for i in 0..1000 {
49-
assert_ok!(ApiToken::insert(&mut conn, id, &format!("token {i}")));
49+
assert_ok!(ApiToken::insert(&mut conn, id, &format!("token {i}")).await);
5050
}
5151

5252
let response = user.put::<()>("/api/v1/me/tokens", NEW_BAR).await;

src/tests/routes/me/tokens/get.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,25 @@ async fn show() {
2727
#[tokio::test(flavor = "multi_thread")]
2828
async fn show_token_with_scopes() {
2929
let (app, _, user) = TestApp::init().with_user().await;
30-
let mut conn = app.db_conn();
30+
let mut conn = app.async_db_conn().await;
3131
let user_model = user.as_model();
3232
let id = user_model.id;
3333

34-
assert_ok!(ApiToken::insert(&mut conn, id, "bar"));
35-
let token = assert_ok!(ApiToken::insert_with_scopes(
36-
&mut conn,
37-
id,
38-
"baz",
39-
Some(vec![
40-
CrateScope::try_from("serde").unwrap(),
41-
CrateScope::try_from("serde-*").unwrap()
42-
]),
43-
Some(vec![EndpointScope::PublishUpdate]),
44-
Some((Utc::now() - Duration::days(31)).naive_utc()),
45-
));
34+
assert_ok!(ApiToken::insert(&mut conn, id, "bar").await);
35+
let token = assert_ok!(
36+
ApiToken::insert_with_scopes(
37+
&mut conn,
38+
id,
39+
"baz",
40+
Some(vec![
41+
CrateScope::try_from("serde").unwrap(),
42+
CrateScope::try_from("serde-*").unwrap()
43+
]),
44+
Some(vec![EndpointScope::PublishUpdate]),
45+
Some((Utc::now() - Duration::days(31)).naive_utc()),
46+
)
47+
.await
48+
);
4649

4750
let url = format!("/api/v1/me/tokens/{}", token.model.id);
4851
let response = user.get::<()>(&url).await;
@@ -63,11 +66,11 @@ async fn show_with_anonymous_user() {
6366
#[tokio::test(flavor = "multi_thread")]
6467
async fn show_other_user_token() {
6568
let (app, _, user1) = TestApp::init().with_user().await;
66-
let mut conn = app.db_conn();
69+
let mut conn = app.async_db_conn().await;
6770
let user2 = app.db_new_user("baz").await;
6871
let user2 = user2.as_model();
6972

70-
let token = assert_ok!(ApiToken::insert(&mut conn, user2.id, "bar"));
73+
let token = assert_ok!(ApiToken::insert(&mut conn, user2.id, "bar").await);
7174

7275
let url = format!("/api/v1/me/tokens/{}", token.model.id);
7376
let response = user1.get::<()>(&url).await;

0 commit comments

Comments
 (0)