From c2e088c1cf6327bdb70482fbc8cd5aa87cb0d023 Mon Sep 17 00:00:00 2001 From: Tobias Klug Date: Wed, 28 Aug 2024 14:48:00 +0200 Subject: [PATCH] Implement feature to add strikes --- cli-client/src/clients/client.rs | 4 +- cli-client/src/clients/local_client.rs | 71 +- cli-client/src/clients/remote_client.rs | 35 +- cli-client/src/main.rs | 8 +- infrastructure/strikes/Cargo.lock | 921 +++++++++++++++++++++- infrastructure/strikes/Cargo.toml | 4 +- infrastructure/strikes/src/put_strikes.rs | 72 +- infrastructure/strikes/strikes.tf | 33 + 8 files changed, 1076 insertions(+), 72 deletions(-) diff --git a/cli-client/src/clients/client.rs b/cli-client/src/clients/client.rs index 3f82cc7..bed89eb 100644 --- a/cli-client/src/clients/client.rs +++ b/cli-client/src/clients/client.rs @@ -1,12 +1,10 @@ -use std::collections::HashMap; - use async_trait::async_trait; use crate::tarnished::Tarnished; #[async_trait] pub trait StrikeClient { - fn add_strike(&self, name: &str) -> HashMap; + async fn add_strike(&self, name: &str) -> Result; fn get_tarnished(&self) -> Vec; fn clear_strikes(&self); async fn check_health(&self) -> Result<(), String>; diff --git a/cli-client/src/clients/local_client.rs b/cli-client/src/clients/local_client.rs index ff7d992..c3c81ef 100644 --- a/cli-client/src/clients/local_client.rs +++ b/cli-client/src/clients/local_client.rs @@ -11,7 +11,7 @@ pub struct LocalClient { #[async_trait] impl StrikeClient for LocalClient { - fn add_strike(&self, name: &str) -> HashMap { + async fn add_strike(&self, name: &str) -> Result { let db_path = &self.db_path; if !db_path.exists() { std::fs::create_dir_all(db_path.parent().unwrap()).unwrap(); @@ -24,7 +24,7 @@ impl StrikeClient for LocalClient { std::fs::write(db_path, serde_json::to_string_pretty(&db).unwrap()).unwrap(); - db.clone() + Ok(*db.get(name).unwrap()) } fn get_tarnished(&self) -> Vec { @@ -54,22 +54,20 @@ impl StrikeClient for LocalClient { mod unit_tests { use super::*; - #[test] - fn it_should_add_a_strike_for_an_existing_name() -> Result<(), Box> { + #[tokio::test] + async fn it_should_some_strikes() -> Result<(), Box> { let file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?; let client = LocalClient { db_path: file.to_path_buf(), }; - client.add_strike("guenther"); - let db = client.add_strike("heinz"); + let _ = client.add_strike("guenther").await?; + let _ = client.add_strike("guenther").await?; + let strikes = client.add_strike("guenther").await?; assert_eq!( - db, - [("guenther".to_string(), 1), ("heinz".to_string(), 1)] - .iter() - .cloned() - .collect() + strikes, + 3, ); Ok(()) @@ -109,38 +107,52 @@ mod unit_tests { #[cfg(test)] mod integration_tests { - use crate::clients::local_client::{LocalClient, StrikeClient as _}; + use crate::{clients::local_client::{LocalClient, StrikeClient as _}, tarnished::Tarnished}; - #[test] - fn it_should_add_a_strike() -> Result<(), Box> { + #[tokio::test] + async fn it_should_add_a_strike() -> Result<(), Box> { let file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?; let client = LocalClient { db_path: file.to_path_buf(), }; - let db = client.add_strike("guenther"); - assert_eq!(db, [("guenther".to_string(), 1)].iter().cloned().collect()); + let _ = client.add_strike("guenther").await?; + let strikes = client.get_tarnished(); + assert_eq!(strikes, vec![ + Tarnished { + name: "guenther".to_string(), + strikes: 1 + } + ]); Ok(()) } - #[test] - fn it_should_add_a_strike_to_an_existing_db() -> Result<(), Box> { + #[tokio::test] + async fn it_should_add_a_strike_to_an_existing_db() -> Result<(), Box> { let file = assert_fs::NamedTempFile::new("./tests/fixtures/db.json")?; let client = LocalClient { db_path: file.to_path_buf(), }; - client.add_strike("guenther"); - client.add_strike("heinz"); - - let db = client.add_strike("guenther"); + + let _ = client.add_strike("guenther").await?; + let _ = client.add_strike("heinz").await?; + let _ = client.add_strike("guenther").await?; + + let strikes = client.get_tarnished(); assert_eq!( - db, - [("guenther".to_string(), 2), ("heinz".to_string(), 1)] - .iter() - .cloned() - .collect() + strikes, + vec![ + Tarnished { + name: "guenther".to_string(), + strikes: 2 + }, + Tarnished { + name: "heinz".to_string(), + strikes: 1 + } + ] ); Ok(()) @@ -152,8 +164,9 @@ mod integration_tests { let client = LocalClient { db_path: file.to_path_buf(), }; - client.add_strike("guenther"); - client.add_strike("heinz"); + + let _ = client.add_strike("guenther"); + let _ = client.add_strike("heinz"); client.clear_strikes(); diff --git a/cli-client/src/clients/remote_client.rs b/cli-client/src/clients/remote_client.rs index 6d6db56..138cfaf 100644 --- a/cli-client/src/clients/remote_client.rs +++ b/cli-client/src/clients/remote_client.rs @@ -1,6 +1,5 @@ use async_trait::async_trait; use reqwest; -use std::collections::HashMap; use super::client::StrikeClient; use crate::tarnished::Tarnished; @@ -10,10 +9,20 @@ pub struct RemoteClient { pub base_url: String, } +#[derive(serde::Deserialize)] +struct StrikeResponse { + strike_count: i8, +} + #[async_trait] impl StrikeClient for RemoteClient { - fn add_strike(&self, _name: &str) -> HashMap { - HashMap::new() + async fn add_strike(&self, username: &str) -> Result { + let client = HttpClient { + base_url: self.base_url.clone(), + api_key: self.api_key.clone(), + }; + + client.put_strike(username).await } fn get_tarnished(&self) -> Vec { @@ -56,4 +65,24 @@ impl HttpClient { err => Err(err.to_string()), } } + + async fn put_strike(&self, username: &str) -> Result { + let client = reqwest::Client::new(); + let response = client + .put(format!("{}/strikes/{}", &self.base_url, username)) + .header("x-api-key", &self.api_key) + .send() + .await + .expect("Failed to execute request"); + + match response.status() { + reqwest::StatusCode::OK => { + let body = response.text().await.expect("Failed to read response body"); + Ok(serde_json::from_str::(&body) + .expect("Failed to parse response") + .strike_count) + } + err => Err(err.to_string()), + } + } } diff --git a/cli-client/src/main.rs b/cli-client/src/main.rs index 0a322b3..ea6772f 100644 --- a/cli-client/src/main.rs +++ b/cli-client/src/main.rs @@ -13,10 +13,10 @@ async fn main() { let client = create_client(settings); match &args.clone().command.unwrap() { - Command::Strike { name } => { - client.add_strike(name); - println!("{} has been tarnished!", name); - } + Command::Strike { name } => match client.add_strike(name).await { + Ok(strikes) => println!("{} has now {} strikes!", name, strikes), + Err(err) => eprintln!("Failed to add strike: {}", err), + }, Command::Ls => { let tarnished = client.get_tarnished(); diff --git a/infrastructure/strikes/Cargo.lock b/infrastructure/strikes/Cargo.lock index 31b9c2a..964102f 100644 --- a/infrastructure/strikes/Cargo.lock +++ b/infrastructure/strikes/Cargo.lock @@ -54,16 +54,338 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "aws-config" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 0.2.12", + "ring", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16838e6c9e12125face1c1eff1343c75e3ff540de98ff7ebd61874a89bcfeb9" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-runtime" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f42c2d4218de4dcd890a109461e2f799a1a2ba3bcd2cde9af88360f5df9266c6" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-dynamodb" +version = "1.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aadafd673822026e7ae6be7900c7886f609514b620874c9e3054f4ae38ab82f" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11822090cf501c316c6f75711d77b96fba30658e3867a7762e5e2f5d32d31e81" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78a2a06ff89176123945d1bbe865603c4d7101bea216a550bb4d2e4e9ba74d74" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20a91795850826a6f456f4a48eff1dfa59a0e69bdbf5b8c50518fd372106574" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http 0.2.12", + "once_cell", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df1b0fa6be58efe9d4ccc257df0a53b89cd8909e86591a13ca54817c87517be" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http 0.2.12", + "http 1.1.0", + "once_cell", + "percent-encoding", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62220bc6e97f946ddd51b5f1361f78996e704677afc518a4ff66b7a72ea1378c" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-http" +version = "0.60.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9cd0ae3d97daa0a2bf377a4d8e8e1362cae590c4a1aad0d40058ebca18eb91e" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http-body 0.4.6", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683df9469ef09468dad3473d129960119a0d3593617542b7d52086c8486f2d6" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fbd61ceb3fe8a1cb7352e42689cec5335833cd9f94103a61e98f9bb61c64bb" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abbf454960d0db2ad12684a1640120e7557294b0ff8e2f11236290a1b293225" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "http-body 1.0.1", + "httparse", + "hyper 0.14.30", + "hyper-rustls", + "once_cell", + "pin-project-lite", + "pin-utils", + "rustls", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e086682a53d3aa241192aa110fa8dfce98f2f5ac2ead0de84d41582c7e8fdb96" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.1.0", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-types" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cee7cadb433c781d3299b916fbf620fea813bf38f49db282fb6858141a05cc8" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.1.0", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5221b91b3e441e6675310829fd8984801b772cb1546ef6c0e54dec9f1ac13fef" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "rustc_version", + "tracing", +] + [[package]] name = "aws_lambda_events" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7319a086b79c3ff026a33a61e80f04fd3885fbb73237981ea080d21944e1cb1c" dependencies = [ - "base64", + "base64 0.22.1", "bytes", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "http-serde", "query_map", "serde", @@ -85,12 +407,43 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bytes" version = "1.7.1" @@ -100,6 +453,16 @@ dependencies = [ "serde", ] +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "cc" version = "1.1.14" @@ -115,6 +478,67 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "encoding_rs" version = "0.8.34" @@ -124,6 +548,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "fnv" version = "1.0.7" @@ -228,18 +664,90 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -251,6 +759,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -258,7 +777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -269,8 +788,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -280,7 +799,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f056c8559e3757392c8d091e796416e4649d8e49e88b8d76df6c002f05027fd" dependencies = [ - "http", + "http 1.1.0", "serde", ] @@ -290,6 +809,36 @@ version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.4.1" @@ -299,8 +848,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", @@ -309,6 +858,22 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.30", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + [[package]] name = "hyper-util" version = "0.1.7" @@ -318,9 +883,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.4.1", "pin-project-lite", "socket2", "tokio", @@ -339,6 +904,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itoa" version = "1.0.11" @@ -352,15 +927,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67fe279be7f89f5f72c97c3a96f45c43db8edab1007320ecc6a5741273b4d6db" dependencies = [ "aws_lambda_events", - "base64", + "base64 0.22.1", "bytes", "encoding_rs", "futures", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.4.1", "lambda_runtime", "mime", "percent-encoding", @@ -379,14 +954,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed49669d6430292aead991e19bf13153135a884f916e68f32997c951af637ebe" dependencies = [ "async-stream", - "base64", + "base64 0.22.1", "bytes", "futures", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "http-body-util", "http-serde", - "hyper", + "hyper 1.4.1", "hyper-util", "lambda_runtime_api_client", "pin-project", @@ -409,10 +984,10 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.4.1", "hyper-util", "tokio", "tower", @@ -481,6 +1056,30 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.36.3" @@ -496,6 +1095,18 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -534,6 +1145,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.86" @@ -595,6 +1212,12 @@ dependencies = [ "regex-syntax 0.8.4", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -607,18 +1230,133 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" version = "1.0.209" @@ -673,6 +1411,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -688,6 +1437,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.9" @@ -713,14 +1471,29 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "strikes" version = "0.1.0" dependencies = [ + "aws-config", + "aws-sdk-dynamodb", "lambda_http", + "serde_json", "tokio", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.76" @@ -742,6 +1515,36 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -768,6 +1571,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys", @@ -784,6 +1588,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -795,6 +1609,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "tower" version = "0.4.13" @@ -890,6 +1717,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-bidi" version = "0.3.15" @@ -911,6 +1744,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.2" @@ -922,12 +1761,36 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "want" version = "0.3.1" @@ -1015,3 +1878,15 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/infrastructure/strikes/Cargo.toml b/infrastructure/strikes/Cargo.toml index e11a2a9..a26ab1d 100644 --- a/infrastructure/strikes/Cargo.toml +++ b/infrastructure/strikes/Cargo.toml @@ -8,6 +8,8 @@ path = "src/put_strikes.rs" name = "put_strikes" [dependencies] +aws-config = "1.5.5" +aws-sdk-dynamodb = "1.42.0" lambda_http = "0.13.0" tokio = { version = "1", features = ["macros"] } - +serde_json = "1.0" diff --git a/infrastructure/strikes/src/put_strikes.rs b/infrastructure/strikes/src/put_strikes.rs index 709b2d0..8a0c33d 100644 --- a/infrastructure/strikes/src/put_strikes.rs +++ b/infrastructure/strikes/src/put_strikes.rs @@ -1,25 +1,79 @@ +use std::collections::HashMap; + +use aws_config::BehaviorVersion; +use aws_sdk_dynamodb::{ + error::ProvideErrorMetadata, + types::{AttributeValue, ReturnValue}, + Client, +}; use lambda_http::{run, service_fn, tracing, Body, Error, Request, RequestExt, Response}; async fn function_handler(request: Request) -> Result, Error> { let params = request.path_parameters(); let user = params.first("user"); - tracing::info!("User: {:?}", user); + let config = aws_config::load_defaults(BehaviorVersion::latest()).await; + let client = Client::new(&config); match user { - Some(user) => Ok(Response::builder() - .status(200) - .header("Content-Type", "text/plain") - .body(Body::from(format!("Hello, {}!", user))) - .unwrap()), + Some(username) => { + let strike_count = increment_strikes(username, &client).await?; + Ok(Response::builder() + .status(200) + .body(Body::Text( + serde_json::json!({"strike_count": strike_count}).to_string(), + )) + .expect("Failed to render response")) + } None => Ok(Response::builder() .status(400) - .header("Content-Type", "text/plain") - .body(Body::from("Missing user parameter")) - .unwrap()), + .body(Body::Text("Missing user parameter".to_string())) + .expect("Failed to render response")), + } +} + +async fn increment_strikes(username: &str, client: &Client) -> Result { + let request = client + .update_item() + .table_name("Strikes") + .key("UserId", AttributeValue::S(username.to_string())) + .update_expression("set Strikes = Strikes + :value") + .expression_attribute_values(":value", AttributeValue::N("1".to_string())) + .return_values(ReturnValue::UpdatedNew) + .send() + .await + .map_err(|err| err.into_service_error()); + + match request { + Err(err) => match ProvideErrorMetadata::code(&err) { + Some("ValidationException") => add_user(username, &client).await, + _ => return Err(err.into()), + }, + Ok(response) => { + let strike_count = extract_strike_count(response.attributes().unwrap()); + Ok(strike_count) + } } } +fn extract_strike_count(map: &HashMap) -> u8 { + map.get("Strikes").unwrap().as_n().unwrap().parse().unwrap() +} + +async fn add_user(username: &str, client: &Client) -> Result { + tracing::info!("Adding user: {}", username); + + client + .put_item() + .table_name("Strikes") + .item("UserId", AttributeValue::S(username.to_string())) + .item("Strikes", AttributeValue::N("1".to_string())) + .send() + .await?; + + Ok(1) +} + #[tokio::main] async fn main() -> Result<(), Error> { tracing::init_default_subscriber(); diff --git a/infrastructure/strikes/strikes.tf b/infrastructure/strikes/strikes.tf index 170606f..15eb73f 100644 --- a/infrastructure/strikes/strikes.tf +++ b/infrastructure/strikes/strikes.tf @@ -3,6 +3,19 @@ locals { put_strikes_lambda_name = "put-strikes" } +resource "aws_dynamodb_table" "strikes-table" { + name = "Strikes" + billing_mode = "PROVISIONED" + read_capacity = 8 + write_capacity = 8 + hash_key = "UserId" + + attribute { + name = "UserId" + type = "S" + } +} + data "aws_iam_policy_document" "lambda_assume_role" { statement { effect = "Allow" @@ -16,6 +29,22 @@ data "aws_iam_policy_document" "lambda_assume_role" { } } +data "aws_iam_policy_document" "dynamo_write" { + statement { + effect = "Allow" + + actions = [ + "dynamodb:PutItem", + "dynamodb:GetItem", + "dynamodb:UpdateItem" + ] + + resources = [ + aws_dynamodb_table.strikes-table.arn + ] + } +} + resource "aws_iam_role_policy_attachment" "basic_execution_role_policy_attachment" { role = aws_iam_role.put_strikes_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" @@ -24,6 +53,10 @@ resource "aws_iam_role_policy_attachment" "basic_execution_role_policy_attachmen resource "aws_iam_role" "put_strikes_role" { name = "${local.app_name}-${local.put_strikes_lambda_name}" assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json + inline_policy { + name = "dynamo_write" + policy = data.aws_iam_policy_document.dynamo_write.json + } } data "archive_file" "put_strikes_lambda_archive" {