@@ -29,12 +29,14 @@ use crate::queue::session::AuthQueue;
2929use crate :: routes:: ApiError ;
3030use crate :: search:: indexing:: remove_documents;
3131use crate :: search:: { SearchConfig , SearchError , search_for_project} ;
32+ use crate :: util:: error:: Context ;
3233use crate :: util:: img;
3334use crate :: util:: img:: { delete_old_images, upload_image_optimized} ;
3435use crate :: util:: routes:: read_limited_from_payload;
3536use crate :: util:: validate:: validation_errors_to_string;
3637use actix_web:: { HttpRequest , HttpResponse , web} ;
3738use chrono:: Utc ;
39+ use eyre:: eyre;
3840use futures:: TryStreamExt ;
3941use itertools:: Itertools ;
4042use serde:: { Deserialize , Serialize } ;
@@ -980,7 +982,8 @@ pub async fn project_edit(
980982 . collect :: < Vec < _ > > ( ) ,
981983 & search_config,
982984 )
983- . await ?;
985+ . await
986+ . wrap_internal_err ( "failed to remove documents" ) ?;
984987 }
985988
986989 Ok ( HttpResponse :: NoContent ( ) . body ( "" ) )
@@ -2157,25 +2160,29 @@ pub async fn project_delete(
21572160 redis : web:: Data < RedisPool > ,
21582161 search_config : web:: Data < SearchConfig > ,
21592162 session_queue : web:: Data < AuthQueue > ,
2160- ) -> Result < HttpResponse , ApiError > {
2161- let user = get_user_from_headers (
2163+ ) -> Result < ( ) , ApiError > {
2164+ let ( _ , user) = get_user_from_headers (
21622165 & req,
21632166 & * * pool,
21642167 & redis,
21652168 & session_queue,
21662169 Scopes :: PROJECT_DELETE ,
21672170 )
2168- . await ?
2169- . 1 ;
2171+ . await ?;
21702172 let string = info. into_inner ( ) . 0 ;
21712173
2174+ // In two cases, we return `The specified project does not exist!`:
2175+ // - the project really doesn't exist
2176+ // - the project is hidden from the user
2177+ //
2178+ // We use an `ApiError::Auth` for this case instead of a `ApiError::Request`,
2179+ // because our permissions tests assert that failing under the 2nd use case
2180+ // gives a 401 or 404, but `Request` gives only a 400.
2181+
21722182 let project = db_models:: DBProject :: get ( & string, & * * pool, & redis)
2173- . await ?
2174- . ok_or_else ( || {
2175- ApiError :: InvalidInput (
2176- "The specified project does not exist!" . to_string ( ) ,
2177- )
2178- } ) ?;
2183+ . await
2184+ . wrap_internal_err ( "failed to get project" ) ?
2185+ . wrap_auth_err ( "The specified project does not exist!" ) ?;
21792186
21802187 if !user. role . is_admin ( ) {
21812188 let ( team_member, organization_team_member) =
@@ -2184,13 +2191,14 @@ pub async fn project_delete(
21842191 user. id . into ( ) ,
21852192 & * * pool,
21862193 )
2187- . await ?;
2194+ . await
2195+ . wrap_internal_err ( "failed to get user team member permissions" ) ?;
21882196
21892197 // Hide the project
21902198 if team_member. is_none ( ) && organization_team_member. is_none ( ) {
2191- return Err ( ApiError :: CustomAuthentication (
2192- "The specified project does not exist!" . to_string ( ) ,
2193- ) ) ;
2199+ return Err ( ApiError :: Auth ( eyre ! (
2200+ "The specified project does not exist!"
2201+ ) ) ) ;
21942202 }
21952203
21962204 let permissions = ProjectPermissions :: get_permissions_by_role (
@@ -2207,15 +2215,23 @@ pub async fn project_delete(
22072215 }
22082216 }
22092217
2210- let mut transaction = pool. begin ( ) . await ?;
2218+ let mut transaction = pool
2219+ . begin ( )
2220+ . await
2221+ . wrap_internal_err ( "failed to start transaction" ) ?;
22112222 let context = ImageContext :: Project {
22122223 project_id : Some ( project. inner . id . into ( ) ) ,
22132224 } ;
22142225 let uploaded_images =
22152226 db_models:: DBImage :: get_many_contexted ( context, & mut transaction)
2216- . await ?;
2227+ . await
2228+ . wrap_internal_err ( "failed to get project images" ) ?;
22172229 for image in uploaded_images {
2218- image_item:: DBImage :: remove ( image. id , & mut transaction, & redis) . await ?;
2230+ image_item:: DBImage :: remove ( image. id , & mut transaction, & redis)
2231+ . await
2232+ . wrap_internal_err_with ( || {
2233+ eyre ! ( "failed to remove project image `{:?}`" , image. id)
2234+ } ) ?;
22192235 }
22202236
22212237 sqlx:: query!(
@@ -2226,16 +2242,21 @@ pub async fn project_delete(
22262242 project. inner. id as db_ids:: DBProjectId ,
22272243 )
22282244 . execute ( & mut transaction)
2229- . await ?;
2245+ . await
2246+ . wrap_internal_err ( "failed to delete project from collections_mods" ) ?;
22302247
22312248 let result = db_models:: DBProject :: remove (
22322249 project. inner . id ,
22332250 & mut transaction,
22342251 & redis,
22352252 )
2236- . await ?;
2253+ . await
2254+ . wrap_internal_err ( "failed to remove project" ) ?;
22372255
2238- transaction. commit ( ) . await ?;
2256+ transaction
2257+ . commit ( )
2258+ . await
2259+ . wrap_internal_err ( "failed to commit transaction" ) ?;
22392260
22402261 remove_documents (
22412262 & project
@@ -2245,10 +2266,11 @@ pub async fn project_delete(
22452266 . collect :: < Vec < _ > > ( ) ,
22462267 & search_config,
22472268 )
2248- . await ?;
2269+ . await
2270+ . wrap_internal_err ( "failed to remove project version documents" ) ?;
22492271
22502272 if result. is_some ( ) {
2251- Ok ( HttpResponse :: NoContent ( ) . body ( "" ) )
2273+ Ok ( ( ) )
22522274 } else {
22532275 Err ( ApiError :: NotFound )
22542276 }
0 commit comments