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(crate) fn set_default_idempotency(&mut self, default: bool) {
81        self.idempotent.get_or_insert(default);
82    }
83
84    /// Prepends this prefix to the user agent header value.
85    pub fn set_user_agent<T: Into<String>>(&mut self, v: T) {
86        self.user_agent = Some(v.into());
87    }
88
89    /// Gets the current user-agent prefix
90    pub fn user_agent(&self) -> &Option<String> {
91        &self.user_agent
92    }
93
94    /// Sets the per-attempt timeout.
95    ///
96    /// When using a retry loop, this affects the timeout for each attempt. The
97    /// overall timeout for a request is set by the retry policy.
98    pub fn set_attempt_timeout<T: Into<std::time::Duration>>(&mut self, v: T) {
99        self.attempt_timeout = Some(v.into());
100    }
101
102    /// Gets the current per-attempt timeout.
103    pub fn attempt_timeout(&self) -> &Option<std::time::Duration> {
104        &self.attempt_timeout
105    }
106
107    /// Get the current retry policy override, if any.
108    pub fn retry_policy(&self) -> &Option<Arc<dyn RetryPolicy>> {
109        &self.retry_policy
110    }
111
112    /// Sets the retry policy configuration.
113    pub fn set_retry_policy<V: Into<RetryPolicyArg>>(&mut self, v: V) {
114        self.retry_policy = Some(v.into().0);
115    }
116
117    /// Get the current backoff policy override, if any.
118    pub fn backoff_policy(&self) -> &Option<Arc<dyn BackoffPolicy>> {
119        &self.backoff_policy
120    }
121
122    /// Sets the backoff policy configuration.
123    pub fn set_backoff_policy<V: Into<BackoffPolicyArg>>(&mut self, v: V) {
124        self.backoff_policy = Some(v.into().0);
125    }
126
127    /// Get the current retry throttler override, if any.
128    pub fn retry_throttler(&self) -> &Option<SharedRetryThrottler> {
129        &self.retry_throttler
130    }
131
132    /// Sets the retry throttling configuration.
133    pub fn set_retry_throttler<V: Into<RetryThrottlerArg>>(&mut self, v: V) {
134        self.retry_throttler = Some(v.into().0);
135    }
136
137    /// Get the current polling policy override, if any.
138    pub fn polling_error_policy(&self) -> &Option<Arc<dyn PollingErrorPolicy>> {
139        &self.polling_error_policy
140    }
141
142    /// Sets the polling policy configuration.
143    pub fn set_polling_error_policy<V: Into<PollingErrorPolicyArg>>(&mut self, v: V) {
144        self.polling_error_policy = Some(v.into().0);
145    }
146
147    /// Get the current polling backoff policy override, if any.
148    pub fn polling_backoff_policy(&self) -> &Option<Arc<dyn PollingBackoffPolicy>> {
149        &self.polling_backoff_policy
150    }
151
152    /// Sets the backoff policy configuration.
153    pub fn set_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(&mut self, v: V) {
154        self.polling_backoff_policy = Some(v.into().0);
155    }
156}
157
158/// Implementations of this trait provide setters to configure request options.
159///
160/// The Google Cloud Client Libraries for Rust provide a builder for each RPC.
161/// These builders can be used to set the request parameters, e.g., the name of
162/// the resource targeted by the RPC, as well as any options affecting the
163/// request, such as additional headers or timeouts.
164pub trait RequestOptionsBuilder: internal::RequestBuilder {
165    /// If `v` is `true`, treat the RPC underlying this method as idempotent.
166    fn with_idempotency(self, v: bool) -> Self;
167
168    /// Set the user agent header.
169    fn with_user_agent<V: Into<String>>(self, v: V) -> Self;
170
171    /// Sets the per-attempt timeout.
172    ///
173    /// When using a retry loop, this affects the timeout for each attempt. The
174    /// overall timeout for a request is set by the retry policy.
175    fn with_attempt_timeout<V: Into<std::time::Duration>>(self, v: V) -> Self;
176
177    /// Sets the retry policy configuration.
178    fn with_retry_policy<V: Into<RetryPolicyArg>>(self, v: V) -> Self;
179
180    /// Sets the backoff policy configuration.
181    fn with_backoff_policy<V: Into<BackoffPolicyArg>>(self, v: V) -> Self;
182
183    /// Sets the retry throttler configuration.
184    fn with_retry_throttler<V: Into<RetryThrottlerArg>>(self, v: V) -> Self;
185
186    /// Sets the polling error policy configuration.
187    fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(self, v: V) -> Self;
188
189    /// Sets the polling backoff policy configuration.
190    fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(self, v: V) -> Self;
191}
192
193#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
194pub mod internal {
195    //! This module contains implementation details. It is not part of the
196    //! public API. Types and functions in this module may be changed or removed
197    //! without warnings. Applications should not use any types contained
198    //! within.
199    use super::RequestOptions;
200
201    /// Simplify implementation of the [super::RequestOptionsBuilder] trait in
202    /// generated code.
203    ///
204    /// This is an implementation detail, most applications have little need to
205    /// worry about or use this trait.
206    pub trait RequestBuilder {
207        fn request_options(&mut self) -> &mut RequestOptions;
208    }
209
210    pub fn set_default_idempotency(mut options: RequestOptions, default: bool) -> RequestOptions {
211        options.set_default_idempotency(default);
212        options
213    }
214}
215
216/// Implements the sealed [RequestOptionsBuilder] trait.
217impl<T> RequestOptionsBuilder for T
218where
219    T: internal::RequestBuilder,
220{
221    fn with_idempotency(mut self, v: bool) -> Self {
222        self.request_options().set_idempotency(v);
223        self
224    }
225
226    fn with_user_agent<V: Into<String>>(mut self, v: V) -> Self {
227        self.request_options().set_user_agent(v);
228        self
229    }
230
231    fn with_attempt_timeout<V: Into<std::time::Duration>>(mut self, v: V) -> Self {
232        self.request_options().set_attempt_timeout(v);
233        self
234    }
235
236    fn with_retry_policy<V: Into<RetryPolicyArg>>(mut self, v: V) -> Self {
237        self.request_options().set_retry_policy(v);
238        self
239    }
240
241    fn with_backoff_policy<V: Into<BackoffPolicyArg>>(mut self, v: V) -> Self {
242        self.request_options().set_backoff_policy(v);
243        self
244    }
245
246    fn with_retry_throttler<V: Into<RetryThrottlerArg>>(mut self, v: V) -> Self {
247        self.request_options().set_retry_throttler(v);
248        self
249    }
250
251    fn with_polling_error_policy<V: Into<PollingErrorPolicyArg>>(mut self, v: V) -> Self {
252        self.request_options().set_polling_error_policy(v);
253        self
254    }
255
256    fn with_polling_backoff_policy<V: Into<PollingBackoffPolicyArg>>(mut self, v: V) -> Self {
257        self.request_options().set_polling_backoff_policy(v);
258        self
259    }
260}
261
262#[cfg(test)]
263mod test {
264    use super::internal::*;
265    use super::*;
266    use crate::exponential_backoff::ExponentialBackoffBuilder;
267    use crate::polling_error_policy;
268    use crate::retry_policy::LimitedAttemptCount;
269    use crate::retry_throttler::AdaptiveThrottler;
270    use std::time::Duration;
271    type Result = std::result::Result<(), Box<dyn std::error::Error>>;
272
273    #[derive(Debug, Default)]
274    struct TestBuilder {
275        request_options: RequestOptions,
276    }
277    impl RequestBuilder for TestBuilder {
278        fn request_options(&mut self) -> &mut RequestOptions {
279            &mut self.request_options
280        }
281    }
282
283    #[test]
284    fn request_options() {
285        let mut opts = RequestOptions::default();
286
287        assert_eq!(opts.idempotent, None);
288        opts.set_idempotency(true);
289        assert_eq!(opts.idempotent(), Some(true));
290        opts.set_idempotency(false);
291        assert_eq!(opts.idempotent(), Some(false));
292
293        opts.set_user_agent("test-only");
294        assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
295        assert_eq!(opts.attempt_timeout(), &None);
296
297        let d = Duration::from_secs(123);
298        opts.set_attempt_timeout(d);
299        assert_eq!(opts.user_agent().as_deref(), Some("test-only"));
300        assert_eq!(opts.attempt_timeout(), &Some(d));
301
302        opts.set_retry_policy(LimitedAttemptCount::new(3));
303        assert!(opts.retry_policy().is_some(), "{opts:?}");
304
305        opts.set_backoff_policy(ExponentialBackoffBuilder::new().clamp());
306        assert!(opts.backoff_policy().is_some(), "{opts:?}");
307
308        opts.set_retry_throttler(AdaptiveThrottler::default());
309        assert!(opts.retry_throttler().is_some(), "{opts:?}");
310
311        opts.set_polling_error_policy(polling_error_policy::Aip194Strict);
312        assert!(opts.polling_error_policy().is_some(), "{opts:?}");
313
314        opts.set_polling_backoff_policy(ExponentialBackoffBuilder::new().clamp());
315        assert!(opts.polling_backoff_policy().is_some(), "{opts:?}");
316    }
317
318    #[test]
319    fn request_options_idempotency() {
320        let opts = set_default_idempotency(RequestOptions::default(), true);
321        assert_eq!(opts.idempotent(), Some(true));
322        let opts = set_default_idempotency(opts, false);
323        assert_eq!(opts.idempotent(), Some(true));
324
325        let opts = set_default_idempotency(RequestOptions::default(), false);
326        assert_eq!(opts.idempotent(), Some(false));
327        let opts = set_default_idempotency(opts, true);
328        assert_eq!(opts.idempotent(), Some(false));
329    }
330
331    #[test]
332    fn request_options_builder() -> Result {
333        let mut builder = TestBuilder::default();
334        assert_eq!(builder.request_options().user_agent(), &None);
335        assert_eq!(builder.request_options().attempt_timeout(), &None);
336
337        let mut builder = TestBuilder::default().with_idempotency(true);
338        assert_eq!(builder.request_options().idempotent(), Some(true));
339        let mut builder = TestBuilder::default().with_idempotency(false);
340        assert_eq!(builder.request_options().idempotent(), Some(false));
341
342        let mut builder = TestBuilder::default().with_user_agent("test-only");
343        assert_eq!(
344            builder.request_options().user_agent().as_deref(),
345            Some("test-only")
346        );
347        assert_eq!(builder.request_options().attempt_timeout(), &None);
348
349        let d = Duration::from_secs(123);
350        let mut builder = TestBuilder::default().with_attempt_timeout(d);
351        assert_eq!(builder.request_options().user_agent(), &None);
352        assert_eq!(builder.request_options().attempt_timeout(), &Some(d));
353
354        let mut builder = TestBuilder::default().with_retry_policy(LimitedAttemptCount::new(3));
355        assert!(
356            builder.request_options().retry_policy().is_some(),
357            "{builder:?}"
358        );
359
360        let mut builder =
361            TestBuilder::default().with_backoff_policy(ExponentialBackoffBuilder::new().build()?);
362        assert!(
363            builder.request_options().backoff_policy().is_some(),
364            "{builder:?}"
365        );
366
367        let mut builder = TestBuilder::default().with_retry_throttler(AdaptiveThrottler::default());
368        assert!(
369            builder.request_options().retry_throttler().is_some(),
370            "{builder:?}"
371        );
372
373        let mut builder =
374            TestBuilder::default().with_polling_error_policy(polling_error_policy::Aip194Strict);
375        assert!(
376            builder.request_options().polling_error_policy().is_some(),
377            "{builder:?}"
378        );
379
380        let mut builder = TestBuilder::default()
381            .with_polling_backoff_policy(ExponentialBackoffBuilder::new().build()?);
382        assert!(
383            builder.request_options().polling_backoff_policy().is_some(),
384            "{builder:?}"
385        );
386
387        Ok(())
388    }
389}