| 
1 | 1 | use aws_credential_types::Credentials;  | 
2 | 2 | use aws_sdk_cloudfront::config::retry::RetryConfig;  | 
3 | 3 | use aws_sdk_cloudfront::config::{BehaviorVersion, Region};  | 
 | 4 | +use aws_sdk_cloudfront::error::{BuildError, SdkError};  | 
 | 5 | +use aws_sdk_cloudfront::operation::create_invalidation::CreateInvalidationError;  | 
4 | 6 | use aws_sdk_cloudfront::types::{InvalidationBatch, Paths};  | 
5 | 7 | use aws_sdk_cloudfront::{Client, Config};  | 
6 | 8 | use tracing::{debug, instrument, warn};  | 
7 | 9 | 
 
  | 
 | 10 | +#[derive(Debug, thiserror::Error)]  | 
 | 11 | +pub enum CloudFrontError {  | 
 | 12 | +    #[error(transparent)]  | 
 | 13 | +    BuildError(#[from] BuildError),  | 
 | 14 | +    #[error(transparent)]  | 
 | 15 | +    SdkError(Box<SdkError<CreateInvalidationError>>),  | 
 | 16 | +}  | 
 | 17 | + | 
 | 18 | +impl From<SdkError<CreateInvalidationError>> for CloudFrontError {  | 
 | 19 | +    fn from(err: SdkError<CreateInvalidationError>) -> Self {  | 
 | 20 | +        CloudFrontError::SdkError(Box::new(err))  | 
 | 21 | +    }  | 
 | 22 | +}  | 
 | 23 | + | 
 | 24 | +impl CloudFrontError {  | 
 | 25 | +    pub fn is_too_many_invalidations_error(&self) -> bool {  | 
 | 26 | +        let CloudFrontError::SdkError(sdk_error) = self else {  | 
 | 27 | +            return false;  | 
 | 28 | +        };  | 
 | 29 | + | 
 | 30 | +        let Some(service_error) = sdk_error.as_service_error() else {  | 
 | 31 | +            return false;  | 
 | 32 | +        };  | 
 | 33 | + | 
 | 34 | +        service_error.is_too_many_invalidations_in_progress()  | 
 | 35 | +    }  | 
 | 36 | +}  | 
 | 37 | + | 
8 | 38 | pub struct CloudFront {  | 
9 | 39 |     client: Client,  | 
10 | 40 |     distribution_id: String,  | 
@@ -36,13 +66,13 @@ impl CloudFront {  | 
36 | 66 |     /// Invalidate a file on CloudFront  | 
37 | 67 |     ///  | 
38 | 68 |     /// `path` is the path to the file to invalidate, such as `config.json`, or `re/ge/regex`  | 
39 |  | -    pub async fn invalidate(&self, path: &str) -> anyhow::Result<()> {  | 
 | 69 | +    pub async fn invalidate(&self, path: &str) -> Result<(), CloudFrontError> {  | 
40 | 70 |         self.invalidate_many(vec![path.to_string()]).await  | 
41 | 71 |     }  | 
42 | 72 | 
 
  | 
43 | 73 |     /// Invalidate multiple paths on Cloudfront.  | 
44 | 74 |     #[instrument(skip(self))]  | 
45 |  | -    pub async fn invalidate_many(&self, mut paths: Vec<String>) -> anyhow::Result<()> {  | 
 | 75 | +    pub async fn invalidate_many(&self, mut paths: Vec<String>) -> Result<(), CloudFrontError> {  | 
46 | 76 |         let now = chrono::offset::Utc::now().timestamp_micros();  | 
47 | 77 | 
 
  | 
48 | 78 |         // We need to ensure that paths have a starting slash.  | 
@@ -72,15 +102,11 @@ impl CloudFront {  | 
72 | 102 | 
 
  | 
73 | 103 |         debug!("Sending invalidation request");  | 
74 | 104 | 
 
  | 
75 |  | -        match invalidation_request.send().await {  | 
76 |  | -            Ok(_) => {  | 
77 |  | -                debug!("Invalidation request successful");  | 
78 |  | -                Ok(())  | 
79 |  | -            }  | 
80 |  | -            Err(error) => {  | 
81 |  | -                warn!(?error, "Invalidation request failed");  | 
82 |  | -                Err(error.into())  | 
83 |  | -            }  | 
84 |  | -        }  | 
 | 105 | +        Ok(invalidation_request  | 
 | 106 | +            .send()  | 
 | 107 | +            .await  | 
 | 108 | +            .map(|_| ()) // We don't care about the result, just that it worked  | 
 | 109 | +            .inspect(|_| debug!("Invalidation request successful"))  | 
 | 110 | +            .inspect_err(|error| warn!(?error, "Invalidation request failed"))?)  | 
85 | 111 |     }  | 
86 | 112 | }  | 
0 commit comments