@@ -20,10 +20,10 @@ use phnxtypes::{
20
20
identifiers:: { QsClientId , QsUserId } ,
21
21
messages:: {
22
22
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 ,
27
27
QsVersionedProcessResponseIn , UpdateClientRecordParams , UpdateUserRecordParams ,
28
28
VersionError ,
29
29
} ,
@@ -32,13 +32,13 @@ use phnxtypes::{
32
32
CreateUserRecordParamsOut , PublishKeyPackagesParamsOut , QsRequestParamsOut ,
33
33
} ,
34
34
push_token:: EncryptedPushToken ,
35
- FriendshipToken ,
35
+ ApiVersion , FriendshipToken ,
36
36
} ,
37
37
} ;
38
38
use thiserror:: Error ;
39
39
use tls_codec:: { DeserializeBytes , Serialize } ;
40
40
41
- use crate :: { ApiClient , Protocol } ;
41
+ use crate :: { version :: api_version_negotiation , ApiClient , Protocol } ;
42
42
43
43
pub mod ws;
44
44
@@ -77,43 +77,31 @@ impl ApiClient {
77
77
request_params : QsRequestParamsOut ,
78
78
token_or_signing_key : AuthenticationMethod < ' _ , T > ,
79
79
) -> 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 ;
87
95
} ;
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
117
105
}
118
106
119
107
pub async fn qs_create_user (
@@ -391,3 +379,61 @@ impl ApiClient {
391
379
} )
392
380
}
393
381
}
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