aws-secretsmanager-cache 0.3.0

A client for in-process caching of secrets from AWS Secrets manager
Documentation
# AWS Secrets Manager Rust Caching Client

![CI](https://github.com/adamjq/aws-secretsmanager-cache-rust/actions/workflows/ci.yml/badge.svg)

This crate provides a client for in-process caching of secrets from AWS Secrets Manager for Rust applications. 
It's heavily inspired by the [AWS Secrets Manager Go Caching Client](https://github.com/aws/aws-secretsmanager-caching-go) 
and the [AWS SDK for Rust](https://github.com/awslabs/aws-sdk-rust).

The client internally uses an LRU (least-recently used) caching scheme that provides 
O(1) insertions and O(1) lookups for cached values.

## Getting started

To use this client you must have:
- A Rust development environment
- An Amazon Web Services (AWS) account to access secrets stored in AWS Secrets Manager and use AWS SDK for Rust.

## Usage

The following sample demonstrates how to get started using the client:

```rust
use aws_sdk_secretsmanager::Client;
use aws_secretsmanager_cache::SecretCache;

#[tokio::main]
async fn main() {
    // instantiate an AWS SecretsManager client using the AWS Rust SDK
    let aws_config = aws_config::from_env().load().await;
    let client = Client::new(&aws_config);
    
    let mut cache = SecretCache::new(client);

    match cache.get_secret_string("YOUR_SECRET_ID".to_string()).send().await {
        Ok(secret_value) => {
            // use secret value
        }
        // e.g. ResourceNotFoundException: Secrets Manager can't find the specified secret.
        Err(e) => println!("ERROR: {}", e),
    }
}
```

### Forcing cache refreshes

If a secret has been rotated since the last value was fetched and cached, and hasn't expired in the cache, it's necessary to force a cache refresh for the value by calling AWS and updating the value.

This can be done with `force_refresh()`, for example:

```rust
    match cache
        .get_secret_string("YOUR_SECRET_ID".to_string())
        .force_refresh()
        .send()
        .await
```

## Cache Configuration

- `max_cache_size usize` The maximum number of secrets to maintain in the cache 
before evicting the least frequently accessed
- `cache_item_ttl u128` The number of nanoseconds a cached secret will be considered 
valid before the secret value requires a refresh. Refreshing happens synchronously.

```rust
use aws_sdk_secretsmanager::Client;
use aws_secretsmanager_cache::{CacheConfig, SecretCache};
use std::time;

#[tokio::main]
async fn main() {
    let aws_config = aws_config::from_env().load().await;
    let client = Client::new(&aws_config);

    // cache configuration with 30 second expiry time and maximum 1000 secrets
    let cache_config = CacheConfig::new()
        .cache_item_ttl(time::Duration::from_secs(30).as_nanos())
        .max_cache_size(1000);

    let mut cache = SecretCache::new_with_config(client, cache_config);
}
```

## Global Caching

Certain cloud environments like AWS Lambda encourage initializing clients in the global scope to avoid initialization for
each function invocation. This can be achieved using the `lazy_static` crate, for example: 

```rust
use async_once::AsyncOnce;
use aws_sdk_secretsmanager::Client;
use aws_secretsmanager_cache::SecretCache;
use lazy_static::lazy_static;
use std::sync::Mutex;

// store the cache in the global scope - useful for runtime environments like AWS Lambda
lazy_static! {
    static ref CACHE: AsyncOnce<Mutex<SecretCache>> = AsyncOnce::new(async {
        Mutex::new(SecretCache::new(Client::new(
            &aws_config::from_env().load().await,
        )))
    });
}

#[tokio::main]
async fn main() {
    // use cache
}
```

## Development

### Linting

The project uses [rustfmt](https://github.com/rust-lang/rustfmt) and [clippy](https://github.com/rust-lang/rust-clippy) for 
formatting and linting. Follow the instructions to install `rustfmt` and `clippy` and run:

```bash
cargo fmt
cargo clippy --fix
```

### Tests

Run unit tests locally with:
```bash
cargo test
```

## License

Licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) or the [MIT license](https://opensource.org/licenses/MIT), at your option. Files in the project may not be copied, modified, or distributed except according to those terms.