@@ -20,10 +20,10 @@ use phnxtypes::{
2020 identifiers:: { QsClientId , QsUserId } ,
2121 messages:: {
2222 client_qs:: {
23- ClientKeyPackageParams , ClientKeyPackageResponse , CreateClientRecordResponse ,
24- CreateUserRecordResponse , DeleteClientRecordParams , DeleteUserRecordParams ,
25- DequeueMessagesParams , DequeueMessagesResponse , EncryptionKeyResponse ,
26- KeyPackageParams , KeyPackageResponseIn , QsProcessResponseIn ,
23+ ClientKeyPackageParams , ClientKeyPackageResponse , ClientToQsMessageTbs ,
24+ CreateClientRecordResponse , CreateUserRecordResponse , DeleteClientRecordParams ,
25+ DeleteUserRecordParams , DequeueMessagesParams , DequeueMessagesResponse ,
26+ EncryptionKeyResponse , KeyPackageParams , KeyPackageResponseIn , QsProcessResponseIn ,
2727 QsVersionedProcessResponseIn , UpdateClientRecordParams , UpdateUserRecordParams ,
2828 VersionError ,
2929 } ,
@@ -32,13 +32,13 @@ use phnxtypes::{
3232 CreateUserRecordParamsOut , PublishKeyPackagesParamsOut , QsRequestParamsOut ,
3333 } ,
3434 push_token:: EncryptedPushToken ,
35- FriendshipToken ,
35+ ApiVersion , FriendshipToken ,
3636 } ,
3737} ;
3838use thiserror:: Error ;
3939use tls_codec:: { DeserializeBytes , Serialize } ;
4040
41- use crate :: { ApiClient , Protocol } ;
41+ use crate :: { version :: api_version_negotiation , ApiClient , Protocol } ;
4242
4343pub mod ws;
4444
@@ -77,43 +77,31 @@ impl ApiClient {
7777 request_params : QsRequestParamsOut ,
7878 token_or_signing_key : AuthenticationMethod < ' _ , T > ,
7979 ) -> Result < QsProcessResponseIn , QsRequestError > {
80- let tbs = ClientToQsMessageTbsOut :: new ( request_params) ;
81- let message = match token_or_signing_key {
82- AuthenticationMethod :: Token ( token) => ClientToQsMessageOut :: from_token ( tbs, token) ,
83- AuthenticationMethod :: SigningKey ( signing_key) => tbs
84- . sign ( signing_key)
85- . map_err ( |_| QsRequestError :: LibraryError ) ?,
86- AuthenticationMethod :: None => ClientToQsMessageOut :: without_signature ( tbs) ,
80+ let api_version = self . negotiated_versions ( ) . qs_api_version ( ) ;
81+
82+ let message = sign_params ( request_params, & token_or_signing_key, api_version) ?;
83+ let endpoint = self . build_url ( Protocol :: Http , ENDPOINT_QS ) ;
84+ let response = send_qs_message ( & self . client , & endpoint, & message) . await ?;
85+
86+ // check if we need to negotiate a new API version
87+ let Some ( accepted_version) = api_version_negotiation (
88+ & response,
89+ api_version,
90+ ClientToQsMessageTbs :: SUPPORTED_API_VERSIONS ,
91+ )
92+ . transpose ( ) ?
93+ else {
94+ return process_response ( response) . await ;
8795 } ;
88- let message_bytes = message
89- . tls_serialize_detached ( )
90- . map_err ( |_| QsRequestError :: LibraryError ) ?;
91- match self
92- . client
93- . post ( self . build_url ( Protocol :: Http , ENDPOINT_QS ) )
94- . body ( message_bytes)
95- . send ( )
96- . await
97- {
98- Ok ( res) => {
99- let status = res. status ( ) ;
100- if status. is_success ( ) {
101- // Success!
102- let ds_proc_res_bytes = res. bytes ( ) . await . map_err ( QsRequestError :: Reqwest ) ?;
103- let ds_proc_res = QsVersionedProcessResponseIn :: tls_deserialize_exact_bytes (
104- & ds_proc_res_bytes,
105- )
106- . map_err ( QsRequestError :: Tls ) ?;
107- migrate_qs_process_response ( ds_proc_res)
108- } else {
109- // Error
110- let error = res. text ( ) . await . map_err ( QsRequestError :: Reqwest ) ?;
111- Err ( QsRequestError :: RequestFailed { status, error } )
112- }
113- }
114- // A network error occurred.
115- Err ( err) => Err ( QsRequestError :: NetworkError ( err. to_string ( ) ) ) ,
116- }
96+
97+ self . negotiated_versions ( )
98+ . set_qs_api_version ( accepted_version) ;
99+
100+ let request_params = message. into_payload ( ) . into_unversioned_params ( ) ;
101+ let message = sign_params ( request_params, & token_or_signing_key, accepted_version) ?;
102+
103+ let response = send_qs_message ( & self . client , & endpoint, & message) . await ?;
104+ process_response ( response) . await
117105 }
118106
119107 pub async fn qs_create_user (
@@ -391,3 +379,61 @@ impl ApiClient {
391379 } )
392380 }
393381}
382+
383+ async fn process_response (
384+ response : reqwest:: Response ,
385+ ) -> Result < QsProcessResponseIn , QsRequestError > {
386+ let status = response. status ( ) ;
387+ if status. is_success ( ) {
388+ let bytes = response. bytes ( ) . await . map_err ( QsRequestError :: Reqwest ) ?;
389+ let qs_response = QsVersionedProcessResponseIn :: tls_deserialize_exact_bytes ( & bytes)
390+ . map_err ( QsRequestError :: Tls ) ?;
391+ migrate_qs_process_response ( qs_response)
392+ } else {
393+ let error = response
394+ . text ( )
395+ . await
396+ . unwrap_or_else ( |error| format ! ( "unprocessable response body due to: {error}" ) ) ;
397+ Err ( QsRequestError :: RequestFailed { status, error } )
398+ }
399+ }
400+
401+ async fn send_qs_message (
402+ client : & reqwest:: Client ,
403+ endpoint : & str ,
404+ message : & ClientToQsMessageOut ,
405+ ) -> Result < reqwest:: Response , QsRequestError > {
406+ client
407+ . post ( endpoint)
408+ . body (
409+ message
410+ . tls_serialize_detached ( )
411+ . map_err ( |_| QsRequestError :: LibraryError ) ?,
412+ )
413+ . send ( )
414+ . await
415+ . map_err ( From :: from)
416+ }
417+
418+ fn sign_params < T : SigningKeyBehaviour > (
419+ request_params : QsRequestParamsOut ,
420+ token_or_signing_key : & AuthenticationMethod < ' _ , T > ,
421+ api_version : ApiVersion ,
422+ ) -> Result < ClientToQsMessageOut , QsRequestError > {
423+ let tbs = ClientToQsMessageTbsOut :: with_api_version ( api_version, request_params) . ok_or_else (
424+ || {
425+ VersionError :: new (
426+ api_version,
427+ ClientToQsMessageTbs :: SUPPORTED_API_VERSIONS . to_vec ( ) ,
428+ )
429+ } ,
430+ ) ?;
431+ let message = match token_or_signing_key {
432+ AuthenticationMethod :: Token ( token) => ClientToQsMessageOut :: from_token ( tbs, token. clone ( ) ) ,
433+ AuthenticationMethod :: SigningKey ( signing_key) => tbs
434+ . sign ( * signing_key)
435+ . map_err ( |_| QsRequestError :: LibraryError ) ?,
436+ AuthenticationMethod :: None => ClientToQsMessageOut :: without_signature ( tbs) ,
437+ } ;
438+ Ok ( message)
439+ }
0 commit comments