Skip to content

Convert all request handlers to async functions #5859

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 51 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
a9a3cd9
GET /crates: Convert to async fn
Turbo87 Jan 5, 2023
ee95c35
PUT /crates/new: Convert to async fn
Turbo87 Jan 5, 2023
8f2579b
GET /crates/:crate_id/owners: Convert to async fn
Turbo87 Jan 5, 2023
d2b2ea3
PUT /crates/:crate_id/owners: Convert to async fn
Turbo87 Jan 5, 2023
816aa82
DELETE /crates/:crate_id/owners: Convert to async fn
Turbo87 Jan 5, 2023
81e3c5a
DELETE /crates/:crate_id/:version/yank: Convert to async fn
Turbo87 Jan 5, 2023
77fe098
PUT /crates/:crate_id/:version/unyank: Convert to async fn
Turbo87 Jan 5, 2023
c3de39c
GET /crates/:crate_id/:version/download: Convert to async fn
Turbo87 Jan 5, 2023
0c7037e
GET /versions: Convert to async fn
Turbo87 Jan 5, 2023
7fd9b1a
GET /versions/:version_id: Convert to async fn
Turbo87 Jan 5, 2023
c444aef
GET /crates/:crate_id: Convert to async fn
Turbo87 Jan 5, 2023
7657cee
GET /crates/:crate_id/:version: Convert to async fn
Turbo87 Jan 5, 2023
eb44b40
GET /crates/:crate_id/:version/readme: Convert to async fn
Turbo87 Jan 5, 2023
7c8806b
GET /crates/:crate_id/:version/dependencies: Convert to async fn
Turbo87 Jan 5, 2023
75301b3
GET /crates/:crate_id/:version/downloads: Convert to async fn
Turbo87 Jan 5, 2023
3200aab
GET /crates/:crate_id/:version/authors: Convert to async fn
Turbo87 Jan 5, 2023
50269a8
GET /crates/:crate_id/downloads: Convert to async fn
Turbo87 Jan 5, 2023
d1df03a
GET /crates/:crate_id/versions: Convert to async fn
Turbo87 Jan 5, 2023
2c4b8c2
PUT /crates/:crate_id/follow: Convert to async fn
Turbo87 Jan 5, 2023
15d1eaa
DELETE /crates/:crate_id/follow: Convert to async fn
Turbo87 Jan 5, 2023
cd29dcd
GET /crates/:crate_id/following: Convert to async fn
Turbo87 Jan 5, 2023
6653488
GET /crates/:crate_id/owner_team: Convert to async fn
Turbo87 Jan 5, 2023
ffb82da
GET /crates/:crate_id/owner_user: Convert to async fn
Turbo87 Jan 5, 2023
b4822fd
GET /crates/:crate_id/reverse_dependencies: Convert to async fn
Turbo87 Jan 5, 2023
25038e9
GET /categories: Convert to async fn
Turbo87 Jan 5, 2023
dffac05
GET /categories/:category_id: Convert to async fn
Turbo87 Jan 5, 2023
6ad8f3a
GET /category_slugs: Convert to async fn
Turbo87 Jan 5, 2023
24c10c2
GET /users/:user_id: Convert to async fn
Turbo87 Jan 5, 2023
67a5b97
PUT /users/:user_id: Convert to async fn
Turbo87 Jan 5, 2023
c008bd6
GET /users/:user_id/stats: Convert to async fn
Turbo87 Jan 5, 2023
e17e26c
GET /teams/:team_id: Convert to async fn
Turbo87 Jan 5, 2023
5206516
GET /session/authorize: Convert to async fn
Turbo87 Jan 5, 2023
f2a7746
GET /me: Convert to async fn
Turbo87 Jan 5, 2023
15702ad
GET /me/updates: Convert to async fn
Turbo87 Jan 5, 2023
e02c292
GET /me/tokens: Convert to async fn
Turbo87 Jan 5, 2023
4317888
PUT /me/tokens: Convert to async fn
Turbo87 Jan 5, 2023
3915296
DELETE /me/tokens/:id: Convert to async fn
Turbo87 Jan 5, 2023
c22703f
DELETE /tokens/current: Convert to async fn
Turbo87 Jan 5, 2023
c912975
GET /me/crate_owner_invitations: Convert to async fn
Turbo87 Jan 5, 2023
956bfae
PUT /me/crate_owner_invitations/:crate_id: Convert to async fn
Turbo87 Jan 5, 2023
62816d6
PUT /me/crate_owner_invitations/accept/:token: Convert to async fn
Turbo87 Jan 5, 2023
1eaecde
PUT /me/email_notifications: Convert to async fn
Turbo87 Jan 5, 2023
e4f710d
GET /summary: Convert to async fn
Turbo87 Jan 5, 2023
1e420a1
PUT /confirm/:email_token: Convert to async fn
Turbo87 Jan 5, 2023
c3ec214
PUT /users/:user_id/resend: Convert to async fn
Turbo87 Jan 5, 2023
1e7458a
GET /session/begin: Convert to async fn
Turbo87 Jan 5, 2023
ef54a3a
DELETE /session: Convert to async fn
Turbo87 Jan 5, 2023
40bfc45
GET /metrics/:kind: Convert to async fn
Turbo87 Jan 5, 2023
02b699c
GET /crate_owner_invitations: Convert to async fn
Turbo87 Jan 5, 2023
917c90f
POST /github/secret-scanning/verify: Convert to async fn
Turbo87 Jan 5, 2023
d59cc90
router: Remove obsolete `conduit()` fn
Turbo87 Jan 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 69 additions & 60 deletions src/controllers/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,77 +6,86 @@ use crate::schema::categories;
use crate::views::{EncodableCategory, EncodableCategoryWithSubcategories};

