77use axum:: extract:: Path ;
88use axum:: Json ;
99use crates_io_worker:: BackgroundJob ;
10- use diesel:: { ExpressionMethods , RunQueryDsl } ;
10+ use diesel:: { ExpressionMethods , QueryDsl , RunQueryDsl } ;
1111use diesel_async:: async_connection_wrapper:: AsyncConnectionWrapper ;
1212use http:: request:: Parts ;
1313use http:: StatusCode ;
@@ -22,6 +22,7 @@ use crate::models::{
2222 insert_version_owner_action, Crate , Rights , Version , VersionAction , VersionOwnerAction ,
2323} ;
2424use crate :: rate_limiter:: LimitedAction ;
25+ use crate :: schema:: versions;
2526use crate :: tasks:: spawn_blocking;
2627use crate :: util:: diesel:: Conn ;
2728use crate :: util:: errors:: { bad_request, custom, version_not_found, AppResult } ;
@@ -142,14 +143,52 @@ fn apply_yank_update(
142143) -> AppResult < ( ) > {
143144 // Try to update the yank state first, to avoid unnecessary checks.
144145 update_version_yank_state ( version, update_data) ?;
146+ perform_version_yank_update ( state, req, conn, version, krate) ?;
145147
146- // Add authentication check
148+ Ok ( ( ) )
149+ }
150+
151+ fn update_version_yank_state ( version : & mut Version , update_data : & VersionUpdate ) -> AppResult < ( ) > {
152+ match ( update_data. yanked , & update_data. yank_message ) {
153+ ( Some ( true ) , Some ( message) ) => {
154+ version. yanked = true ;
155+ version. yank_message = Some ( message. clone ( ) ) ;
156+ }
157+ ( Some ( yanked) , None ) => {
158+ version. yanked = yanked;
159+ version. yank_message = None ;
160+ }
161+ ( Some ( false ) , Some ( _) ) => {
162+ return Err ( bad_request ( "Cannot set yank message when unyanking" ) ) ;
163+ }
164+ ( None , Some ( message) ) => {
165+ if version. yanked {
166+ version. yank_message = Some ( message. clone ( ) ) ;
167+ } else {
168+ return Err ( bad_request (
169+ "Cannot update yank message for a version that is not yanked" ,
170+ ) ) ;
171+ }
172+ }
173+ // If both yanked and yank_message are None, do nothing.
174+ // This function only cares about updating the yanked state and yank message.
175+ ( None , None ) => { }
176+ }
177+ Ok ( ( ) )
178+ }
179+
180+ pub fn perform_version_yank_update (
181+ state : & AppState ,
182+ req : & Parts ,
183+ conn : & mut impl Conn ,
184+ version : & Version ,
185+ krate : & Crate ,
186+ ) -> AppResult < ( ) > {
147187 let auth = AuthCheck :: default ( )
148188 . with_endpoint_scope ( EndpointScope :: Yank )
149189 . for_crate ( & krate. name )
150190 . check ( req, conn) ?;
151191
152- // Add rate limiting check
153192 state
154193 . rate_limiter
155194 . check_rate_limit ( auth. user_id ( ) , LimitedAction :: YankUnyank , conn) ?;
@@ -158,68 +197,52 @@ fn apply_yank_update(
158197 let user = auth. user ( ) ;
159198 let owners = krate. owners ( conn) ?;
160199
161- // Check user rights
162200 if Handle :: current ( ) . block_on ( user. rights ( state, & owners) ) ? < Rights :: Publish {
163201 if user. is_admin {
202+ let action = if version. yanked {
203+ "yanking"
204+ } else {
205+ "unyanking"
206+ } ;
164207 warn ! (
165- "Admin {} is updating {}@{}" ,
208+ "Admin {} is {action} {}@{}" ,
166209 user. gh_login, krate. name, version. num
167210 ) ;
168211 } else {
169212 return Err ( custom (
170213 StatusCode :: FORBIDDEN ,
171- "must already be an owner to update version " ,
214+ "must already be an owner to yank or unyank " ,
172215 ) ) ;
173216 }
174217 }
175218
176- diesel:: update ( & * version)
219+ // Check if the yanked state or yank message has changed
220+ let ( yanked, yank_message) = versions:: table
221+ . find ( version. id )
222+ . select ( ( versions:: yanked, versions:: yank_message) )
223+ . first :: < ( bool , Option < String > ) > ( conn) ?;
224+
225+ if yanked == version. yanked && yank_message == version. yank_message {
226+ // No changes, return early
227+ return Ok ( ( ) ) ;
228+ }
229+
230+ diesel:: update ( version)
177231 . set ( (
178- crate :: schema :: versions:: yanked. eq ( version. yanked ) ,
179- crate :: schema :: versions:: yank_message. eq ( & version. yank_message ) ,
232+ versions:: yanked. eq ( version. yanked ) ,
233+ versions:: yank_message. eq ( & version. yank_message ) ,
180234 ) )
181235 . execute ( conn) ?;
182236
183- // Add version owner action
184237 let action = if version. yanked {
185238 VersionAction :: Yank
186239 } else {
187240 VersionAction :: Unyank
188241 } ;
189242 insert_version_owner_action ( conn, version. id , user. id , api_token_id, action) ?;
190243
191- // Enqueue jobs
192244 jobs:: enqueue_sync_to_index ( & krate. name , conn) ?;
193245 UpdateDefaultVersion :: new ( krate. id ) . enqueue ( conn) ?;
194246
195247 Ok ( ( ) )
196248}
197-
198- fn update_version_yank_state ( version : & mut Version , update_data : & VersionUpdate ) -> AppResult < ( ) > {
199- match ( update_data. yanked , & update_data. yank_message ) {
200- ( Some ( true ) , Some ( message) ) => {
201- version. yanked = true ;
202- version. yank_message = Some ( message. clone ( ) ) ;
203- }
204- ( Some ( yanked) , None ) => {
205- version. yanked = yanked;
206- version. yank_message = None ;
207- }
208- ( Some ( false ) , Some ( _) ) => {
209- return Err ( bad_request ( "Cannot set yank message when unyanking" ) ) ;
210- }
211- ( None , Some ( message) ) => {
212- if version. yanked {
213- version. yank_message = Some ( message. clone ( ) ) ;
214- } else {
215- return Err ( bad_request (
216- "Cannot update yank message for a version that is not yanked" ,
217- ) ) ;
218- }
219- }
220- // If both yanked and yank_message are None, do nothing.
221- // This function only cares about updating the yanked state and yank message.
222- ( None , None ) => { }
223- }
224- Ok ( ( ) )
225- }
0 commit comments