Skip to main content

fakecloud_timestream/
shared.rs

1//! Primitives shared across the Amazon Timestream handlers: ARN synthesis,
2//! id minting, timestamps, endpoint-host derivation, and the internal
3//! `database\u{1}table` key. Kept in one place so the write and query paths
4//! cannot diverge on wire format.
5
6use fakecloud_core::service::AwsRequest;
7
8/// Current time as awsJson1_0 epoch-seconds (a floating-point number).
9/// Timestream's `Date` / `Time` shapes carry no `@timestampFormat`, so
10/// awsJson1_0's default epoch-seconds applies.
11pub fn now_epoch() -> f64 {
12    chrono::Utc::now().timestamp_millis() as f64 / 1000.0
13}
14
15/// Amazon Timestream database ARN,
16/// `arn:aws:timestream:{region}:{account}:database/{name}`.
17pub fn database_arn(region: &str, account: &str, name: &str) -> String {
18    format!("arn:aws:timestream:{region}:{account}:database/{name}")
19}
20
21/// Amazon Timestream table ARN,
22/// `arn:aws:timestream:{region}:{account}:database/{db}/table/{name}`.
23pub fn table_arn(region: &str, account: &str, database: &str, table: &str) -> String {
24    format!("arn:aws:timestream:{region}:{account}:database/{database}/table/{table}")
25}
26
27/// Amazon Timestream scheduled-query ARN,
28/// `arn:aws:timestream:{region}:{account}:scheduled-query/{name}-{suffix}`.
29pub fn scheduled_query_arn(region: &str, account: &str, name: &str, suffix: &str) -> String {
30    format!("arn:aws:timestream:{region}:{account}:scheduled-query/{name}-{suffix}")
31}
32
33/// A fresh opaque id (query id, batch-load task id, ARN suffix). Clients treat
34/// these as opaque tokens they only echo back.
35pub fn new_id() -> String {
36    uuid::Uuid::new_v4().simple().to_string().to_uppercase()
37}
38
39/// Internal storage key for a table within a database: `database` + a unit
40/// separator + `table`. The separator can never appear in a Timestream
41/// `ResourceName`, so the key round-trips unambiguously.
42pub fn table_key(database: &str, table: &str) -> String {
43    format!("{database}\u{1}{table}")
44}
45
46/// The bare host (`host[:port]`, no scheme) that `DescribeEndpoints` should
47/// advertise so a client's follow-up calls come back to this fakecloud server.
48/// Derived from the request `Host` header (the address the client actually
49/// dialed); falls back to the canonical regional Timestream ingest host when no
50/// `Host` header is present.
51pub fn endpoint_address(req: &AwsRequest) -> String {
52    req.headers
53        .get("host")
54        .and_then(|v| v.to_str().ok())
55        .filter(|h| !h.is_empty())
56        .map(|h| h.to_string())
57        .unwrap_or_else(|| format!("ingest.timestream.{}.amazonaws.com", req.region))
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn database_arn_shape() {
66        assert_eq!(
67            database_arn("us-east-1", "000000000000", "metrics"),
68            "arn:aws:timestream:us-east-1:000000000000:database/metrics"
69        );
70    }
71
72    #[test]
73    fn table_arn_shape() {
74        assert_eq!(
75            table_arn("us-east-1", "000000000000", "metrics", "cpu"),
76            "arn:aws:timestream:us-east-1:000000000000:database/metrics/table/cpu"
77        );
78    }
79
80    #[test]
81    fn table_key_round_trips() {
82        let k = table_key("metrics", "cpu");
83        assert_eq!(k.split_once('\u{1}'), Some(("metrics", "cpu")));
84    }
85}