11use crate :: app:: AppState ;
22use crate :: auth:: AuthCheck ;
33use crate :: auth:: Authentication ;
4- use crate :: controllers:: helpers:: pagination:: { Page , PaginationOptions } ;
4+ use crate :: controllers:: helpers:: pagination:: { Page , PaginationOptions , PaginationQueryParams } ;
55use crate :: models:: { Crate , CrateOwnerInvitation , Rights , User } ;
66use crate :: schema:: { crate_owner_invitations, crates, users} ;
7- use crate :: util:: errors:: { bad_request, forbidden, internal, AppResult } ;
7+ use crate :: util:: errors:: { bad_request, forbidden, internal, AppResult , BoxedAppError } ;
88use crate :: util:: RequestUtils ;
99use crate :: views:: {
1010 EncodableCrateOwnerInvitation , EncodableCrateOwnerInvitationV1 , EncodablePublicUser ,
1111 InvitationResponse ,
1212} ;
13- use axum:: extract:: Path ;
13+ use axum:: extract:: { FromRequestParts , Path , Query } ;
1414use axum:: Json ;
1515use axum_extra:: json;
1616use axum_extra:: response:: ErasedJson ;
@@ -70,28 +70,38 @@ pub async fn list_crate_owner_invitations_for_user(
7070 } ) )
7171}
7272
73+ #[ derive( Debug , Deserialize , FromRequestParts , utoipa:: IntoParams ) ]
74+ #[ from_request( via( Query ) ) ]
75+ #[ into_params( parameter_in = Query ) ]
76+ pub struct ListQueryParams {
77+ /// Filter crate owner invitations by crate name.
78+ ///
79+ /// Only crate owners can query pending invitations for their crate.
80+ crate_name : Option < String > ,
81+
82+ /// The ID of the user who was invited to be a crate owner.
83+ ///
84+ /// This parameter needs to match the authenticated user's ID.
85+ invitee_id : Option < i32 > ,
86+ }
87+
7388/// List all crate owner invitations for a crate or user.
7489#[ utoipa:: path(
7590 get,
7691 path = "/api/private/crate_owner_invitations" ,
92+ params( ListQueryParams , PaginationQueryParams ) ,
7793 tag = "owners" ,
7894 responses( ( status = 200 , description = "Successful Response" ) ) ,
7995) ]
8096pub async fn list_crate_owner_invitations (
8197 app : AppState ,
98+ params : ListQueryParams ,
8299 req : Parts ,
83100) -> AppResult < Json < PrivateListResponse > > {
84101 let mut conn = app. db_read ( ) . await ?;
85102 let auth = AuthCheck :: only_cookie ( ) . check ( & req, & mut conn) . await ?;
86103
87- let filter = if let Some ( crate_name) = req. query ( ) . get ( "crate_name" ) {
88- ListFilter :: CrateName ( crate_name. clone ( ) )
89- } else if let Some ( id) = req. query ( ) . get ( "invitee_id" ) . and_then ( |i| i. parse ( ) . ok ( ) ) {
90- ListFilter :: InviteeId ( id)
91- } else {
92- return Err ( bad_request ( "missing or invalid filter" ) ) ;
93- } ;
94-
104+ let filter = params. try_into ( ) ?;
95105 let list = prepare_list ( & app, & req, auth, filter, & mut conn) . await ?;
96106 Ok ( Json ( list) )
97107}
@@ -101,6 +111,22 @@ enum ListFilter {
101111 InviteeId ( i32 ) ,
102112}
103113
114+ impl TryFrom < ListQueryParams > for ListFilter {
115+ type Error = BoxedAppError ;
116+
117+ fn try_from ( params : ListQueryParams ) -> Result < Self , Self :: Error > {
118+ let filter = if let Some ( crate_name) = params. crate_name {
119+ ListFilter :: CrateName ( crate_name. clone ( ) )
120+ } else if let Some ( id) = params. invitee_id {
121+ ListFilter :: InviteeId ( id)
122+ } else {
123+ return Err ( bad_request ( "missing or invalid filter" ) ) ;
124+ } ;
125+
126+ Ok ( filter)
127+ }
128+ }
129+
104130async fn prepare_list (
105131 state : & AppState ,
106132 req : & Parts ,
0 commit comments