diff --git a/Cargo.lock b/Cargo.lock index 30f6c64c6c3..2d27c5e3a4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,9 +567,11 @@ dependencies = [ "multer", "pin-project-lite", "serde", + "serde_json", "tower", "tower-layer", "tower-service", + "typed-json", ] [[package]] @@ -5318,6 +5320,16 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" +[[package]] +name = "typed-json" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6024a8d0025400b3f6b189366e9aa92012cf9c4fe1cd2620848dd61425c49eed" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index 4e75c423817..db5c29dcb05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ aws-ip-ranges = "=0.913.0" aws-sdk-cloudfront = "=1.52.0" aws-sdk-sqs = "=1.49.0" axum = { version = "=0.7.8", features = ["macros", "matched-path"] } -axum-extra = { version = "=0.9.5", features = ["cookie-signed", "typed-header"] } +axum-extra = { version = "=0.9.5", features = ["cookie-signed", "erased-json", "typed-header"] } base64 = "=0.22.1" bigdecimal = { version = "=0.4.6", features = ["serde"] } bon = "=3.0.0" diff --git a/src/controllers/category.rs b/src/controllers/category.rs index 1099ef56aa4..6384fcf880c 100644 --- a/src/controllers/category.rs +++ b/src/controllers/category.rs @@ -6,14 +6,14 @@ use crate::util::errors::AppResult; use crate::util::RequestUtils; use crate::views::{EncodableCategory, EncodableCategoryWithSubcategories}; use axum::extract::Path; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel::QueryDsl; use diesel_async::RunQueryDsl; use http::request::Parts; -use serde_json::Value; /// Handles the `GET /categories` route. -pub async fn index(app: AppState, req: Parts) -> AppResult> { +pub async fn index(app: AppState, req: Parts) -> AppResult { // 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. @@ -35,14 +35,14 @@ pub async fn index(app: AppState, req: Parts) -> AppResult> { // Query for the total count of categories let total = Category::count_toplevel(&mut conn).await?; - Ok(Json(json!({ + Ok(json!({ "categories": categories, "meta": { "total": total }, - }))) + })) } /// Handles the `GET /categories/:category_id` route. -pub async fn show(state: AppState, Path(slug): Path) -> AppResult> { +pub async fn show(state: AppState, Path(slug): Path) -> AppResult { let mut conn = state.db_read().await?; let cat: Category = Category::by_slug(&slug).first(&mut conn).await?; @@ -71,11 +71,11 @@ pub async fn show(state: AppState, Path(slug): Path) -> AppResult AppResult> { +pub async fn slugs(state: AppState) -> AppResult { let mut conn = state.db_read().await?; let slugs: Vec = categories::table @@ -91,5 +91,5 @@ pub async fn slugs(state: AppState) -> AppResult> { description: String, } - Ok(Json(json!({ "category_slugs": slugs }))) + Ok(json!({ "category_slugs": slugs })) } diff --git a/src/controllers/crate_owner_invitation.rs b/src/controllers/crate_owner_invitation.rs index a882376479e..b36a6c8017c 100644 --- a/src/controllers/crate_owner_invitation.rs +++ b/src/controllers/crate_owner_invitation.rs @@ -15,18 +15,19 @@ use crate::views::{ }; use axum::extract::Path; use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use chrono::{Duration, Utc}; use diesel::pg::Pg; use diesel::sql_types::Bool; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; use indexmap::IndexMap; -use serde_json::Value; use std::collections::{HashMap, HashSet}; use tokio::runtime::Handle; /// Handles the `GET /api/v1/me/crate_owner_invitations` route. -pub async fn list(app: AppState, req: Parts) -> AppResult> { +pub async fn list(app: AppState, req: Parts) -> AppResult { let mut conn = app.db_read().await?; let auth = AuthCheck::only_cookie().check(&req, &mut conn).await?; spawn_blocking(move || { @@ -59,10 +60,10 @@ pub async fn list(app: AppState, req: Parts) -> AppResult> { }) .collect::>>()?; - Ok(Json(json!({ + Ok(json!({ "crate_owner_invitations": crate_owner_invitations, "users": users, - }))) + })) }) .await } @@ -275,7 +276,7 @@ struct OwnerInvitation { } /// Handles the `PUT /api/v1/me/crate_owner_invitations/:crate_id` route. -pub async fn handle_invite(state: AppState, req: BytesRequest) -> AppResult> { +pub async fn handle_invite(state: AppState, req: BytesRequest) -> AppResult { let (parts, body) = req.0.into_parts(); let crate_invite: OwnerInvitation = @@ -299,14 +300,14 @@ pub async fn handle_invite(state: AppState, req: BytesRequest) -> AppResult, -) -> AppResult> { +) -> AppResult { let mut conn = state.db_write().await?; let invitation = CrateOwnerInvitation::find_by_token(&token, &mut conn).await?; @@ -315,10 +316,10 @@ pub async fn handle_invite_with_token( let crate_id = invitation.crate_id; invitation.accept(&mut conn, config).await?; - Ok(Json(json!({ + Ok(json!({ "crate_owner_invitation": { "crate_id": crate_id, "accepted": true, }, - }))) + })) } diff --git a/src/controllers/helpers.rs b/src/controllers/helpers.rs index 6624c316ee9..0263695e2b0 100644 --- a/src/controllers/helpers.rs +++ b/src/controllers/helpers.rs @@ -1,6 +1,6 @@ use crate::util::errors::AppResult; use axum::response::{IntoResponse, Response}; -use axum::Json; +use axum_extra::json; pub(crate) mod pagination; @@ -8,5 +8,5 @@ pub(crate) use self::pagination::Paginate; pub fn ok_true() -> AppResult { let json = json!({ "ok": true }); - Ok(Json(json).into_response()) + Ok(json.into_response()) } diff --git a/src/controllers/keyword.rs b/src/controllers/keyword.rs index 858f07a40d7..37b3b6b47d1 100644 --- a/src/controllers/keyword.rs +++ b/src/controllers/keyword.rs @@ -6,11 +6,11 @@ use crate::tasks::spawn_blocking; use crate::util::errors::AppResult; use crate::views::EncodableKeyword; use axum::extract::{Path, Query}; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel::prelude::*; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; -use serde_json::Value; #[derive(Deserialize)] pub struct IndexQuery { @@ -18,7 +18,7 @@ pub struct IndexQuery { } /// Handles the `GET /keywords` route. -pub async fn index(state: AppState, qp: Query, req: Parts) -> AppResult> { +pub async fn index(state: AppState, qp: Query, req: Parts) -> AppResult { use crate::schema::keywords; let mut query = keywords::table.into_boxed(); @@ -41,18 +41,18 @@ pub async fn index(state: AppState, qp: Query, req: Parts) -> AppRes .map(Keyword::into) .collect::>(); - Ok(Json(json!({ + Ok(json!({ "keywords": kws, "meta": { "total": total }, - }))) + })) }) .await } /// Handles the `GET /keywords/:keyword_id` route. -pub async fn show(Path(name): Path, state: AppState) -> AppResult> { +pub async fn show(Path(name): Path, state: AppState) -> AppResult { let mut conn = state.db_read().await?; let kw = Keyword::find_by_keyword(&mut conn, &name).await?; - Ok(Json(json!({ "keyword": EncodableKeyword::from(kw) }))) + Ok(json!({ "keyword": EncodableKeyword::from(kw) })) } diff --git a/src/controllers/krate/downloads.rs b/src/controllers/krate/downloads.rs index 7b48fb6c597..171b014a610 100644 --- a/src/controllers/krate/downloads.rs +++ b/src/controllers/krate/downloads.rs @@ -10,14 +10,14 @@ use crate::sql::to_char; use crate::util::errors::{crate_not_found, AppResult}; use crate::views::EncodableVersionDownload; use axum::extract::Path; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel::prelude::*; use diesel_async::RunQueryDsl; -use serde_json::Value; use std::cmp; /// Handles the `GET /crates/:crate_id/downloads` route. -pub async fn downloads(state: AppState, Path(crate_name): Path) -> AppResult> { +pub async fn downloads(state: AppState, Path(crate_name): Path) -> AppResult { let mut conn = state.db_read().await?; use diesel::dsl::*; @@ -68,10 +68,10 @@ pub async fn downloads(state: AppState, Path(crate_name): Path) -> AppRe downloads: i64, } - Ok(Json(json!({ + Ok(json!({ "version_downloads": downloads, "meta": { "extra_downloads": extra, }, - }))) + })) } diff --git a/src/controllers/krate/follow.rs b/src/controllers/krate/follow.rs index b26f2df6c67..d000a10c532 100644 --- a/src/controllers/krate/follow.rs +++ b/src/controllers/krate/follow.rs @@ -8,11 +8,11 @@ use crate::schema::*; use crate::util::errors::{crate_not_found, AppResult}; use axum::extract::Path; use axum::response::Response; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel::prelude::*; use diesel_async::{AsyncPgConnection, RunQueryDsl}; use http::request::Parts; -use serde_json::Value; async fn follow_target( crate_name: &str, @@ -66,7 +66,7 @@ pub async fn following( app: AppState, Path(crate_name): Path, req: Parts, -) -> AppResult> { +) -> AppResult { use diesel::dsl::exists; let mut conn = app.db_read_prefer_primary().await?; @@ -80,5 +80,5 @@ pub async fn following( .get_result::(&mut conn) .await?; - Ok(Json(json!({ "following": following }))) + Ok(json!({ "following": following })) } diff --git a/src/controllers/krate/metadata.rs b/src/controllers/krate/metadata.rs index ee0344d5e3f..87156783b75 100644 --- a/src/controllers/krate/metadata.rs +++ b/src/controllers/krate/metadata.rs @@ -20,20 +20,20 @@ use crate::views::{ }; use axum::extract::Path; use axum::response::{IntoResponse, Response}; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; -use serde_json::Value; use std::cmp::Reverse; use std::str::FromStr; /// Handles the `GET /crates/new` special case. -pub async fn show_new(app: AppState, req: Parts) -> AppResult> { +pub async fn show_new(app: AppState, req: Parts) -> AppResult { show(app, Path("new".to_string()), req).await } /// Handles the `GET /crates/:crate_id` route. -pub async fn show(app: AppState, Path(name): Path, req: Parts) -> AppResult> { +pub async fn show(app: AppState, Path(name): Path, req: Parts) -> AppResult { let conn = app.db_read().await?; spawn_blocking(move || { use diesel::RunQueryDsl; @@ -157,12 +157,12 @@ pub async fn show(app: AppState, Path(name): Path, req: Parts) -> AppRes .map(Category::into) .collect::>() }); - Ok(Json(json!({ + Ok(json!({ "crate": encodable_crate, "versions": encodable_versions, "keywords": encodable_keywords, "categories": encodable_cats, - }))) + })) }) .await } @@ -237,7 +237,7 @@ pub async fn readme( ) -> Response { let redirect_url = app.storage.readme_location(&crate_name, &version); if req.wants_json() { - Json(json!({ "url": redirect_url })).into_response() + json!({ "url": redirect_url }).into_response() } else { redirect(redirect_url) } @@ -248,7 +248,7 @@ pub async fn reverse_dependencies( app: AppState, Path(name): Path, req: Parts, -) -> AppResult> { +) -> AppResult { use diesel_async::RunQueryDsl; let mut conn = app.db_read().await?; @@ -295,9 +295,9 @@ pub async fn reverse_dependencies( }) .collect::>(); - Ok(Json(json!({ + Ok(json!({ "dependencies": rev_deps, "versions": versions, "meta": { "total": total }, - }))) + })) } diff --git a/src/controllers/krate/owners.rs b/src/controllers/krate/owners.rs index 132de3086e3..22201a4c86b 100644 --- a/src/controllers/krate/owners.rs +++ b/src/controllers/krate/owners.rs @@ -10,15 +10,16 @@ use crate::{app::AppState, models::krate::OwnerAddError}; use crate::{auth::AuthCheck, email::Email}; use axum::extract::Path; use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; use http::StatusCode; use secrecy::{ExposeSecret, SecretString}; -use serde_json::Value; use tokio::runtime::Handle; /// Handles the `GET /crates/:crate_id/owners` route. -pub async fn owners(state: AppState, Path(crate_name): Path) -> AppResult> { +pub async fn owners(state: AppState, Path(crate_name): Path) -> AppResult { use diesel_async::RunQueryDsl; let mut conn = state.db_read().await?; @@ -36,11 +37,11 @@ pub async fn owners(state: AppState, Path(crate_name): Path) -> AppResul .map(Owner::into) .collect::>(); - Ok(Json(json!({ "users": owners }))) + Ok(json!({ "users": owners })) } /// Handles the `GET /crates/:crate_id/owner_team` route. -pub async fn owner_team(state: AppState, Path(crate_name): Path) -> AppResult> { +pub async fn owner_team(state: AppState, Path(crate_name): Path) -> AppResult { use diesel_async::RunQueryDsl; let mut conn = state.db_read().await?; @@ -56,11 +57,11 @@ pub async fn owner_team(state: AppState, Path(crate_name): Path) -> AppR .map(Owner::into) .collect::>(); - Ok(Json(json!({ "teams": owners }))) + Ok(json!({ "teams": owners })) } /// Handles the `GET /crates/:crate_id/owner_user` route. -pub async fn owner_user(state: AppState, Path(crate_name): Path) -> AppResult> { +pub async fn owner_user(state: AppState, Path(crate_name): Path) -> AppResult { let conn = state.db_read().await?; spawn_blocking(move || { use diesel::RunQueryDsl; @@ -77,7 +78,7 @@ pub async fn owner_user(state: AppState, Path(crate_name): Path) -> AppR .map(Owner::into) .collect::>(); - Ok(Json(json!({ "users": owners }))) + Ok(json!({ "users": owners })) }) .await } @@ -88,7 +89,7 @@ pub async fn add_owners( Path(crate_name): Path, parts: Parts, Json(body): Json, -) -> AppResult> { +) -> AppResult { modify_owners(app, crate_name, parts, body, true).await } @@ -98,7 +99,7 @@ pub async fn remove_owners( Path(crate_name): Path, parts: Parts, Json(body): Json, -) -> AppResult> { +) -> AppResult { modify_owners(app, crate_name, parts, body, false).await } @@ -114,7 +115,7 @@ async fn modify_owners( parts: Parts, body: ChangeOwnersRequest, add: bool, -) -> AppResult> { +) -> AppResult { let logins = body.owners; // Bound the number of invites processed per request to limit the cost of @@ -243,7 +244,7 @@ async fn modify_owners( } } - Ok(Json(json!({ "ok": true, "msg": comma_sep_msg }))) + Ok(json!({ "msg": comma_sep_msg, "ok": true })) }) .await } diff --git a/src/controllers/krate/search.rs b/src/controllers/krate/search.rs index 57db6213d34..d5267cec4fe 100644 --- a/src/controllers/krate/search.rs +++ b/src/controllers/krate/search.rs @@ -2,14 +2,14 @@ use crate::auth::AuthCheck; use crate::util::diesel::prelude::*; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel::dsl::{exists, sql, InnerJoinQuerySource, LeftJoinQuerySource}; use diesel::sql_types::{Array, Bool, Text}; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use diesel_async::AsyncPgConnection; use diesel_full_text_search::*; use http::request::Parts; -use serde_json::Value; use std::cell::OnceCell; use tokio::runtime::Handle; @@ -47,7 +47,7 @@ use crate::util::RequestUtils; /// caused the break. In the future, we should look at splitting this /// function out to cover the different use cases, and create unit tests /// for them. -pub async fn search(app: AppState, req: Parts) -> AppResult> { +pub async fn search(app: AppState, req: Parts) -> AppResult { let conn = app.db_read().await?; spawn_blocking(move || { use diesel::RunQueryDsl; @@ -258,14 +258,14 @@ pub async fn search(app: AppState, req: Parts) -> AppResult> { ) .collect::>(); - Ok(Json(json!({ + Ok(json!({ "crates": crates, "meta": { "total": total, "next_page": next_page, "prev_page": prev_page, }, - }))) + })) }) .await } diff --git a/src/controllers/krate/versions.rs b/src/controllers/krate/versions.rs index 8926910fdf9..62cad9f008b 100644 --- a/src/controllers/krate/versions.rs +++ b/src/controllers/krate/versions.rs @@ -2,13 +2,13 @@ use crate::util::diesel::prelude::*; use axum::extract::Path; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel::connection::DefaultLoadingMode; use diesel::dsl::not; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; use indexmap::{IndexMap, IndexSet}; -use serde_json::Value; use std::cmp::Reverse; use std::str::FromStr; @@ -27,7 +27,7 @@ pub async fn versions( state: AppState, Path(crate_name): Path, req: Parts, -) -> AppResult> { +) -> AppResult { let conn = state.db_read().await?; spawn_blocking(move || { use diesel::RunQueryDsl; @@ -79,10 +79,10 @@ pub async fn versions( .map(|((v, pb), aas)| EncodableVersion::from(v, &crate_name, pb, aas)) .collect::>(); - Ok(Json(match pagination { + Ok(match pagination { Some(_) => json!({ "versions": versions, "meta": versions_and_publishers.meta }), None => json!({ "versions": versions }), - })) + }) }) .await } @@ -463,6 +463,7 @@ impl FromStr for ShowIncludeMode { mod tests { use super::{ReleaseTrackDetails, ReleaseTrackName, ReleaseTracks}; use indexmap::IndexMap; + use serde_json::json; #[track_caller] fn version(str: &str) -> semver::Version { diff --git a/src/controllers/site_metadata.rs b/src/controllers/site_metadata.rs index b55bd58d714..910055458e5 100644 --- a/src/controllers/site_metadata.rs +++ b/src/controllers/site_metadata.rs @@ -1,6 +1,6 @@ use crate::app::AppState; use axum::response::IntoResponse; -use axum::Json; +use axum_extra::json; /// Returns the JSON representation of the current deployed commit sha. /// @@ -12,9 +12,9 @@ pub async fn show_deployed_sha(state: AppState) -> impl IntoResponse { let deployed_sha = dotenvy::var("HEROKU_SLUG_COMMIT").unwrap_or_else(|_| String::from("unknown")); - Json(json!({ + json!({ "deployed_sha": &deployed_sha[..], "commit": &deployed_sha[..], "read_only": read_only, - })) + }) } diff --git a/src/controllers/summary.rs b/src/controllers/summary.rs index 073efd87ebc..4276efaed9c 100644 --- a/src/controllers/summary.rs +++ b/src/controllers/summary.rs @@ -5,17 +5,17 @@ use crate::schema::{ }; use crate::util::errors::AppResult; use crate::views::{EncodableCategory, EncodableCrate, EncodableKeyword}; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel::{ BelongingToDsl, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, QueryDsl, SelectableHelper, }; use diesel_async::AsyncPgConnection; use diesel_async::RunQueryDsl; -use serde_json::Value; /// Handles the `GET /summary` route. -pub async fn summary(state: AppState) -> AppResult> { +pub async fn summary(state: AppState) -> AppResult { let mut conn = state.db_read().await?; let popular_categories = Category::toplevel(&mut conn, "crates", 10, 0) @@ -129,7 +129,7 @@ pub async fn summary(state: AppState) -> AppResult> { .map(Keyword::into) .collect::>(); - Ok(Json(json!({ + Ok(json!({ "num_downloads": num_downloads, "num_crates": num_crates, "new_crates": encode_crates(&mut conn, new_crates).await?, @@ -138,7 +138,7 @@ pub async fn summary(state: AppState) -> AppResult> { "just_updated": encode_crates(&mut conn, just_updated).await?, "popular_keywords": popular_keywords, "popular_categories": popular_categories, - }))) + })) } type Record = (Crate, i64, Option, Option, Option); diff --git a/src/controllers/team.rs b/src/controllers/team.rs index cefbb372a1b..b1263ae463d 100644 --- a/src/controllers/team.rs +++ b/src/controllers/team.rs @@ -3,16 +3,16 @@ use crate::models::Team; use crate::util::errors::AppResult; use crate::views::EncodableTeam; use axum::extract::Path; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel::prelude::*; use diesel_async::RunQueryDsl; -use serde_json::Value; /// Handles the `GET /teams/:team_id` route. -pub async fn show_team(state: AppState, Path(name): Path) -> AppResult> { +pub async fn show_team(state: AppState, Path(name): Path) -> AppResult { use crate::schema::teams::dsl::{login, teams}; let mut conn = state.db_read().await?; let team: Team = teams.filter(login.eq(&name)).first(&mut conn).await?; - Ok(Json(json!({ "team": EncodableTeam::from(team) }))) + Ok(json!({ "team": EncodableTeam::from(team) })) } diff --git a/src/controllers/token.rs b/src/controllers/token.rs index a25d303e9b6..c36f92c0667 100644 --- a/src/controllers/token.rs +++ b/src/controllers/token.rs @@ -12,13 +12,14 @@ use crate::util::errors::{bad_request, AppResult}; use axum::extract::{Path, Query}; use axum::response::{IntoResponse, Response}; use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use chrono::NaiveDateTime; use diesel::data_types::PgInterval; use diesel::dsl::{now, IntervalDsl}; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; use http::StatusCode; -use serde_json::Value; #[derive(Deserialize)] pub struct GetParams { @@ -40,7 +41,7 @@ pub async fn list( app: AppState, Query(params): Query, req: Parts, -) -> AppResult> { +) -> AppResult { use diesel_async::RunQueryDsl; let mut conn = app.db_read_prefer_primary().await?; @@ -59,7 +60,7 @@ pub async fn list( .load(&mut conn) .await?; - Ok(Json(json!({ "api_tokens": tokens }))) + Ok(json!({ "api_tokens": tokens })) } /// The incoming serialization format for the `ApiToken` model. @@ -83,7 +84,7 @@ pub async fn new( app: AppState, parts: Parts, Json(new): Json, -) -> AppResult> { +) -> AppResult { if new.api_token.name.is_empty() { return Err(bad_request("name must have a value")); } @@ -164,13 +165,13 @@ pub async fn new( let api_token = EncodableApiTokenWithToken::from(api_token); - Ok(Json(json!({ "api_token": api_token }))) + Ok(json!({ "api_token": api_token })) }) .await } /// Handles the `GET /me/tokens/:id` route. -pub async fn show(app: AppState, Path(id): Path, req: Parts) -> AppResult> { +pub async fn show(app: AppState, Path(id): Path, req: Parts) -> AppResult { use diesel_async::RunQueryDsl; let mut conn = app.db_write().await?; @@ -182,11 +183,11 @@ pub async fn show(app: AppState, Path(id): Path, req: Parts) -> AppResult, req: Parts) -> AppResult> { +pub async fn revoke(app: AppState, Path(id): Path, req: Parts) -> AppResult { use diesel_async::RunQueryDsl; let mut conn = app.db_write().await?; @@ -197,7 +198,7 @@ pub async fn revoke(app: AppState, Path(id): Path, req: Parts) -> AppResult .execute(&mut conn) .await?; - Ok(Json(json!({}))) + Ok(json!({})) } /// Handles the `DELETE /tokens/current` route. diff --git a/src/controllers/user/me.rs b/src/controllers/user/me.rs index cd6bade858e..68d8192f011 100644 --- a/src/controllers/user/me.rs +++ b/src/controllers/user/me.rs @@ -3,9 +3,10 @@ use crate::util::diesel::prelude::*; use axum::extract::Path; use axum::response::Response; use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; -use serde_json::Value; use std::collections::HashMap; use crate::app::AppState; @@ -65,7 +66,7 @@ pub async fn me(app: AppState, req: Parts) -> AppResult> { } /// Handles the `GET /me/updates` route. -pub async fn updates(app: AppState, req: Parts) -> AppResult> { +pub async fn updates(app: AppState, req: Parts) -> AppResult { let mut conn = app.db_read_prefer_primary().await?; let auth = AuthCheck::only_cookie().check(&req, &mut conn).await?; spawn_blocking(move || { @@ -97,10 +98,10 @@ pub async fn updates(app: AppState, req: Parts) -> AppResult> { }) .collect::>(); - Ok(Json(json!({ + Ok(json!({ "versions": versions, "meta": { "more": more }, - }))) + })) }) .await } diff --git a/src/controllers/user/other.rs b/src/controllers/user/other.rs index a4b6550cc79..8ba6459abb3 100644 --- a/src/controllers/user/other.rs +++ b/src/controllers/user/other.rs @@ -1,9 +1,9 @@ use axum::extract::Path; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use bigdecimal::{BigDecimal, ToPrimitive}; use diesel::prelude::*; use diesel_async::RunQueryDsl; -use serde_json::Value; use crate::app::AppState; use crate::models::{CrateOwner, OwnerKind, User}; @@ -13,7 +13,7 @@ use crate::util::errors::AppResult; use crate::views::EncodablePublicUser; /// Handles the `GET /users/:user_id` route. -pub async fn show(state: AppState, Path(user_name): Path) -> AppResult> { +pub async fn show(state: AppState, Path(user_name): Path) -> AppResult { let mut conn = state.db_read_prefer_primary().await?; use crate::schema::users::dsl::{gh_login, id, users}; @@ -25,11 +25,11 @@ pub async fn show(state: AppState, Path(user_name): Path) -> AppResult) -> AppResult> { +pub async fn stats(state: AppState, Path(user_id): Path) -> AppResult { let mut conn = state.db_read_prefer_primary().await?; use diesel::dsl::sum; @@ -45,5 +45,5 @@ pub async fn stats(state: AppState, Path(user_id): Path) -> AppResult Json { +pub async fn begin(app: AppState, session: SessionExtension) -> ErasedJson { let (url, state) = app .github_oauth .authorize_url(oauth2::CsrfToken::new_random) @@ -44,7 +45,7 @@ pub async fn begin(app: AppState, session: SessionExtension) -> Json { let state = state.secret().to_string(); session.insert("github_oauth_state".to_string(), state.clone()); - Json(json!({ "url": url.to_string(), "state": state })) + json!({ "url": url.to_string(), "state": state }) } #[derive(Clone, Debug, Deserialize, FromRequestParts)] diff --git a/src/controllers/version/downloads.rs b/src/controllers/version/downloads.rs index 65ee2ff8b19..bd4f2791e16 100644 --- a/src/controllers/version/downloads.rs +++ b/src/controllers/version/downloads.rs @@ -13,11 +13,11 @@ use crate::util::{redirect, RequestUtils}; use crate::views::EncodableVersionDownload; use axum::extract::Path; use axum::response::{IntoResponse, Response}; -use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use chrono::{Duration, NaiveDate, Utc}; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; use http::request::Parts; -use serde_json::Value; /// Handles the `GET /crates/:crate_id/:version/download` route. /// This returns a URL to the location where the crate is stored. @@ -29,7 +29,7 @@ pub async fn download( let wants_json = req.wants_json(); let redirect_url = app.storage.crate_location(&crate_name, &version); if wants_json { - Ok(Json(json!({ "url": redirect_url })).into_response()) + Ok(json!({ "url": redirect_url }).into_response()) } else { Ok(redirect(redirect_url)) } @@ -40,7 +40,7 @@ pub async fn downloads( app: AppState, Path((crate_name, version)): Path<(String, String)>, req: Parts, -) -> AppResult> { +) -> AppResult { if semver::Version::parse(&version).is_err() { return Err(version_not_found(&crate_name, &version)); } @@ -67,7 +67,7 @@ pub async fn downloads( .map(VersionDownload::into) .collect::>(); - Ok(Json(json!({ "version_downloads": downloads }))) + Ok(json!({ "version_downloads": downloads })) }) .await } diff --git a/src/controllers/version/metadata.rs b/src/controllers/version/metadata.rs index 2ae82dc3030..a9d50836c05 100644 --- a/src/controllers/version/metadata.rs +++ b/src/controllers/version/metadata.rs @@ -6,6 +6,8 @@ use axum::extract::Path; use axum::Json; +use axum_extra::json; +use axum_extra::response::ErasedJson; use crates_io_database::schema::{crates, dependencies}; use crates_io_worker::BackgroundJob; use diesel_async::async_connection_wrapper::AsyncConnectionWrapper; @@ -13,7 +15,6 @@ use diesel_async::AsyncPgConnection; use http::request::Parts; use http::StatusCode; use serde::Deserialize; -use serde_json::Value; use tokio::runtime::Handle; use crate::app::AppState; @@ -54,7 +55,7 @@ pub struct VersionUpdateRequest { pub async fn dependencies( state: AppState, Path((crate_name, version)): Path<(String, String)>, -) -> AppResult> { +) -> AppResult { use diesel_async::RunQueryDsl; if semver::Version::parse(&version).is_err() { @@ -74,18 +75,18 @@ pub async fn dependencies( .map(|(dep, crate_name)| EncodableDependency::from_dep(dep, &crate_name)) .collect::>(); - Ok(Json(json!({ "dependencies": deps }))) + Ok(json!({ "dependencies": deps })) } /// Handles the `GET /crates/:crate_id/:version/authors` route. -pub async fn authors() -> Json { +pub async fn authors() -> ErasedJson { // Currently we return the empty list. // Because the API is not used anymore after RFC https://github.com/rust-lang/rfcs/pull/3052. - Json(json!({ + json!({ "users": [], "meta": { "names": [] }, - })) + }) } /// Handles the `GET /crates/:crate/:version` route. @@ -95,7 +96,7 @@ pub async fn authors() -> Json { pub async fn show( state: AppState, Path((crate_name, version)): Path<(String, String)>, -) -> AppResult> { +) -> AppResult { if semver::Version::parse(&version).is_err() { return Err(version_not_found(&crate_name, &version)); } @@ -109,7 +110,7 @@ pub async fn show( let actions = VersionOwnerAction::by_version(conn, &version)?; let version = EncodableVersion::from(version, &krate.name, published_by, actions); - Ok(Json(json!({ "version": version }))) + Ok(json!({ "version": version })) }) .await } @@ -122,7 +123,7 @@ pub async fn update( Path((crate_name, version)): Path<(String, String)>, req: Parts, Json(update_request): Json, -) -> AppResult> { +) -> AppResult { if semver::Version::parse(&version).is_err() { return Err(version_not_found(&crate_name, &version)); } @@ -147,7 +148,7 @@ pub async fn update( let published_by = version.published_by(conn)?; let actions = VersionOwnerAction::by_version(conn, &version)?; let updated_version = EncodableVersion::from(version, &krate.name, published_by, actions); - Ok(Json(json!({ "version": updated_version }))) + Ok(json!({ "version": updated_version })) }) .await } diff --git a/src/lib.rs b/src/lib.rs index 9e37fdaa058..de3afb0cc65 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,8 +14,6 @@ extern crate diesel; #[macro_use] extern crate serde; #[macro_use] -extern crate serde_json; -#[macro_use] extern crate tracing; pub use crate::{app::App, email::Emails}; diff --git a/src/tests/krate/following.rs b/src/tests/krate/following.rs index aa16827a45f..9f0c086cfad 100644 --- a/src/tests/krate/following.rs +++ b/src/tests/krate/following.rs @@ -3,6 +3,7 @@ use crate::tests::util::{RequestHelper, TestApp}; use googletest::prelude::*; use http::StatusCode; use insta::assert_snapshot; +use serde_json::json; async fn assert_is_following(crate_name: &str, expected: bool, user: &impl RequestHelper) { let response = user diff --git a/src/tests/krate/publish/build_metadata.rs b/src/tests/krate/publish/build_metadata.rs index bf82e8fcbdb..c85d852195e 100644 --- a/src/tests/krate/publish/build_metadata.rs +++ b/src/tests/krate/publish/build_metadata.rs @@ -2,6 +2,7 @@ use crate::tests::builders::PublishBuilder; use crate::tests::util::{RequestHelper, TestApp}; use http::StatusCode; use insta::assert_json_snapshot; +use serde_json::json; async fn version_with_build_metadata(v1: &str, v2: &str, expected_error: &str) { let (_app, _anon, _cookie, token) = TestApp::full().with_token(); diff --git a/src/tests/owners.rs b/src/tests/owners.rs index 97279ff12bd..f7af5023657 100644 --- a/src/tests/owners.rs +++ b/src/tests/owners.rs @@ -16,6 +16,7 @@ use chrono::{Duration, Utc}; use diesel::prelude::*; use http::StatusCode; use insta::assert_snapshot; +use serde_json::json; #[derive(Deserialize)] struct TeamResponse { diff --git a/src/tests/routes/crates/versions/list.rs b/src/tests/routes/crates/versions/list.rs index e2434509e81..5c55ddbe6ac 100644 --- a/src/tests/routes/crates/versions/list.rs +++ b/src/tests/routes/crates/versions/list.rs @@ -6,6 +6,7 @@ use diesel::{prelude::*, update}; use googletest::prelude::*; use http::StatusCode; use insta::{assert_json_snapshot, assert_snapshot}; +use serde_json::json; #[tokio::test(flavor = "multi_thread")] async fn versions() { diff --git a/src/tests/routes/me/email_notifications.rs b/src/tests/routes/me/email_notifications.rs index d743dab19fe..d242834b58d 100644 --- a/src/tests/routes/me/email_notifications.rs +++ b/src/tests/routes/me/email_notifications.rs @@ -4,6 +4,7 @@ use crate::tests::new_user; use crate::tests::util::{RequestHelper, TestApp}; use diesel::prelude::*; use http::StatusCode; +use serde_json::json; #[derive(Serialize)] struct EmailNotificationsUpdate { diff --git a/src/tests/routes/me/tokens/create.rs b/src/tests/routes/me/tokens/create.rs index d302d09bd6a..1462f06119f 100644 --- a/src/tests/routes/me/tokens/create.rs +++ b/src/tests/routes/me/tokens/create.rs @@ -7,7 +7,7 @@ use diesel_async::RunQueryDsl; use googletest::prelude::*; use http::StatusCode; use insta::assert_snapshot; -use serde_json::Value; +use serde_json::{json, Value}; static NEW_BAR: &[u8] = br#"{ "api_token": { "name": "bar" } }"#; diff --git a/src/tests/routes/me/tokens/list.rs b/src/tests/routes/me/tokens/list.rs index 5c21177f4c9..ef293ff4b7c 100644 --- a/src/tests/routes/me/tokens/list.rs +++ b/src/tests/routes/me/tokens/list.rs @@ -5,6 +5,7 @@ use crate::tests::util::{RequestHelper, TestApp}; use chrono::{Duration, Utc}; use http::StatusCode; use insta::assert_snapshot; +use serde_json::json; #[tokio::test(flavor = "multi_thread")] async fn list_logged_out() { diff --git a/src/tests/routes/private/crate_owner_invitations.rs b/src/tests/routes/private/crate_owner_invitations.rs index 68fa3737764..dc31e7f9fcc 100644 --- a/src/tests/routes/private/crate_owner_invitations.rs +++ b/src/tests/routes/private/crate_owner_invitations.rs @@ -4,6 +4,7 @@ use crate::tests::builders::CrateBuilder; use crate::tests::util::{MockCookieUser, RequestHelper, TestApp}; use crate::views::{EncodableCrateOwnerInvitation, EncodablePublicUser}; use http::StatusCode; +use serde_json::json; #[derive(Deserialize, Debug, PartialEq, Eq)] struct CrateOwnerInvitationsResponse { diff --git a/src/tests/routes/users/update.rs b/src/tests/routes/users/update.rs index 49d9c9d6122..a163e92c6f4 100644 --- a/src/tests/routes/users/update.rs +++ b/src/tests/routes/users/update.rs @@ -1,6 +1,7 @@ use crate::tests::util::{RequestHelper, Response, TestApp}; use http::StatusCode; use insta::assert_snapshot; +use serde_json::json; mod publish_notifications; diff --git a/src/tests/routes/users/update/publish_notifications.rs b/src/tests/routes/users/update/publish_notifications.rs index b512ba2e715..5d050bcdf35 100644 --- a/src/tests/routes/users/update/publish_notifications.rs +++ b/src/tests/routes/users/update/publish_notifications.rs @@ -2,6 +2,7 @@ use crate::tests::builders::PublishBuilder; use crate::tests::util::{RequestHelper, TestApp}; use http::StatusCode; use insta::assert_snapshot; +use serde_json::json; #[tokio::test(flavor = "multi_thread")] async fn test_unsubscribe_and_resubscribe() { diff --git a/src/tests/user.rs b/src/tests/user.rs index 48d45d15139..6bb3b459e8d 100644 --- a/src/tests/user.rs +++ b/src/tests/user.rs @@ -8,6 +8,7 @@ use crate::util::token::HashedToken; use diesel::prelude::*; use http::StatusCode; use secrecy::ExposeSecret; +use serde_json::json; impl crate::tests::util::MockCookieUser { async fn confirm_email(&self, email_token: &str) { diff --git a/src/tests/util.rs b/src/tests/util.rs index 47c464028de..b98d939df54 100644 --- a/src/tests/util.rs +++ b/src/tests/util.rs @@ -36,6 +36,7 @@ use chrono::NaiveDateTime; use cookie::Cookie; use http::header; use secrecy::ExposeSecret; +use serde_json::json; use std::collections::HashMap; use std::net::SocketAddr; use tower::ServiceExt; diff --git a/src/util/errors/json.rs b/src/util/errors/json.rs index 25910e3f4e7..78e83c92c20 100644 --- a/src/util/errors/json.rs +++ b/src/util/errors/json.rs @@ -1,5 +1,6 @@ use axum::response::{IntoResponse, Response}; -use axum::{Extension, Json}; +use axum::Extension; +use axum_extra::json; use std::borrow::Cow; use std::fmt; @@ -13,7 +14,7 @@ use http::{header, StatusCode}; /// Generates a response with the provided status and description as JSON fn json_error(detail: &str, status: StatusCode) -> Response { let json = json!({ "errors": [{ "detail": detail }] }); - (status, Json(json)).into_response() + (status, json).into_response() } // The following structs are empty and do not provide a custom message to the user diff --git a/src/worker/jobs/downloads/queue/job.rs b/src/worker/jobs/downloads/queue/job.rs index fbef8d8c688..4ebb8fe228e 100644 --- a/src/worker/jobs/downloads/queue/job.rs +++ b/src/worker/jobs/downloads/queue/job.rs @@ -232,6 +232,7 @@ mod tests { use diesel_async::RunQueryDsl; use insta::assert_snapshot; use parking_lot::Mutex; + use serde_json::json; #[tokio::test] async fn test_process_cdn_log_queue() {