From ec87bdbd2a8a5a3f1f3e5679c9ec2f73411f82b5 Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Sat, 21 Sep 2024 18:02:46 -0400 Subject: [PATCH] [Feature] Put Recipe (#77) * Dependency updates * Update playwright base url default to sandbox * Add put recipe w/o validation * Reorganize playwright tests * Fix issue where body of put response was old * Add validation tests to playwright * Clipp and formatting * More formatting --- Cargo.lock | 204 +++++++------- package-lock.json | 249 +++++++++--------- package.json | 14 +- playwright.config.ts | 4 +- playwright/tests/recipeConstants.ts | 9 + playwright/tests/recipeErrors.spec.ts | 41 +++ .../{recipes.spec.ts => recipesPost.spec.ts} | 45 ++-- playwright/tests/recipesPut.spec.ts | 92 +++++++ src/bin/recipes/controller.rs | 3 +- src/lib/aws_client/mod.rs | 3 +- src/lib/lib.rs | 2 +- src/lib/recipe/mapper.rs | 14 +- src/lib/recipe/repository.rs | 13 +- src/lib/recipe/request_models.rs | 7 + src/lib/services/recipes.rs | 33 ++- 15 files changed, 465 insertions(+), 268 deletions(-) create mode 100644 playwright/tests/recipeConstants.ts create mode 100644 playwright/tests/recipeErrors.spec.ts rename playwright/tests/{recipes.spec.ts => recipesPost.spec.ts} (71%) create mode 100644 playwright/tests/recipesPut.spec.ts diff --git a/Cargo.lock b/Cargo.lock index de8d485..1c796c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "anstyle" @@ -47,9 +47,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.81" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", @@ -64,9 +64,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "aws-config" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e95816a168520d72c0e7680c405a5a8c1fb6a035b4bc4b9d7b0de8e1a941697" +checksum = "848d7b9b605720989929279fa644ce8f244d0ce3146fcca5b70e4eb7b3c020fc" dependencies = [ "aws-credential-types", "aws-runtime", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16838e6c9e12125face1c1eff1343c75e3ff540de98ff7ebd61874a89bcfeb9" +checksum = "60e8f6b615cb5fc60a98132268508ad104310f0cfb25a1c22eee76efdf9154da" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -106,14 +106,15 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.0" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f42c2d4218de4dcd890a109461e2f799a1a2ba3bcd2cde9af88360f5df9266c6" +checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" dependencies = [ "aws-credential-types", "aws-sigv4", "aws-smithy-async", "aws-smithy-http", + "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", "aws-types", @@ -130,9 +131,9 @@ dependencies = [ [[package]] name = "aws-sdk-dynamodb" -version = "1.42.0" +version = "1.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aadafd673822026e7ae6be7900c7886f609514b620874c9e3054f4ae38ab82f" +checksum = "8abc61e7374a01ecebcc3fd465655a2e1b83455f15b26716bfac05c35d25fa1b" dependencies = [ "aws-credential-types", "aws-runtime", @@ -153,9 +154,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.39.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11822090cf501c316c6f75711d77b96fba30658e3867a7762e5e2f5d32d31e81" +checksum = "70a9d27ed1c12b1140c47daf1bc541606c43fdafd918c4797d520db0043ceef2" dependencies = [ "aws-credential-types", "aws-runtime", @@ -175,9 +176,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.40.0" +version = "1.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a2a06ff89176123945d1bbe865603c4d7101bea216a550bb4d2e4e9ba74d74" +checksum = "44514a6ca967686cde1e2a1b81df6ef1883d0e3e570da8d8bc5c491dcb6fc29b" dependencies = [ "aws-credential-types", "aws-runtime", @@ -197,9 +198,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.39.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20a91795850826a6f456f4a48eff1dfa59a0e69bdbf5b8c50518fd372106574" +checksum = "cd7a4d279762a35b9df97209f6808b95d4fe78547fe2316b4d200a0283960c5a" dependencies = [ "aws-credential-types", "aws-runtime", @@ -220,9 +221,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.3" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df1b0fa6be58efe9d4ccc257df0a53b89cd8909e86591a13ca54817c87517be" +checksum = "cc8db6904450bafe7473c6ca9123f88cc11089e41a025408f992db4e22d3be68" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -254,9 +255,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.60.9" +version = "0.60.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9cd0ae3d97daa0a2bf377a4d8e8e1362cae590c4a1aad0d40058ebca18eb91e" +checksum = "5c8bc3e8fdc6b8d07d976e301c02fe553f72a39b7a9fea820e023268467d7ab6" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -293,9 +294,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.6.3" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abbf454960d0db2ad12684a1640120e7557294b0ff8e2f11236290a1b293225" +checksum = "d1ce695746394772e7000b39fe073095db6d45a862d0767dd5ad0ac0d7f8eb87" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -337,9 +338,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.2" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cee7cadb433c781d3299b916fbf620fea813bf38f49db282fb6858141a05cc8" +checksum = "03701449087215b5369c7ea17fef0dd5d24cb93439ec5af0c7615f58c3f22605" dependencies = [ "base64-simd", "bytes", @@ -363,9 +364,9 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.60.8" +version = "0.60.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d123fbc2a4adc3c301652ba8e149bf4bc1d1725affb9784eb20c953ace06bf55" +checksum = "ab0b0166827aa700d3dc519f72f8b3a91c35d0b8d042dc5d643a91e6f80648fc" dependencies = [ "xmlparser", ] @@ -402,9 +403,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" dependencies = [ "async-trait", "axum-core", @@ -428,7 +429,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", "tracing", @@ -436,9 +437,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" dependencies = [ "async-trait", "bytes", @@ -449,7 +450,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -457,17 +458,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -509,9 +510,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" dependencies = [ "serde", ] @@ -528,9 +529,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.15" +version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ "shlex", ] @@ -559,9 +560,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -762,9 +763,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" [[package]] name = "h2" @@ -952,9 +953,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", "futures-channel", @@ -965,7 +966,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -982,9 +983,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", @@ -1045,7 +1046,7 @@ dependencies = [ "serde_path_to_error", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tracing", ] @@ -1064,7 +1065,7 @@ dependencies = [ "hyper 1.4.1", "hyper-util", "tokio", - "tower", + "tower 0.4.13", "tower-service", ] @@ -1127,11 +1128,11 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] @@ -1143,7 +1144,7 @@ dependencies = [ "hermit-abi", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1199,9 +1200,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.3" +version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" dependencies = [ "memchr", ] @@ -1380,7 +1381,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1391,9 +1392,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -1455,11 +1456,11 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1487,9 +1488,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -1503,18 +1504,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -1534,9 +1535,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", "memchr", @@ -1623,7 +1624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1640,9 +1641,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.76" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -1724,9 +1725,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", @@ -1736,7 +1737,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1762,9 +1763,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite", @@ -1773,9 +1774,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", @@ -1800,6 +1801,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-http" version = "0.5.2" @@ -1892,15 +1909,15 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] @@ -1987,6 +2004,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/package-lock.json b/package-lock.json index 02fe113..ef6a19f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,10 @@ "name": "meal-planner", "version": "0.1.0", "dependencies": { - "aws-cdk-lib": "^2.154.1", + "aws-cdk-lib": "^2.159.1", "aws-cdk-local": "^2.18.0", "cargo-lambda-cdk": "^0.0.22", - "cdk-pipelines-github": "^0.4.124", + "cdk-pipelines-github": "^0.4.125", "constructs": "^10.3.0", "source-map-support": "^0.5.21" }, @@ -19,13 +19,13 @@ "meal-planner": "cdk/bin/meal-planner.ts" }, "devDependencies": { - "@playwright/test": "^1.46.1", - "@types/jest": "^29.5.12", - "@types/node": "^20.16.1", + "@playwright/test": "^1.47.2", + "@types/jest": "^29.5.13", + "@types/node": "^20.16.5", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", - "aws-cdk": "^2.154.1", - "eslint": "^8.57.0", + "aws-cdk": "^2.159.1", + "eslint": "^8.57.1", "eslint-config-google": "^0.14.0", "jest": "^29.7.0", "ts-jest": "^29.2.5", @@ -48,9 +48,9 @@ } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.202", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.202.tgz", - "integrity": "sha512-JqlF0D4+EVugnG5dAsNZMqhu3HW7ehOXm5SDMxMbXNDMdsF0pxtQKNHRl52z1U9igsHmaFpUgSGjbhAJ+0JONg==", + "version": "2.2.203", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.203.tgz", + "integrity": "sha512-7ZhjD0L62dhWL0yzoLCxvJTU3tLHTz/yEg6GKt3foSj+ljVR1KSP8MuAi+QPb4pT7ZTfVzELMtI36Y/ROuL3ig==", "license": "Apache-2.0" }, "node_modules/@aws-cdk/asset-kubectl-v20": { @@ -60,15 +60,15 @@ "license": "Apache-2.0" }, "node_modules/@aws-cdk/asset-node-proxy-agent-v6": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.0.3.tgz", - "integrity": "sha512-twhuEG+JPOYCYPx/xy5uH2+VUsIEhPTzDY0F1KuB+ocjWWB/KEDiOVL19nHvbPCB6fhWnkykXEMJ4HHcKvjtvg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.0.tgz", + "integrity": "sha512-7bY3J8GCVxLupn/kNmpPc5VJz8grx+4RKfnnJiO1LG+uxkZfANZG3RMHhE+qQxxwkyQ9/MfPtTpf748UhR425A==", "license": "Apache-2.0" }, "node_modules/@aws-cdk/cloud-assembly-schema": { - "version": "36.0.18", - "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-36.0.18.tgz", - "integrity": "sha512-PP6vgYVoydEh7JKjv/UQccUI2dGfEHlfX+hhI4e3arcT73xmiXP5ck9SrToqgPx4aio3tHTQpnDYeD7l3XV/gQ==", + "version": "36.3.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-36.3.0.tgz", + "integrity": "sha512-mLSYgcMFTNCXrGAD7xob95p9s47/7WwEWUJiexxM46H2GxiijhlhLQJs31AS5uRRP6Cx1DLEu4qayKAUOOVGrw==", "bundleDependencies": [ "jsonschema", "semver" @@ -167,13 +167,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", - "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -297,14 +297,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" @@ -405,13 +405,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", - "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.4" + "@babel/types": "^7.25.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -476,13 +476,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz", + "integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -675,17 +675,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", - "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.4", - "@babel/parser": "^7.25.4", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.4", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -704,9 +704,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", - "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dev": true, "license": "MIT", "dependencies": { @@ -766,9 +766,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "license": "MIT", "engines": { @@ -824,9 +824,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { @@ -834,14 +834,14 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -1396,13 +1396,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.1.tgz", - "integrity": "sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", + "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.46.1" + "playwright": "1.47.2" }, "bin": { "playwright": "cli.js" @@ -1549,9 +1549,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "version": "29.5.13", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", + "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", "dev": true, "license": "MIT", "dependencies": { @@ -1567,9 +1567,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.16.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz", - "integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==", + "version": "20.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", + "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", "dev": true, "license": "MIT", "dependencies": { @@ -1836,9 +1836,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "license": "MIT", "dependencies": { @@ -1966,9 +1966,9 @@ "license": "MIT" }, "node_modules/aws-cdk": { - "version": "2.154.1", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.154.1.tgz", - "integrity": "sha512-yJoLTo+fUHRLD4YQMt/QoOPgiT/daci4I5KcaDK8Cx2fWA0Z3h5U9+bWS3ah+8OeZ91fciNCwt6Yt/0p+cp2GQ==", + "version": "2.159.1", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.159.1.tgz", + "integrity": "sha512-bkJOxic/NpJYQCF3MQhfyJVlFtIzMJeVGZp9jZa7TczxJp79Q/TNKzVJYv6GFabNS1wglGPfWkFB/rIJlRhJkg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1982,9 +1982,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.154.1", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.154.1.tgz", - "integrity": "sha512-XV04/XyNKJ2yyMfYsiSmWx+rIKwTrcrd87p61t4xhE240Iy6Y6LxXVdvkNEOjjbeXVmOUQ7JBG9cW1BeeFiDgg==", + "version": "2.159.1", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.159.1.tgz", + "integrity": "sha512-zcOyAs3+DTu+CtLehdOgvyosZ7nbLZ+OfBE6uVNMshXm957oXJrLsu6hehLt81TDxfItWYNluFcXkwepZDm6Ng==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -2002,17 +2002,17 @@ "dependencies": { "@aws-cdk/asset-awscli-v1": "^2.2.202", "@aws-cdk/asset-kubectl-v20": "^2.1.2", - "@aws-cdk/asset-node-proxy-agent-v6": "^2.0.3", - "@aws-cdk/cloud-assembly-schema": "^36.0.5", + "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.0", + "@aws-cdk/cloud-assembly-schema": "^36.0.24", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", "fs-extra": "^11.2.0", - "ignore": "^5.3.1", + "ignore": "^5.3.2", "jsonschema": "^1.4.1", "mime-types": "^2.1.35", "minimatch": "^3.1.2", "punycode": "^2.3.1", - "semver": "^7.6.2", + "semver": "^7.6.3", "table": "^6.8.2", "yaml": "1.10.2" }, @@ -2029,14 +2029,14 @@ "license": "Apache-2.0" }, "node_modules/aws-cdk-lib/node_modules/ajv": { - "version": "8.16.0", + "version": "8.17.1", "inBundle": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -2126,6 +2126,11 @@ "inBundle": true, "license": "MIT" }, + "node_modules/aws-cdk-lib/node_modules/fast-uri": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT" + }, "node_modules/aws-cdk-lib/node_modules/fs-extra": { "version": "11.2.0", "inBundle": true, @@ -2145,7 +2150,7 @@ "license": "ISC" }, "node_modules/aws-cdk-lib/node_modules/ignore": { - "version": "5.3.1", + "version": "5.3.2", "inBundle": true, "license": "MIT", "engines": { @@ -2236,7 +2241,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.6.2", + "version": "7.6.3", "inBundle": true, "license": "ISC", "bin": { @@ -2309,14 +2314,6 @@ "node": ">= 10.0.0" } }, - "node_modules/aws-cdk-lib/node_modules/uri-js": { - "version": "4.4.1", - "inBundle": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/aws-cdk-lib/node_modules/yaml": { "version": "1.10.2", "inBundle": true, @@ -2574,9 +2571,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001653", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz", - "integrity": "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==", + "version": "1.0.30001662", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz", + "integrity": "sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==", "dev": true, "funding": [ { @@ -2707,9 +2704,9 @@ } }, "node_modules/cdk-pipelines-github": { - "version": "0.4.124", - "resolved": "https://registry.npmjs.org/cdk-pipelines-github/-/cdk-pipelines-github-0.4.124.tgz", - "integrity": "sha512-9sTjmGn6MODrVUoIaopnphs8sqm5ggEK0RuKQ5M9iuLxe6WGrfsSjLH7P1RXD+vfGbTXOJq82Gzk1IEGhk2evw==", + "version": "0.4.125", + "resolved": "https://registry.npmjs.org/cdk-pipelines-github/-/cdk-pipelines-github-0.4.125.tgz", + "integrity": "sha512-H2jakAFADDDdQmve2LElevUKL/58ds68gRk1ksTCk6vuFxpsQFnkXuIFG0gubH/XnF+o+6vyut0FHKV7uXyZ1A==", "bundleDependencies": [ "decamelize", "fast-json-patch", @@ -2719,7 +2716,7 @@ "dependencies": { "decamelize": "^5.0.1", "fast-json-patch": "^3.1.1", - "yaml": "^2.5.0" + "yaml": "^2.5.1" }, "engines": { "node": ">= 18.12.0" @@ -2746,7 +2743,7 @@ "license": "MIT" }, "node_modules/cdk-pipelines-github/node_modules/yaml": { - "version": "2.5.0", + "version": "2.5.1", "inBundle": true, "license": "ISC", "bin": { @@ -2800,9 +2797,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz", - "integrity": "sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", "dev": true, "license": "MIT" }, @@ -2927,13 +2924,13 @@ } }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -3048,9 +3045,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", + "version": "1.5.27", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz", + "integrity": "sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==", "dev": true, "license": "ISC" }, @@ -3085,9 +3082,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -3108,17 +3105,17 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -4886,9 +4883,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, @@ -5102,9 +5099,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true, "license": "ISC" }, @@ -5201,13 +5198,13 @@ } }, "node_modules/playwright": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz", - "integrity": "sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", + "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.46.1" + "playwright-core": "1.47.2" }, "bin": { "playwright": "cli.js" @@ -5220,9 +5217,9 @@ } }, "node_modules/playwright-core": { - "version": "1.46.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.1.tgz", - "integrity": "sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", + "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 41d5fe0..f1f278e 100644 --- a/package.json +++ b/package.json @@ -14,13 +14,13 @@ "watch": "tsc -w" }, "devDependencies": { - "@playwright/test": "^1.46.1", - "@types/jest": "^29.5.12", - "@types/node": "^20.16.1", + "@playwright/test": "^1.47.2", + "@types/jest": "^29.5.13", + "@types/node": "^20.16.5", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", - "aws-cdk": "^2.154.1", - "eslint": "^8.57.0", + "aws-cdk": "^2.159.1", + "eslint": "^8.57.1", "eslint-config-google": "^0.14.0", "jest": "^29.7.0", "ts-jest": "^29.2.5", @@ -28,10 +28,10 @@ "typescript": "~5.2.2" }, "dependencies": { - "aws-cdk-lib": "^2.154.1", + "aws-cdk-lib": "^2.159.1", "aws-cdk-local": "^2.18.0", "cargo-lambda-cdk": "^0.0.22", - "cdk-pipelines-github": "^0.4.124", + "cdk-pipelines-github": "^0.4.125", "constructs": "^10.3.0", "source-map-support": "^0.5.21" } diff --git a/playwright.config.ts b/playwright.config.ts index 0fd573b..8875dc5 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -5,6 +5,8 @@ import { defineConfig } from '@playwright/test'; * https://github.com/motdotla/dotenv */ // require('dotenv').config(); +const baseUrl = process.env.PLAYWRIGHT_BASE_URL ?? + 'https://api.sandbox.mealplanner.chittyinsights.dev'; /** * See https://playwright.dev/docs/test-configuration. @@ -25,7 +27,7 @@ export default defineConfig({ * See https://playwright.dev/docs/api/class-testoptions. */ use: { - baseURL: `${process.env.PLAYWRIGHT_BASE_URL}`, + baseURL: `${baseUrl}`, extraHTTPHeaders: { 'Accept': 'application/json', // 'Authorization': `token ${process.env.API_TOKEN}`, diff --git a/playwright/tests/recipeConstants.ts b/playwright/tests/recipeConstants.ts new file mode 100644 index 0000000..98ab6b2 --- /dev/null +++ b/playwright/tests/recipeConstants.ts @@ -0,0 +1,9 @@ +export const NIL_UUID = '00000000-0000-0000-0000-000000000000'; + +export const createData = { + name: 'Playwright Recipe', +}; + +export const updateData = { + name: 'Updated Playwright Recipe', +}; diff --git a/playwright/tests/recipeErrors.spec.ts b/playwright/tests/recipeErrors.spec.ts new file mode 100644 index 0000000..8ab8327 --- /dev/null +++ b/playwright/tests/recipeErrors.spec.ts @@ -0,0 +1,41 @@ +import { test, expect } from '@playwright/test'; +import { NIL_UUID, updateData } from './recipeConstants'; + +test('Create Invalid Recipe', async ({ request }) => { + const response = await request.put(`./recipes`, { + data: { + id: 'this-is-not-a-uuid', + name: '', + }, + }); + + expect(response.status()).toEqual(422); +}); + +test('Create Recipe w/ Null Name', async ({ request }) => { + const response = await request.put(`./recipes`, { + data: { + id: NIL_UUID, + }, + }); + + expect(response.status()).toEqual(422); +}); + +test('Read Non-existent Recipe', async ({ request }) => { + const response = await request.get(`./recipes/${NIL_UUID}`); + + expect(response.status()).toEqual(404); +}); + +test('Update Non-existent Recipe', async ({ request }) => { + const response = await request.patch(`./recipes/${NIL_UUID}`, { data: updateData }); + + expect(response.status()).toEqual(404); +}); + +test('Delete Non-existent Recipe', async ({ request }) => { + const response = await request.delete(`./recipes/${NIL_UUID}`); + + expect(response.status()).toEqual(404); +}); diff --git a/playwright/tests/recipes.spec.ts b/playwright/tests/recipesPost.spec.ts similarity index 71% rename from playwright/tests/recipes.spec.ts rename to playwright/tests/recipesPost.spec.ts index 8f1df4a..97b7614 100644 --- a/playwright/tests/recipes.spec.ts +++ b/playwright/tests/recipesPost.spec.ts @@ -1,14 +1,6 @@ import { test, expect } from '@playwright/test'; +import { createData, updateData } from './recipeConstants'; -const createData = { - name: 'Playwright Recipe', -}; - -const updateData = { - name: 'Updated Playwright Recipe', -}; - -const NIL_UUID = '00000000-0000-0000-0000-000000000000'; let recipeUuid: string; @@ -22,10 +14,10 @@ test('pingable', async ({ request }) => { test.describe('Happy Path', () => { test.describe.configure({ mode: 'serial' }); - test('Create Recipe', async ({ request }) => { + test('Post Recipe', async ({ request }) => { const response = await request.post('./recipes', { data: createData }); - expect(response.ok()).toBeTruthy(); + expect(response.status()).toBe(201); const responseBody = await response.json(); expect(responseBody).toEqual({ @@ -36,6 +28,19 @@ test.describe('Happy Path', () => { recipeUuid = responseBody.id; }); + test('Put Recipe', async ({ request }) => { + const response = await request.put('./recipes', { data: { id: recipeUuid, ...createData } }); + + expect(response.status()).toBe(200); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + id: recipeUuid, + ...createData, + }); + recipeUuid = responseBody.id; + }); + test('Read Recipe', async ({ request }) => { const response = await request.get(`./recipes/${recipeUuid}`); @@ -83,21 +88,3 @@ test.describe('Happy Path', () => { expect(response.status()).toEqual(204); }); }); - -test('Read Non-existent Recipe', async ({ request }) => { - const response = await request.get(`./recipes/${NIL_UUID}`); - - expect(response.status()).toEqual(404); -}); - -test('Update Non-existent Recipe', async ({ request }) => { - const response = await request.patch(`./recipes/${NIL_UUID}`, { data: updateData }); - - expect(response.status()).toEqual(404); -}); - -test('Delete Non-existent Recipe', async ({ request }) => { - const response = await request.delete(`./recipes/${NIL_UUID}`); - - expect(response.status()).toEqual(404); -}); diff --git a/playwright/tests/recipesPut.spec.ts b/playwright/tests/recipesPut.spec.ts new file mode 100644 index 0000000..db73e4b --- /dev/null +++ b/playwright/tests/recipesPut.spec.ts @@ -0,0 +1,92 @@ +import { test, expect } from '@playwright/test'; +import { createData, updateData } from './recipeConstants'; + +let recipeUuid: string = '01010101-0101-0101-0101-010101010101'; + +test('pingable', async ({ request }) => { + const response = await request.get('./recipes/ping'); + + expect(response.ok()).toBeTruthy(); + expect(await response.json()).toEqual({ msg: 'Pong' }); +}); + +test.describe('Happy Path', () => { + test.describe.configure({ mode: 'serial' }); + + test('Put Recipe to create', async ({ request }) => { + const response = await request.put('./recipes', { data: { id: recipeUuid, ...createData } }); + + expect(response.status()).toBe(201); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + id: recipeUuid, + ...createData, + }); + }); + + test('Read Recipe', async ({ request }) => { + const response = await request.get(`./recipes/${recipeUuid}`); + + expect(response.ok()).toBeTruthy(); + expect(await response.json()).toEqual({ + id: recipeUuid, + ...createData, + }); + }); + + test('List Recipes', async ({ request }) => { + const response = await request.get('./recipes'); + + expect(response.ok()).toBeTruthy(); + const json = await response.json(); + expect(json).toContainEqual({ + id: recipeUuid, + ...createData, + }); + }); + + test('Put Recipe update', async ({ request }) => { + const response = await request.put('./recipes', { + data: { + id: recipeUuid, + name: 'PUT Update', + }, + }); + + expect(response.status()).toBe(200); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + id: recipeUuid, + name: 'PUT Update', + }); + recipeUuid = responseBody.id; + }); + + test('Update Recipe', async ({ request }) => { + const response = await request.patch(`./recipes/${recipeUuid}`, { data: updateData }); + + expect(response.ok()).toBeTruthy(); + expect(await response.json()).toEqual({ + id: recipeUuid, + ...updateData, + }); + }); + + test('Read Updated Recipe', async ({ request }) => { + const response = await request.get(`./recipes/${recipeUuid}`); + + expect(response.ok()).toBeTruthy(); + expect(await response.json()).toEqual({ + id: recipeUuid, + ...updateData, + }); + }); + + test.afterAll('Delete Recipe', async ({ request }) => { + const response = await request.delete(`./recipes/${recipeUuid}`); + + expect(response.status()).toEqual(204); + }); +}); diff --git a/src/bin/recipes/controller.rs b/src/bin/recipes/controller.rs index eb00ea9..c6b3ae7 100644 --- a/src/bin/recipes/controller.rs +++ b/src/bin/recipes/controller.rs @@ -1,5 +1,5 @@ use aws_config::BehaviorVersion; -use axum::routing::{delete, get, patch, post}; +use axum::routing::{delete, get, patch, post, put}; use axum::Router; use meal_planner::recipe::repository::DynamoDbRecipe; use meal_planner::services::{recipes, ApplicationContext}; @@ -19,6 +19,7 @@ pub async fn recipes() -> Router { Router::new() .route("/", get(recipes::list::)) .route("/", post(recipes::create::)) + .route("/", put(recipes::write::)) .route("/:id", get(recipes::read_one::)) .route("/:id", patch(recipes::update::)) .route("/:id", delete(recipes::delete_one::)) diff --git a/src/lib/aws_client/mod.rs b/src/lib/aws_client/mod.rs index b4ffa8c..d28084f 100644 --- a/src/lib/aws_client/mod.rs +++ b/src/lib/aws_client/mod.rs @@ -6,7 +6,7 @@ use aws_sdk_dynamodb::operation::delete_item::{DeleteItemError, DeleteItemOutput use aws_sdk_dynamodb::operation::get_item::{GetItemError, GetItemOutput}; use aws_sdk_dynamodb::operation::put_item::{PutItemError, PutItemOutput}; use aws_sdk_dynamodb::operation::scan::{ScanError, ScanOutput}; -use aws_sdk_dynamodb::types::AttributeValue; +use aws_sdk_dynamodb::types::{AttributeValue, ReturnValue}; use aws_sdk_dynamodb::Client; use axum::async_trait; @@ -67,6 +67,7 @@ impl DynamoDbClient for DynamoDbClientImpl { .put_item() .table_name(table_name) .set_item(Some(item)) + .return_values(ReturnValue::AllOld) .send() .await .map_err(SdkError::into_service_error) diff --git a/src/lib/lib.rs b/src/lib/lib.rs index b2e249f..9fbfd98 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -13,6 +13,6 @@ pub mod services; pub trait Repository: Send + Sync { fn get_all(&self) -> impl Future, StatusCode>> + Send; fn find_by_id(&self, id: Uuid) -> impl Future> + Send; - fn save(&self, item: &T) -> impl Future> + Send; + fn save(&self, item: &T) -> impl Future, StatusCode>> + Send; fn delete_by_id(&self, id: Uuid) -> impl Future> + Send; } diff --git a/src/lib/recipe/mapper.rs b/src/lib/recipe/mapper.rs index 36f2e97..c595d47 100644 --- a/src/lib/recipe/mapper.rs +++ b/src/lib/recipe/mapper.rs @@ -1,16 +1,24 @@ use uuid::Uuid; -use super::request_models::{PatchRecipe, PostRecipe}; +use super::request_models::{PatchRecipe, PostRecipe, PutRecipe}; use super::Recipe; #[must_use] -pub fn to_recipe(id: Uuid, value: &PostRecipe) -> Recipe { +pub fn map_post_recipe(id: Uuid, value: &PostRecipe) -> Recipe { Recipe { id, name: value.name.clone(), } } +#[must_use] +pub fn map_put_recipe(value: &PutRecipe) -> Recipe { + Recipe { + id: value.id, + name: value.name.clone(), + } +} + pub fn update_recipe(recipe: &mut Recipe, value: &PatchRecipe) { if let Some(new_name) = &value.name { recipe.name = new_name.to_string(); @@ -34,7 +42,7 @@ mod test { name: NAME.to_owned(), }; - let recipe = mapper::to_recipe(Uuid::nil(), &create_request); + let recipe = mapper::map_post_recipe(Uuid::nil(), &create_request); assert_eq!( Recipe { diff --git a/src/lib/recipe/repository.rs b/src/lib/recipe/repository.rs index 7b9b4b8..42e2000 100644 --- a/src/lib/recipe/repository.rs +++ b/src/lib/recipe/repository.rs @@ -48,8 +48,7 @@ impl Repository for DynamoDbRecipe { let items: Vec = scan_result .items() .iter() - .map(|item| from_item(item.clone())) - .flatten() + .flat_map(|item| from_item(item.clone())) .collect(); Ok(items) @@ -74,14 +73,18 @@ impl Repository for DynamoDbRecipe { Ok(recipe) } - async fn save(&self, recipe: &Recipe) -> Result<(), StatusCode> { + async fn save(&self, recipe: &Recipe) -> Result, StatusCode> { let item = to_item(recipe).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - self.client + let output = self + .client .put_item(&self.table_name, item) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - Ok(()) + match output.attributes { + Some(item) => from_item(item).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR), + None => Ok(None), + } } async fn delete_by_id(&self, id: Uuid) -> Result<(), StatusCode> { diff --git a/src/lib/recipe/request_models.rs b/src/lib/recipe/request_models.rs index d1dfa4f..ce1045a 100644 --- a/src/lib/recipe/request_models.rs +++ b/src/lib/recipe/request_models.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Deserializer}; +use uuid::Uuid; #[derive(Debug, Deserialize, PartialEq)] pub struct PostRecipe { @@ -27,6 +28,12 @@ where Ok(option.filter(|s| !s.is_empty())) } +#[derive(Debug, Deserialize, PartialEq)] +pub struct PutRecipe { + pub(super) id: Uuid, + pub(super) name: String, +} + #[cfg(test)] mod test { use super::PostRecipe; diff --git a/src/lib/services/recipes.rs b/src/lib/services/recipes.rs index b67a2e4..aa11e71 100644 --- a/src/lib/services/recipes.rs +++ b/src/lib/services/recipes.rs @@ -3,7 +3,7 @@ use axum::http::StatusCode; use axum::Json; use uuid::Uuid; -use crate::recipe::request_models::{PatchRecipe, PostRecipe}; +use crate::recipe::request_models::{PatchRecipe, PostRecipe, PutRecipe}; use crate::recipe::{mapper, Recipe}; use crate::services::ApplicationContext; use crate::Repository; @@ -25,6 +25,29 @@ where Ok(Json(recipes)) } +/// Attempts to create a recipe in the database. +/// +/// # Errors +/// +/// This function converts the result of the database operation to a status code +/// wrapped in an error. +pub async fn write( + State(state): State>, + Json(payload): Json, +) -> Result<(StatusCode, Json), StatusCode> +where + T: Repository, +{ + let recipe = mapper::map_put_recipe(&payload); + + let save_result: Option = state.repo.save(&recipe).await?; + + match save_result { + Some(_) => Ok((StatusCode::OK, Json(recipe))), + None => Ok((StatusCode::CREATED, Json(recipe))), + } +} + /// Attempts to create a recipe in the database. /// /// # Errors @@ -34,14 +57,14 @@ where pub async fn create( State(state): State>, Json(payload): Json, -) -> Result, StatusCode> +) -> Result<(StatusCode, Json), StatusCode> where T: Repository, { - let recipe = mapper::to_recipe(Uuid::new_v4(), &payload); + let recipe = mapper::map_post_recipe(Uuid::new_v4(), &payload); state.repo.save(&recipe).await?; - Ok(Json(recipe)) + Ok((StatusCode::CREATED, Json(recipe))) } /// Attempts to find a recipe in the database given the uuid. @@ -121,7 +144,7 @@ mod test { mock_repo .expect_save() .with(function(|recipe: &Recipe| recipe.name.eq("Name"))) - .return_once(|_| Box::pin(async { Ok(()) })); + .return_once(|_| Box::pin(async { Ok(None) })); let state = State(ApplicationContext::> { repo: mock_repo }); let payload = Json(PostRecipe::default());