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 ;
@@ -142,14 +142,52 @@ fn apply_yank_update(
142142) -> AppResult < ( ) > {
143143 // Try to update the yank state first, to avoid unnecessary checks.
144144 update_version_yank_state ( version, update_data) ?;
145+ perform_version_yank_update ( state, req, conn, version, krate) ?;
145146
146- // Add authentication check
147+ Ok ( ( ) )
148+ }
149+
150+ fn update_version_yank_state ( version : & mut Version , update_data : & VersionUpdate ) -> AppResult < ( ) > {
151+ match ( update_data. yanked , & update_data. yank_message ) {
152+ ( Some ( true ) , Some ( message) ) => {
153+ version. yanked = true ;
154+ version. yank_message = Some ( message. clone ( ) ) ;
155+ }
156+ ( Some ( yanked) , None ) => {
157+ version. yanked = yanked;
158+ version. yank_message = None ;
159+ }
160+ ( Some ( false ) , Some ( _) ) => {
161+ return Err ( bad_request ( "Cannot set yank message when unyanking" ) ) ;
162+ }
163+ ( None , Some ( message) ) => {
164+ if version. yanked {
165+ version. yank_message = Some ( message. clone ( ) ) ;
166+ } else {
167+ return Err ( bad_request (
168+ "Cannot update yank message for a version that is not yanked" ,
169+ ) ) ;
170+ }
171+ }
172+ // If both yanked and yank_message are None, do nothing.
173+ // This function only cares about updating the yanked state and yank message.
174+ ( None , None ) => { }
175+ }
176+ Ok ( ( ) )
177+ }
178+
179+ pub fn perform_version_yank_update (
180+ state : & AppState ,
181+ req : & Parts ,
182+ conn : & mut impl Conn ,
183+ version : & Version ,
184+ krate : & Crate ,
185+ ) -> AppResult < ( ) > {
147186 let auth = AuthCheck :: default ( )
148187 . with_endpoint_scope ( EndpointScope :: Yank )
149188 . for_crate ( & krate. name )
150189 . check ( req, conn) ?;
151190
152- // Add rate limiting check
153191 state
154192 . rate_limiter
155193 . check_rate_limit ( auth. user_id ( ) , LimitedAction :: YankUnyank , conn) ?;
@@ -158,7 +196,6 @@ fn apply_yank_update(
158196 let user = auth. user ( ) ;
159197 let owners = krate. owners ( conn) ?;
160198
161- // Check user rights
162199 if Handle :: current ( ) . block_on ( user. rights ( state, & owners) ) ? < Rights :: Publish {
163200 if user. is_admin {
164201 warn ! (
@@ -168,58 +205,41 @@ fn apply_yank_update(
168205 } else {
169206 return Err ( custom (
170207 StatusCode :: FORBIDDEN ,
171- "must already be an owner to update version " ,
208+ "must already be an owner to yank or unyank " ,
172209 ) ) ;
173210 }
174211 }
175212
176- diesel:: update ( & * version)
213+ // Check if the yanked state or yank message has changed
214+ let ( yanked, yank_message) = crate :: schema:: versions:: table
215+ . find ( version. id )
216+ . select ( (
217+ crate :: schema:: versions:: yanked,
218+ crate :: schema:: versions:: yank_message,
219+ ) )
220+ . first :: < ( bool , Option < String > ) > ( conn) ?;
221+
222+ if yanked == version. yanked && yank_message == version. yank_message {
223+ // No changes, return early
224+ return Ok ( ( ) ) ;
225+ }
226+
227+ diesel:: update ( version)
177228 . set ( (
178229 crate :: schema:: versions:: yanked. eq ( version. yanked ) ,
179230 crate :: schema:: versions:: yank_message. eq ( & version. yank_message ) ,
180231 ) )
181232 . execute ( conn) ?;
182233
183- // Add version owner action
184234 let action = if version. yanked {
185235 VersionAction :: Yank
186236 } else {
187237 VersionAction :: Unyank
188238 } ;
189239 insert_version_owner_action ( conn, version. id , user. id , api_token_id, action) ?;
190240
191- // Enqueue jobs
192241 jobs:: enqueue_sync_to_index ( & krate. name , conn) ?;
193242 UpdateDefaultVersion :: new ( krate. id ) . enqueue ( conn) ?;
194243
195244 Ok ( ( ) )
196245}
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