/// Handles the `GET /categories` route.
pub fn index(req: ConduitRequest) -> AppResult<Json<Value>> {
let query = req.query();
// FIXME: There are 69 categories, 47 top level. This isn't going to
// grow by an OoM. We need a limit for /summary, but we don't need
// to paginate this.
let options = PaginationOptions::builder().gather(&req)?;
let offset = options.offset().unwrap_or_default();
let sort = query.get("sort").map_or("alpha", String::as_str);
pub async fn index(req: ConduitRequest) -> AppResult<Json<Value>> {
conduit_compat(move || {
let query = req.query();
// FIXME: There are 69 categories, 47 top level. This isn't going to
// grow by an OoM. We need a limit for /summary, but we don't need
// to paginate this.
let options = PaginationOptions::builder().gather(&req)?;
let offset = options.offset().unwrap_or_default();
let sort = query.get("sort").map_or("alpha", String::as_str);

let conn = req.app().db_read()?;
let categories =
Category::toplevel(&conn, sort, i64::from(options.per_page), i64::from(offset))?;
let categories = categories
.into_iter()
.map(Category::into)
.collect::<Vec<EncodableCategory>>();
let conn = req.app().db_read()?;
let categories =
Category::toplevel(&conn, sort, i64::from(options.per_page), i64::from(offset))?;
let categories = categories
.into_iter()
.map(Category::into)
.collect::<Vec<EncodableCategory>>();

// Query for the total count of categories
let total = Category::count_toplevel(&conn)?;
// Query for the total count of categories
let total = Category::count_toplevel(&conn)?;

Ok(Json(json!({
"categories": categories,
"meta": { "total": total },
})))
Ok(Json(json!({
"categories": categories,
"meta": { "total": total },
})))
})
.await
}

/// Handles the `GET /categories/:category_id` route.
pub fn show(req: ConduitRequest) -> AppResult<Json<Value>> {
let slug = req.param("category_id").unwrap();
let conn = req.app().db_read()?;
let cat: Category = Category::by_slug(slug).first(&*conn)?;
let subcats = cat
.subcategories(&conn)?
.into_iter()
.map(Category::into)
.collect();
let parents = cat
.parent_categories(&conn)?
.into_iter()
.map(Category::into)
.collect();
pub async fn show(req: ConduitRequest) -> AppResult<Json<Value>> {
conduit_compat(move || {
let slug = req.param("category_id").unwrap();
let conn = req.app().db_read()?;
let cat: Category = Category::by_slug(slug).first(&*conn)?;
let subcats = cat
.subcategories(&conn)?
.into_iter()
.map(Category::into)
.collect();
let parents = cat
.parent_categories(&conn)?
.into_iter()
.map(Category::into)
.collect();

let cat = EncodableCategory::from(cat);
let cat_with_subcats = EncodableCategoryWithSubcategories {
id: cat.id,
category: cat.category,
slug: cat.slug,
description: cat.description,
created_at: cat.created_at,
crates_cnt: cat.crates_cnt,
subcategories: subcats,
parent_categories: parents,
};
let cat = EncodableCategory::from(cat);
let cat_with_subcats = EncodableCategoryWithSubcategories {
id: cat.id,
category: cat.category,
slug: cat.slug,
description: cat.description,
created_at: cat.created_at,
crates_cnt: cat.crates_cnt,
subcategories: subcats,
parent_categories: parents,
};

Ok(Json(json!({ "category": cat_with_subcats })))
Ok(Json(json!({ "category": cat_with_subcats })))
})
.await
}

/// Handles the `GET /category_slugs` route.
pub fn slugs(req: ConduitRequest) -> AppResult<Json<Value>> {
let conn = req.app().db_read()?;
let slugs: Vec<Slug> = categories::table
.select((categories::slug, categories::slug, categories::description))
.order(categories::slug)
.load(&*conn)?;
pub async fn slugs(req: ConduitRequest) -> AppResult<Json<Value>> {
conduit_compat(move || {
let conn = req.app().db_read()?;
let slugs: Vec<Slug> = categories::table
.select((categories::slug, categories::slug, categories::description))
.order(categories::slug)
.load(&*conn)?;

#[derive(Serialize, Queryable)]
struct Slug {
id: String,
slug: String,
description: String,
}
#[derive(Serialize, Queryable)]
struct Slug {
id: String,
slug: String,
description: String,
}

Ok(Json(json!({ "category_slugs": slugs })))
Ok(Json(json!({ "category_slugs": slugs })))
})
.await
}
164 changes: 88 additions & 76 deletions src/controllers/crate_owner_invitation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,55 +16,61 @@ use indexmap::IndexMap;
use std::collections::{HashMap, HashSet};

/// Handles the `GET /api/v1/me/crate_owner_invitations` route.
pub fn list(req: ConduitRequest) -> AppResult<Json<Value>> {
let auth = AuthCheck::only_cookie().check(&req)?;
let user_id = auth.user_id();

let PrivateListResponse {
invitations, users, ..
} = prepare_list(&req, auth, ListFilter::InviteeId(user_id))?;

// The schema for the private endpoints is converted to the schema used by v1 endpoints.
let crate_owner_invitations = invitations
.into_iter()
.map(|private| {
Ok(EncodableCrateOwnerInvitationV1 {
invited_by_username: users
.iter()
.find(|u| u.id == private.inviter_id)
.ok_or_else(|| internal(&format!("missing user {}", private.inviter_id)))?
.login
.clone(),
invitee_id: private.invitee_id,
inviter_id: private.inviter_id,
crate_name: private.crate_name,
crate_id: private.crate_id,
created_at: private.created_at,
expires_at: private.expires_at,
pub async fn list(req: ConduitRequest) -> AppResult<Json<Value>> {
conduit_compat(move || {
let auth = AuthCheck::only_cookie().check(&req)?;
let user_id = auth.user_id();

let PrivateListResponse {
invitations, users, ..
} = prepare_list(&req, auth, ListFilter::InviteeId(user_id))?;

// The schema for the private endpoints is converted to the schema used by v1 endpoints.
let crate_owner_invitations = invitations
.into_iter()
.map(|private| {
Ok(EncodableCrateOwnerInvitationV1 {
invited_by_username: users
.iter()
.find(|u| u.id == private.inviter_id)
.ok_or_else(|| internal(&format!("missing user {}", private.inviter_id)))?
.login
.clone(),
invitee_id: private.invitee_id,
inviter_id: private.inviter_id,
crate_name: private.crate_name,
crate_id: private.crate_id,
created_at: private.created_at,
expires_at: private.expires_at,
})
})
})
.collect::<AppResult<Vec<EncodableCrateOwnerInvitationV1>>>()?;
.collect::<AppResult<Vec<EncodableCrateOwnerInvitationV1>>>()?;

Ok(Json(json!({
"crate_owner_invitations": crate_owner_invitations,
"users": users,
})))
Ok(Json(json!({
"crate_owner_invitations": crate_owner_invitations,
"users": users,
})))
})
.await
}

/// Handles the `GET /api/private/crate_owner_invitations` route.
pub fn private_list(req: ConduitRequest) -> AppResult<Json<PrivateListResponse>> {
let auth = AuthCheck::only_cookie().check(&req)?;

let filter = if let Some(crate_name) = req.query().get("crate_name") {
ListFilter::CrateName(crate_name.clone())
} else if let Some(id) = req.query().get("invitee_id").and_then(|i| i.parse().ok()) {
ListFilter::InviteeId(id)
} else {
return Err(bad_request("missing or invalid filter"));
};
pub async fn private_list(req: ConduitRequest) -> AppResult<Json<PrivateListResponse>> {
conduit_compat(move || {
let auth = AuthCheck::only_cookie().check(&req)?;

let filter = if let Some(crate_name) = req.query().get("crate_name") {
ListFilter::CrateName(crate_name.clone())
} else if let Some(id) = req.query().get("invitee_id").and_then(|i| i.parse().ok()) {
ListFilter::InviteeId(id)
} else {
return Err(bad_request("missing or invalid filter"));
};

let list = prepare_list(&req, auth, filter)?;
Ok(Json(list))
let list = prepare_list(&req, auth, filter)?;
Ok(Json(list))
})
.await
}

enum ListFilter {
Expand Down Expand Up @@ -250,45 +256,51 @@ struct OwnerInvitation {
}

/// Handles the `PUT /api/v1/me/crate_owner_invitations/:crate_id` route.
pub fn handle_invite(mut req: ConduitRequest) -> AppResult<Json<Value>> {
let crate_invite: OwnerInvitation =
serde_json::from_reader(req.body_mut()).map_err(|_| bad_request("invalid json request"))?;
pub async fn handle_invite(mut req: ConduitRequest) -> AppResult<Json<Value>> {
conduit_compat(move || {
let crate_invite: OwnerInvitation = serde_json::from_reader(req.body_mut())
.map_err(|_| bad_request("invalid json request"))?;

let crate_invite = crate_invite.crate_owner_invite;
let crate_invite = crate_invite.crate_owner_invite;

let auth = AuthCheck::default().check(&req)?;
let user_id = auth.user_id();
let auth = AuthCheck::default().check(&req)?;
let user_id = auth.user_id();

let state = req.app();
let conn = &*state.db_write()?;
let config = &state.config;
let state = req.app();
let conn = &*state.db_write()?;
let config = &state.config;

let invitation = CrateOwnerInvitation::find_by_id(user_id, crate_invite.crate_id, conn)?;
if crate_invite.accepted {
invitation.accept(conn, config)?;
} else {
invitation.decline(conn)?;
}
let invitation = CrateOwnerInvitation::find_by_id(user_id, crate_invite.crate_id, conn)?;
if crate_invite.accepted {
invitation.accept(conn, config)?;
} else {
invitation.decline(conn)?;
}

Ok(Json(json!({ "crate_owner_invitation": crate_invite })))
Ok(Json(json!({ "crate_owner_invitation": crate_invite })))
})
.await
}

/// Handles the `PUT /api/v1/me/crate_owner_invitations/accept/:token` route.
pub fn handle_invite_with_token(req: ConduitRequest) -> AppResult<Json<Value>> {
let state = req.app();
let config = &state.config;
let conn = state.db_write()?;

let req_token = req.param("token").unwrap();

let invitation = CrateOwnerInvitation::find_by_token(req_token, &conn)?;
let crate_id = invitation.crate_id;
invitation.accept(&conn, config)?;

Ok(Json(json!({
"crate_owner_invitation": {
"crate_id": crate_id,
"accepted": true,
},
})))
pub async fn handle_invite_with_token(req: ConduitRequest) -> AppResult<Json<Value>> {
conduit_compat(move || {
let state = req.app();
let config = &state.config;
let conn = state.db_write()?;

let req_token = req.param("token").unwrap();

let invitation = CrateOwnerInvitation::find_by_token(req_token, &conn)?;
let crate_id = invitation.crate_id;
invitation.accept(&conn, config)?;

Ok(Json(json!({
"crate_owner_invitation": {
"crate_id": crate_id,
"accepted": true,
},
})))
})
.await
}
Loading