google_cloud_gax/
options.rs

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Client configuration and per request options.
16//!
17//! While the client library  defaults are intended to work for most
18//! applications, it is sometimes necessary to change the configuration. Notably
19//! the default endpoint, and the default authentication credentials do not work
20//! for some applications.
21//!
22//! Likewise, applications may need to customize the behavior of some calls made
23//! via a client, even a customized one. Applications sometimes change the
24//! timeout for an specific call, or change the retry configuration. The
25//! `*Builder` returned by each client method implements the
26//! [RequestOptionsBuilder] trait where applications can override some defaults.
27
28use crate::backoff_policy::{BackoffPolicy, BackoffPolicyArg};
29use crate::polling_backoff_policy::{PollingBackoffPolicy, PollingBackoffPolicyArg};
30use crate::polling_error_policy::{PollingErrorPolicy, PollingErrorPolicyArg};
31use crate::retry_policy::{RetryPolicy, RetryPolicyArg};
32use crate::retry_throttler::{RetryThrottlerArg, SharedRetryThrottler};
33use std::sync::Arc;
34
35/// A set of options configuring a single request.
36///
37/// Application only use this class directly in mocks, where they may want to
38/// verify their application has configured all the right request parameters and
39/// options.
40///
41/// All other code uses this type indirectly, via the per-request builders.
42#[derive(Clone, Debug, Default)]
43pub struct RequestOptions {
44    idempotent: Option<bool>,
45    user_agent: Option<String>,
46    attempt_timeout: Option<std::time::Duration>,
47    retry_policy: Option<Arc<dyn RetryPolicy>>,
48    backoff_policy: Option<Arc<dyn BackoffPolicy>>,
49    retry_throttler: Option<SharedRetryThrottler>,
50    polling_error_policy: Option<Arc<dyn PollingErrorPolicy>>,
51    polling_backoff_policy: Option<Arc<dyn PollingBackoffPolicy>>,
52}
53
54impl RequestOptions {
55    /// Gets the idempotency
56    pub fn idempotent(&self) -> Option<bool> {
57        self.idempotent
58    }
59
60    /// Treat the RPC underlying RPC in this method as idempotent.
61    ///
62    /// If a retry policy is configured, the policy may examine the idempotency
63    /// and the error details to decide if the error is retryable. Typically
64    /// [idempotent] RPCs are safe to retry under more error conditions
65    /// than non-idempotent RPCs.
66    ///
67    /// The client libraries provide a default for RPC idempotency, based on the
68    /// HTTP method (`GET`, `POST`, `DELETE`, etc.).
69    ///
70    /// [idempotent]: https://en.wikipedia.org/wiki/Idempotence
71    pub fn set_idempotency(&mut self, value: bool) {
72        self.idempotent = Some(value);
73    }
74
75    /// Set the idempotency for the underlying RPC unless it is already set.
76    ///
77    /// If [set_idempotency][Self::set_idempotency] was already called this
78    /// method has no effect. Otherwise it sets the idempotency. The client
79    /// libraries use this to provide a default idempotency value.
80    pub fn set_default_idempotency(mut self, default: bool) -> Self {
81        self.idempotent.get_or_insert(default);
82        self
83    }
84
85    /// Prepends this prefix to the user agent header value.
86    pub fn set_user_agent<T: Into<String>>(&mut self, v: T) {
87        self.user_agent = Some(v.into());
88    }
89
90    /// Gets the current user-agent prefix
91    pub fn user_agent(&self) -> &Option<String> {
92        &self.user_agent
93    }
94
95    /// Sets the per-attempt timeout.
96    ///
97    /// When using a retry loop, this affects the timeout for each attempt. The
98    /// overall timeout for a request is set by the retry policy.
99    pub fn set_attempt_timeout<T: Into<std::time::Duration>>(&mut self, v: T) {
100        self.attempt_timeout = Some(v.into());
101    }
102
103    /// Gets the current per-attempt timeout.
104    pub fn attempt_timeout(&self) -> &Option<std::time::Duration> {
105        &self.attempt_timeout
106    }
107
108    /// Get the current retry policy override, if any.
109    pub fn retry_policy(&self) -> &Option<Arc<dyn RetryPolicy>> {
110        &self.retry_policy
111    }
112
113    /// Sets the retry policy configuration.
114    pub fn set_retry_policy<V: Into<RetryPolicyArg>>(&mut self, v: V) {
115        self.retry_policy = Some(v.into().0);
116    }
117
118    /// Get the current backoff policy override, if any.
119    pub fn backoff_policy(&self) -> &Option<Arc<dyn BackoffPolicy>> {
120        &self.backoff_policy
121    }
122
123    /// Sets the backoff policy configuration.
124    pub fn set_backoff_policy<V: Into<BackoffPolicyArg>>(&mut self, v: V) {
125        self.backoff_policy = Some(v.into().0);
126    }
127
128    /// Get the current retry throttler override, if any.
129    pub fn retry_throttler(&self) -> &Option<SharedRetryThrottler> {
130        &self.retry_throttler
131    }
132
133    /// Sets the retry throttling configuration.
134    pub fn set_retry_throttler<V: Into<RetryThrottlerArg>>(&mut self, v: V) {
135        self.retry_throttler = Some(v.into().0);
136    }
137
138    /// Get the current polling policy override, if any.
139    pub fn polling_error_policy(&self) -> &Option<Arc<dyn PollingErrorPolicy>> {
140        &self.polling_error_policy
141    }
142
143    /// Sets the polling policy configuration.
144    pub fn set_polling_error_policy<V: Into<PollingErrorPolicyArg>>(&mut self, v: V) {
145        self.polling_error_policy = Some(v.into().0);
146    }
147
148    /// Get the current polling backoff policy override, if any.
149    pub fn polling_backoff_policy(&self) -> &Option<Arc<dyn PollingBackoffPolicy>> {
150        &self.polling_backoff_policy
151    }
152
153    /// Sets the backoff policy configuration.
154    pub fn set_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(&mut self, v: V) {
155        self.polling_backoff_policy = Some(v.into().0);
156    }
157}
158
159/// Implementations of this trait provide setters to configure request options.
160///
161/// The Google Cloud Client Libraries for Rust provide a builder for each RPC.
162/// These builders can be used to set the request parameters, e.g., the name of
163/// the resource targeted by the RPC, as well as any options affecting the
164/// request, such as additional headers or timeouts.
165pub trait RequestOptionsBuilder: internal::RequestBuilder {
166    /// If `v` is `true`, treat the RPC underlying this method as idempotent.
167    fn with_idempotency(self, v: bool) -> Self;
168
169    /// Set the user agent header.
170    fn with_user_agent<V: Into<String>>(self, v: V) -> Self;
171
172    /// Sets the per-attempt timeout.
173    ///
174    /// When using a retry loop, this affects the timeout for each attempt. The
175    /// overall timeout for a request is set by the retry policy.
176    fn with_attempt_timeout<V: Into<std::time::Duration>>(self, v: V) -> Self;
177
178    /// Sets the retry policy configuration.
179    fn with_retry_policy<V: Into<RetryPolicyArg>>(self, v: V) -> Self;
180
181    /// Sets the backoff policy configuration.
182    fn with_backoff_policy<V: Into<BackoffPolicyArg>>(self, v: V) -> Self;
183
184    /// Sets the retry throttler configuration.
185    fn with_retry_throttler<V: Into<RetryThrottlerArg>>(self, v: V) -> Self;
186
187    /// Sets the polling error policy configuration.
188    fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(self, v: V) -> Self;
189
190    /// Sets the polling backoff policy configuration.
191    fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(self, v: V) -> Self;
192}
193
194/// This module contains implementation details. It is not part of the public
195/// API. Types inside may be changed or removed without warnings. Applications
196/// should not use any types contained within.
197#[doc(hidden)]
198pub mod internal {
199    /// Simplify implementation of the [super::RequestOptionsBuilder] trait in
200    /// generated code.
201    ///
202    /// This is an implementation detail, most applications have little need to
203    /// worry about or use this trait.
204    pub trait RequestBuilder {
205        fn request_options(&mut self) -> &mut super::RequestOptions;
206    }
207}
208
209/// Implements the sealed [RequestOptionsBuilder] trait.
210impl<T> RequestOptionsBuilder for T
211where
212    T: internal::RequestBuilder,
213{
214    fn with_idempotency(mut self, v: bool) -> Self {
215        self.request_options().set_idempotency(v);
216        self
217    }
218
219    fn with_user_agent<V: Into<String>>(mut self, v: V) -> Self {
220        self.request_options().set_user_agent(v);
221        self
222    }
223
224    fn with_attempt_timeout<V: Into<std::time::Duration>>(mut self, v: V) -> Self {
225        self.request_options().set_attempt_timeout(v);
226        self
227    }
228
229    fn with_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
230        self.request_options().set_retry_policy(v);
231        self
232    }
233
234    fn with_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
235        self.request_options().set_backoff_policy(v);
236        self
237    }
238
239    fn with_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
240        self.request_options().set_retry_throttler(v);
241        self
242    }
243
244    fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(mut self, v: V) -> Self {
245        self.request_options().set_polling_error_policy(v);
246        self
247    }
248
249    fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(mut self, v: V) -> Self {
250        self.request_options().set_polling_backoff_policy(v);
251        self
252    }
253}
254
255#[cfg(test)]
256mod test {
257    use super::internal::*;
258    use super::*;
259    use crate::exponential_backoff::ExponentialBackoffBuilder;
260    use crate::polling_error_policy;
261    use crate::retry_policy::LimitedAttemptCount;
262    use crate::retry_throttler::AdaptiveThrottler;
263    use std::time::Duration;
264    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
265
266    #[derive(Debug, Default)]
267    struct TestBuilder {
268        request_options: RequestOptions,
269    }
270    impl RequestBuilder for TestBuilder {
271        fn request_options(&mut self) -> &mut RequestOptions {
272            &mut self.request_options
273        }
274    }
275
276    #[test]
277    fn request_options() {
278        let mut opts = RequestOptions::default();
279
280        assert_eq!(opts.idempotent, None);
281        opts.set_idempotency(true);
282        assert_eq!(opts.idempotent(), Some(true));
283        opts.set_idempotency(false);
284        assert_eq!(opts.idempotent(), Some(false));
285
286        opts.set_user_agent("test-only");
287        assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
288        assert_eq!(opts.attempt_timeout(), &None);
289
290        let d = Duration::from_secs(123);
291        opts.set_attempt_timeout(d);
292        assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
293        assert_eq!(opts.attempt_timeout(), &Some(d));
294
295        opts.set_retry_policy(LimitedAttemptCount::new(3));
296        assert!(opts.retry_policy().is_some(), "{opts:?}");
297
298        opts.set_backoff_policy(ExponentialBackoffBuilder::new().clamp());
299        assert!(opts.backoff_policy().is_some(), "{opts:?}");
300
301        opts.set_retry_throttler(AdaptiveThrottler::default());
302        assert!(opts.retry_throttler().is_some(), "{opts:?}");
303
304        opts.set_polling_error_policy(polling_error_policy::Aip194Strict);
305        assert!(opts.polling_error_policy().is_some(), "{opts:?}");
306
307        opts.set_polling_backoff_policy(ExponentialBackoffBuilder::new().clamp());
308        assert!(opts.polling_backoff_policy().is_some(), "{opts:?}");
309    }
310
311    #[test]
312    fn request_options_idempotency() {
313        let opts = RequestOptions::default().set_default_idempotency(true);
314        assert_eq!(opts.idempotent(), Some(true));
315        let opts = opts.set_default_idempotency(false);
316        assert_eq!(opts.idempotent(), Some(true));
317
318        let opts = RequestOptions::default().set_default_idempotency(false);
319        assert_eq!(opts.idempotent(), Some(false));
320        let opts = opts.set_default_idempotency(true);
321        assert_eq!(opts.idempotent(), Some(false));
322    }
323
324    #[test]
325    fn request_options_builder() -> Result {
326        let mut builder = TestBuilder::default();
327        assert_eq!(builder.request_options().user_agent(), &None);
328        assert_eq!(builder.request_options().attempt_timeout(), &None);
329
330        let mut builder = TestBuilder::default().with_idempotency(true);
331        assert_eq!(builder.request_options().idempotent(), Some(true));
332        let mut builder = TestBuilder::default().with_idempotency(false);
333        assert_eq!(builder.request_options().idempotent(), Some(false));
334
335        let mut builder = TestBuilder::default().with_user_agent("test-only");
336        assert_eq!(
337            builder.request_options().user_agent().as_deref(),
338            Some("test-only")
339        );
340        assert_eq!(builder.request_options().attempt_timeout(), &None);
341
342        let d = Duration::from_secs(123);
343        let mut builder = TestBuilder::default().with_attempt_timeout(d);
344        assert_eq!(builder.request_options().user_agent(), &None);
345        assert_eq!(builder.request_options().attempt_timeout(), &Some(d));
346
347        let mut builder = TestBuilder::default().with_retry_policy(LimitedAttemptCount::new(3));
348        assert!(
349            builder.request_options().retry_policy().is_some(),
350            "{builder:?}"
351        );
352
353        let mut builder =
354            TestBuilder::default().with_backoff_policy(ExponentialBackoffBuilder::new().build()?);
355        assert!(
356            builder.request_options().backoff_policy().is_some(),
357            "{builder:?}"
358        );
359
360        let mut builder = TestBuilder::default().with_retry_throttler(AdaptiveThrottler::default());
361        assert!(
362            builder.request_options().retry_throttler().is_some(),
363            "{builder:?}"
364        );
365
366        let mut builder =
367            TestBuilder::default().with_polling_error_policy(polling_error_policy::Aip194Strict);
368        assert!(
369            builder.request_options().polling_error_policy().is_some(),
370            "{builder:?}"
371        );
372
373        let mut builder = TestBuilder::default()
374            .with_polling_backoff_policy(ExponentialBackoffBuilder::new().build()?);
375        assert!(
376            builder.request_options().polling_backoff_policy().is_some(),
377            "{builder:?}"
378        );
379
380        Ok(())
381    }
382}