From ddcdf3afa6cd3fd0b7de1163b307265d36db3c6e Mon Sep 17 00:00:00 2001 From: Chris Holden Date: Tue, 17 Dec 2024 13:37:26 -0500 Subject: [PATCH] feat: granule links via event subscription (#44) --- README.md | 2 - alembic_migration/Pipfile | 2 +- alembic_migration/Pipfile.lock | 334 ++++++---- cdk/downloader_stack.py | 77 ++- cdk/integration_stack.py | 18 +- .../hls-s2-downloader-link-subscription.png | Bin 0 -> 244631 bytes integration_tests/conftest.py | 9 + .../test_link_push_subscription.py | 149 +++++ lambdas/downloader/Pipfile | 2 +- lambdas/downloader/Pipfile.lock | 340 +++++----- lambdas/link_fetcher/Pipfile | 11 +- lambdas/link_fetcher/Pipfile.lock | 599 +++++++++++++----- lambdas/link_fetcher/README.md | 67 +- lambdas/link_fetcher/app/__init__.py | 0 .../link_fetcher/{ => app}/allowed_tiles.txt | 0 lambdas/link_fetcher/app/common.py | 140 ++++ .../{handler.py => app/search_handler.py} | 137 +--- .../link_fetcher/app/subscription_endpoint.py | 215 +++++++ .../link_fetcher/app/subscription_handler.py | 15 + lambdas/link_fetcher/manage_subscription.py | 297 +++++++++ lambdas/link_fetcher/tests/conftest.py | 7 +- .../data/push-granule-created-s2-n1.json | 209 ++++++ lambdas/link_fetcher/tests/docker-compose.yml | 1 - lambdas/link_fetcher/tests/test_common.py | 103 +++ .../tests/test_link_fetcher_handler.py | 101 +-- .../tests/test_subscription_endpoint.py | 301 +++++++++ lambdas/requeuer/Pipfile | 2 +- lambdas/requeuer/Pipfile.lock | 386 ++++++----- setup.py | 3 +- 29 files changed, 2674 insertions(+), 853 deletions(-) create mode 100644 images/hls-s2-downloader-link-subscription.png create mode 100644 integration_tests/test_link_push_subscription.py create mode 100644 lambdas/link_fetcher/app/__init__.py rename lambdas/link_fetcher/{ => app}/allowed_tiles.txt (100%) create mode 100644 lambdas/link_fetcher/app/common.py rename lambdas/link_fetcher/{handler.py => app/search_handler.py} (68%) create mode 100644 lambdas/link_fetcher/app/subscription_endpoint.py create mode 100644 lambdas/link_fetcher/app/subscription_handler.py create mode 100644 lambdas/link_fetcher/manage_subscription.py create mode 100644 lambdas/link_fetcher/tests/data/push-granule-created-s2-n1.json create mode 100644 lambdas/link_fetcher/tests/test_common.py create mode 100644 lambdas/link_fetcher/tests/test_subscription_endpoint.py diff --git a/README.md b/README.md index f51be5b7..3462085d 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,6 @@ $ npm install # This installs any node packages that are within package.json (CD $ make install # This calls `pipenv install --dev` on the repo root and any of the directories that contain a Makefile with `install` ``` -_**Note** you might have an issue installing `psycopg2` - I found [this](https://github.com/pypa/pipenv/issues/3991#issuecomment-564645309) helpful_ - A file named `.env` is expected in the root of the repository, the expected values are: ```bash diff --git a/alembic_migration/Pipfile b/alembic_migration/Pipfile index d099c64a..b6595c2f 100644 --- a/alembic_migration/Pipfile +++ b/alembic_migration/Pipfile @@ -14,7 +14,7 @@ sqlalchemy = "==1.4.0" db = {editable = true, path = "./../layers/db"} pytest = "==7.4.3" moto = "==5.0.17" -psycopg2 = "==2.9.10" +psycopg2-binary = "==2.9.10" pytest-docker = "==2.0.1" assertpy = "==1.1" pytest-cov = "==4.1.0" diff --git a/alembic_migration/Pipfile.lock b/alembic_migration/Pipfile.lock index d078246b..27a4d95f 100644 --- a/alembic_migration/Pipfile.lock +++ b/alembic_migration/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3a0c02a88a70b1762cd50bc669a5323d2588c0ce3dce3fa0f5d212144e6efd0d" + "sha256": "3d0853932c6ea3664fd7c1a200b159d587be8d92b22ed4d3350c5410c62d2583" }, "pipfile-spec": 6, "requires": { @@ -36,11 +36,11 @@ }, "botocore": { "hashes": [ - "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a", - "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547" + "sha256:564c2478e50179e0b766e6a87e5e0cdd35e1bc37eb375c1cf15511f5dd13600d", + "sha256:a7b13bbd959bf2d6f38f681676aab408be01974c46802ab997617b51399239f7" ], "markers": "python_version >= '3.8'", - "version": "==1.35.47" + "version": "==1.35.81" }, "cfnresponse": { "hashes": [ @@ -147,11 +147,11 @@ }, "mako": { "hashes": [ - "sha256:9ec3a1583713479fae654f83ed9fa8c9a4c16b7bb0daba0e6bbebff50c0d983d", - "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a" + "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627", + "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8" ], "markers": "python_version >= '3.8'", - "version": "==1.3.6" + "version": "==1.3.8" }, "markupsafe": { "hashes": [ @@ -246,19 +246,19 @@ }, "s3transfer": { "hashes": [ - "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d", - "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.16.0" + "version": "==1.17.0" }, "sqlalchemy": { "hashes": [ @@ -328,11 +328,11 @@ }, "attrs": { "hashes": [ - "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", - "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" + "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", + "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308" ], - "markers": "python_version >= '3.7'", - "version": "==24.2.0" + "markers": "python_version >= '3.8'", + "version": "==24.3.0" }, "boto3": { "hashes": [ @@ -345,19 +345,19 @@ }, "botocore": { "hashes": [ - "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a", - "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547" + "sha256:564c2478e50179e0b766e6a87e5e0cdd35e1bc37eb375c1cf15511f5dd13600d", + "sha256:a7b13bbd959bf2d6f38f681676aab408be01974c46802ab997617b51399239f7" ], "markers": "python_version >= '3.8'", - "version": "==1.35.47" + "version": "==1.35.81" }, "certifi": { "hashes": [ - "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", - "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" + "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", + "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" ], "markers": "python_version >= '3.6'", - "version": "==2024.8.30" + "version": "==2024.12.14" }, "cffi": { "hashes": [ @@ -548,104 +548,104 @@ "toml" ], "hashes": [ - "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376", - "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", - "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111", - "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", - "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", - "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", - "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", - "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", - "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", - "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c", - "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", - "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", - "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", - "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0", - "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", - "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", - "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", - "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", - "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", - "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", - "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", - "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", - "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", - "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", - "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", - "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", - "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", - "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", - "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", - "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901", - "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", - "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", - "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", - "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", - "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", - "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", - "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", - "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", - "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3", - "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", - "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076", - "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", - "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", - "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", - "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", - "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", - "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", - "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09", - "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", - "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", - "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f", - "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", - "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", - "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", - "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", - "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", - "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", - "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", - "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", - "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", - "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", - "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858" + "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4", + "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c", + "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f", + "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b", + "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6", + "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae", + "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692", + "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4", + "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4", + "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717", + "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d", + "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198", + "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1", + "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3", + "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb", + "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d", + "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08", + "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf", + "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b", + "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710", + "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c", + "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae", + "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077", + "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00", + "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb", + "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664", + "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014", + "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9", + "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6", + "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e", + "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9", + "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa", + "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611", + "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b", + "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a", + "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8", + "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030", + "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678", + "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015", + "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902", + "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97", + "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845", + "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419", + "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464", + "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be", + "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9", + "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7", + "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be", + "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1", + "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba", + "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5", + "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073", + "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4", + "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a", + "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a", + "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3", + "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599", + "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0", + "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b", + "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec", + "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1", + "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3" ], "markers": "python_version >= '3.9'", - "version": "==7.6.4" + "version": "==7.6.9" }, "cryptography": { "hashes": [ - "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", - "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", - "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", - "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", - "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", - "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", - "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", - "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", - "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", - "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", - "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", - "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", - "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", - "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", - "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", - "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", - "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", - "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", - "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", - "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", - "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", - "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", - "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", - "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", - "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", - "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", - "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7" + "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", + "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", + "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", + "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", + "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", + "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", + "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", + "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", + "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", + "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", + "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", + "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", + "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", + "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", + "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", + "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", + "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", + "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", + "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", + "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", + "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", + "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", + "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", + "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", + "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", + "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", + "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" ], - "markers": "python_version >= '3.7'", - "version": "==43.0.3" + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.0" }, "db": { "editable": true, @@ -840,11 +840,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "pluggy": { "hashes": [ @@ -854,17 +854,75 @@ "markers": "python_version >= '3.8'", "version": "==1.5.0" }, - "psycopg2": { + "psycopg2-binary": { "hashes": [ - "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4", - "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11", - "sha256:47c4f9875125344f4c2b870e41b6aad585901318068acd01de93f3677a6522c2", - "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e", - "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716", - "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067", - "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442", - "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b", - "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a" + "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", + "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5", + "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", + "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", + "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", + "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", + "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", + "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", + "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", + "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", + "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", + "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", + "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", + "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", + "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", + "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", + "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", + "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", + "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", + "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", + "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", + "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", + "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", + "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", + "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", + "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", + "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", + "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", + "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44", + "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", + "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", + "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", + "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa", + "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", + "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", + "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", + "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", + "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", + "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", + "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", + "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", + "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", + "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", + "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3", + "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", + "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92", + "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", + "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", + "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8", + "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", + "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", + "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864", + "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", + "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", + "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", + "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", + "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", + "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", + "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", + "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4", + "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", + "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", + "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", + "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", + "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", + "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", + "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863" ], "index": "pypi", "markers": "python_version >= '3.8'", @@ -1015,19 +1073,19 @@ }, "s3transfer": { "hashes": [ - "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d", - "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.16.0" + "version": "==1.17.0" }, "sqlalchemy": { "hashes": [ @@ -1080,11 +1138,11 @@ }, "werkzeug": { "hashes": [ - "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c", - "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306" + "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", + "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746" ], - "markers": "python_version >= '3.8'", - "version": "==3.0.4" + "markers": "python_version >= '3.9'", + "version": "==3.1.3" }, "xmltodict": { "hashes": [ diff --git a/cdk/downloader_stack.py b/cdk/downloader_stack.py index 3d2b9abf..b2d699fa 100644 --- a/cdk/downloader_stack.py +++ b/cdk/downloader_stack.py @@ -6,16 +6,17 @@ Duration, RemovalPolicy, Stack, + aws_apigatewayv2, + aws_apigatewayv2_integrations, aws_cloudwatch, aws_ec2, aws_events, aws_events_targets, aws_iam, aws_lambda, - aws_s3, ) from aws_cdk import aws_lambda_python_alpha as aws_lambda_python -from aws_cdk import aws_logs, aws_rds, aws_secretsmanager, aws_sqs, aws_ssm +from aws_cdk import aws_logs, aws_rds, aws_s3, aws_secretsmanager, aws_sqs, aws_ssm from aws_cdk import aws_stepfunctions as sfn from aws_cdk import aws_stepfunctions_tasks as tasks from constructs import Construct @@ -255,7 +256,7 @@ def __init__( self, id=f"{identifier}-link-fetcher", entry="lambdas/link_fetcher", - index="handler.py", + index="app/search_handler.py", handler="handler", layers=[ db_layer, @@ -286,6 +287,58 @@ def __init__( threshold=1, ) + link_subscription = aws_lambda_python.PythonFunction( + self, + id=f"{identifier}-link-subscription", + entry="lambdas/link_fetcher", + index="app/subscription_handler.py", + handler="handler", + layers=[ + db_layer, + ], + memory_size=200, + timeout=Duration.minutes(15), + runtime=aws_lambda.Runtime.PYTHON_3_11, + environment=link_fetcher_environment_vars, + ) + + aws_logs.LogGroup( + self, + id=f"{identifier}-link-subscription-log-group", + log_group_name=f"/aws/lambda/{link_subscription.function_name}", + removal_policy=RemovalPolicy.DESTROY + if removal_policy_destroy + else RemovalPolicy.RETAIN, + retention=aws_logs.RetentionDays.ONE_DAY + if removal_policy_destroy + else aws_logs.RetentionDays.TWO_WEEKS, + ) + + aws_cloudwatch.Alarm( + self, + id=f"{identifier}-link-subscription-errors-alarm", + metric=link_fetcher.metric_errors(), + evaluation_periods=3, + threshold=1, + ) + + forwarder_api = aws_apigatewayv2.HttpApi( + self, + "EsaPushSubscriptionHandlerApi", + api_name="EsaPushSubscriptionHandlerApi", + default_integration=aws_apigatewayv2_integrations.HttpLambdaIntegration( + "EsaPushSubscriptionHandlerApi-Integration", + handler=link_subscription, + ), + ) + + aws_ssm.StringParameter( + self, + id=f"{identifier}-link-subscription-endpoint-url", + string_value=forwarder_api.url, + parameter_name=f"/hls-s2-downloader-serverless/{identifier}/link_subscription_endpoint_url", + ) + downloader_environment_vars = { "STAGE": identifier, "DB_CONNECTION_SECRET_ARN": downloader_rds_secret.secret_arn, @@ -338,7 +391,7 @@ def __init__( self, id=f"{identifier}-downloader-role-arn", string_value=self.downloader.role.role_arn, - parameter_name=(f"/integration_tests/{identifier}/downloader_role_arn"), + parameter_name=f"/integration_tests/{identifier}/downloader_role_arn", ) self.downloader.role.add_managed_policy(lambda_insights_policy) @@ -351,15 +404,9 @@ def __init__( downloader_bucket.grant_write(self.downloader) downloader_rds_secret.grant_read(link_fetcher) + downloader_rds_secret.grant_read(link_subscription) downloader_rds_secret.grant_read(self.downloader) - scihub_credentials = aws_secretsmanager.Secret.from_secret_name_v2( - self, - id=f"{identifier}-scihub-credentials", - secret_name=f"hls-s2-downloader-serverless/{identifier}/scihub-credentials", - ) - scihub_credentials.grant_read(self.downloader) - copernicus_credentials = aws_secretsmanager.Secret.from_secret_name_v2( self, id=f"{identifier}-copernicus-credentials", @@ -368,9 +415,17 @@ def __init__( copernicus_credentials.grant_read(self.downloader) copernicus_credentials.grant_read(self.token_rotator) + esa_subscription_credentials = aws_secretsmanager.Secret.from_secret_name_v2( + self, + id=f"{identifier}-esa-subscription-credentials", + secret_name=f"hls-s2-downloader-serverless/{identifier}/esa-subscription-credentials", + ) + esa_subscription_credentials.grant_read(link_subscription) + token_parameter.grant_read(self.downloader) to_download_queue.grant_send_messages(link_fetcher) + to_download_queue.grant_send_messages(link_subscription) to_download_queue.grant_consume_messages(self.downloader) # We must resort to using CfnEventSourceMapping to set the maximum concurrency diff --git a/cdk/integration_stack.py b/cdk/integration_stack.py index b918e883..6eba0af8 100644 --- a/cdk/integration_stack.py +++ b/cdk/integration_stack.py @@ -1,9 +1,9 @@ import json from typing import Optional -from aws_cdk import Duration, RemovalPolicy, Stack, aws_apigateway, aws_lambda +from aws_cdk import Duration, RemovalPolicy, Stack, aws_apigateway, aws_iam, aws_lambda from aws_cdk import aws_lambda_python_alpha as aws_lambda_python -from aws_cdk import aws_iam, aws_logs, aws_s3, aws_secretsmanager, aws_ssm +from aws_cdk import aws_logs, aws_s3, aws_secretsmanager, aws_ssm from constructs import Construct @@ -27,16 +27,16 @@ def __init__( ) ) - # TODO remove this, along with other references to it, but leaving for - # now, just in case removing it would break the downloader lambda aws_secretsmanager.Secret( self, - id=f"{identifier}-integration-scihub-credentials", - secret_name=f"hls-s2-downloader-serverless/{identifier}/scihub-credentials", - description="Dummy values for the Mock SciHub API credentials", + id=f"{identifier}-integration-esa-subscription-credentials", + secret_name=f"hls-s2-downloader-serverless/{identifier}/esa-subscription-credentials", + description="Dummy values for the ESA 'push' subscription authentication", generate_secret_string=aws_secretsmanager.SecretStringGenerator( - secret_string_template=json.dumps({"username": "test-user"}), - generate_string_key="password", + secret_string_template=json.dumps( + {"notification_username": "test-user"} + ), + generate_string_key="notification_password", ), ) diff --git a/images/hls-s2-downloader-link-subscription.png b/images/hls-s2-downloader-link-subscription.png new file mode 100644 index 0000000000000000000000000000000000000000..e46b709e640edec289a70752ddc56750addd29d4 GIT binary patch literal 244631 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

`bJ7s`yXg;Zj>%fiC_+{z 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 Iterable[Ses session.commit() +@pytest.fixture +def link_subscription_endpoint_url(ssm_client: SSMClient, identifier: str): + qname = f"/hls-s2-downloader-serverless/{identifier}/link_subscription_endpoint_url" + result = ssm_client.get_parameter(Name=qname) + value = result["Parameter"].get("Value") + assert value is not None, f"No such SSM parameter: {qname}" + return value + + @pytest.fixture def step_function_arn(ssm_parameter: Callable[[str], str]): return ssm_parameter("link_fetcher_step_function_arn") diff --git a/integration_tests/test_link_push_subscription.py b/integration_tests/test_link_push_subscription.py new file mode 100644 index 00000000..b9adbf1e --- /dev/null +++ b/integration_tests/test_link_push_subscription.py @@ -0,0 +1,149 @@ +import datetime as dt +import json +from pathlib import Path +from typing import Callable +from uuid import uuid4 + +import boto3 +import polling2 +import pytest +import requests +from db.models.granule import Granule +from mypy_boto3_sqs import SQSClient +from sqlalchemy.orm import Session + + +def check_sqs_message_count(sqs_client, queue_url, count): + queue_attributes = sqs_client.get_queue_attributes( + QueueUrl=queue_url, AttributeNames=["ApproximateNumberOfMessages"] + ) + return int(queue_attributes["Attributes"]["ApproximateNumberOfMessages"]) == count + + +def _format_dt(datetime: dt.datetime) -> str: + """Format datetime into string used by ESA's payload""" + return datetime.isoformat().replace("+00:00", "Z") + + +@pytest.fixture +def recent_event_s2_created() -> dict: + """Create a recent Sentinel-2 "Created" event from ESA's push subscription + + This message contains two types of fields, + * Message metadata (event type, subscription ID, ack ID, notification date, etc) + * Message "body" - `(.value)` + """ + # Reusing example from ESA as a template + data = ( + Path(__file__).parents[1] + / "lambdas" + / "link_fetcher" + / "tests" + / "data" + / "push-granule-created-s2-n1.json" + ) + payload = json.loads(data.read_text()) + + # Update relevant parts of message payload to be "recent" + # where recent is <30 days from today as we're not currently + # reprocessing historical scenes that ESA has reprocessed + now = dt.datetime.now(tz=dt.timezone.utc) + + payload["NotificationDate"] = _format_dt(now) + payload["value"]["OriginDate"] = _format_dt(now - dt.timedelta(seconds=7)) + payload["value"]["PublicationDate"] = _format_dt(now - dt.timedelta(seconds=37)) + payload["value"]["ModificationDate"] = _format_dt(now - dt.timedelta(seconds=1)) + payload["value"]["ContentDate"] = { + "Start": _format_dt(now - dt.timedelta(hours=3, seconds=3)), + "End": _format_dt(now - dt.timedelta(hours=3)), + } + # We're not using fields in `payload["value"]["Attributes"]` but there's duplicate + # datetime information in there following OData conventions + + # Randomize ID of message to ensure each fixture's return is unique according + # to our DB (which uses granule ID as primary key) + payload["value"]["Id"] = str(uuid4()) + + return payload + + +@pytest.fixture +def link_subscription_credentials( + identifier: str, ssm_parameter: Callable[[str], str] +) -> tuple[str, str]: + """Return user/pass credentials for subscription endpoint""" + secrets_manager_client = boto3.client("secretsmanager") + secret = json.loads( + secrets_manager_client.get_secret_value( + SecretId=( + f"hls-s2-downloader-serverless/{identifier}/esa-subscription-credentials" + ) + )["SecretString"] + ) + + return ( + secret["notification_username"], + secret["notification_password"], + ) + + +@pytest.mark.parametrize("notification_count", [1, 2]) +def test_link_push_subscription_handles_event( + recent_event_s2_created: dict, + link_subscription_endpoint_url: str, + link_subscription_credentials: tuple[str, str], + db_session: Session, + sqs_client: SQSClient, + queue_url: str, + notification_count: int, +): + """Test that we handle a new granule created notification + + We have occasionally observed duplicate granule IDs being + sent to our API endpoint and we want to only process one, + so this test includes a parametrized "notification_count" + to replicate this reality. + """ + for _ in range(notification_count): + resp = requests.post( + f"{link_subscription_endpoint_url}events", + auth=link_subscription_credentials, + json=recent_event_s2_created, + ) + + # ensure correct response (204) + assert resp.status_code == 204 + + # ensure we have SQS message + polling2.poll( + check_sqs_message_count, + args=(sqs_client, queue_url, 1), + step=5, + timeout=120, + ) + + # ensure we have 1 granule for this ID + granules = ( + db_session.query(Granule).filter( + Granule.id == recent_event_s2_created["value"]["Id"] + ) + ).all() + assert len(granules) == 1 + + +def test_link_push_subscription_user_auth_rejects_incorrect( + link_subscription_endpoint_url: str, +): + """Test that we reject incorrect authentication""" + url = f"{link_subscription_endpoint_url}events" + resp = requests.post( + url, + auth=( + "foo", + "bar", + ), + json={}, + ) + + # ensure correct response (401 Unauthorized) + assert resp.status_code == 401 diff --git a/lambdas/downloader/Pipfile b/lambdas/downloader/Pipfile index 6c5c864c..1c1696d3 100644 --- a/lambdas/downloader/Pipfile +++ b/lambdas/downloader/Pipfile @@ -15,7 +15,7 @@ db = {editable = true, path = "./../../layers/db"} pytest-docker = "==2.0.1" alembic = "==1.12.1" moto = "==5.0.17" -psycopg2 = "==2.9.10" +psycopg2-binary = "==2.9.10" assertpy = "==1.1" responses = "==0.23.1" freezegun = "==1.0.0" diff --git a/lambdas/downloader/Pipfile.lock b/lambdas/downloader/Pipfile.lock index 51d5a653..89c789e7 100644 --- a/lambdas/downloader/Pipfile.lock +++ b/lambdas/downloader/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e58efc825094313dd6dacac89c3e5f270f54cf3f9d550c871d75b84cd7f9c3ab" + "sha256": "efe1a983b300a24acee015f2cca32ffdc8d6f5f614ca5809476c3633077e7102" }, "pipfile-spec": 6, "requires": { @@ -38,19 +38,19 @@ }, "botocore": { "hashes": [ - "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a", - "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547" + "sha256:564c2478e50179e0b766e6a87e5e0cdd35e1bc37eb375c1cf15511f5dd13600d", + "sha256:a7b13bbd959bf2d6f38f681676aab408be01974c46802ab997617b51399239f7" ], "markers": "python_version >= '3.8'", - "version": "==1.35.47" + "version": "==1.35.81" }, "certifi": { "hashes": [ - "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", - "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" + "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", + "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" ], "markers": "python_version >= '3.6'", - "version": "==2024.8.30" + "version": "==2024.12.14" }, "charset-normalizer": { "hashes": [ @@ -284,19 +284,19 @@ }, "s3transfer": { "hashes": [ - "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d", - "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.16.0" + "version": "==1.17.0" }, "sqlalchemy": { "hashes": [ @@ -367,11 +367,11 @@ }, "attrs": { "hashes": [ - "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", - "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" + "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", + "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308" ], - "markers": "python_version >= '3.7'", - "version": "==24.2.0" + "markers": "python_version >= '3.8'", + "version": "==24.3.0" }, "boto3": { "hashes": [ @@ -384,19 +384,19 @@ }, "botocore": { "hashes": [ - "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a", - "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547" + "sha256:564c2478e50179e0b766e6a87e5e0cdd35e1bc37eb375c1cf15511f5dd13600d", + "sha256:a7b13bbd959bf2d6f38f681676aab408be01974c46802ab997617b51399239f7" ], "markers": "python_version >= '3.8'", - "version": "==1.35.47" + "version": "==1.35.81" }, "certifi": { "hashes": [ - "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", - "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" + "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", + "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" ], "markers": "python_version >= '3.6'", - "version": "==2024.8.30" + "version": "==2024.12.14" }, "cffi": { "hashes": [ @@ -587,104 +587,104 @@ "toml" ], "hashes": [ - "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376", - "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", - "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111", - "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", - "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", - "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", - "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", - "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", - "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", - "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c", - "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", - "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", - "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", - "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0", - "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", - "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", - "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", - "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", - "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", - "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", - "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", - "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", - "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", - "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", - "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", - "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", - "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", - "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", - "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", - "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901", - "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", - "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", - "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", - "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", - "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", - "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", - "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", - "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", - "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3", - "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", - "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076", - "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", - "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", - "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", - "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", - "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", - "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", - "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09", - "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", - "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", - "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f", - "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", - "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", - "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", - "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", - "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", - "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", - "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", - "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", - "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", - "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", - "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858" + "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4", + "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c", + "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f", + "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b", + "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6", + "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae", + "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692", + "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4", + "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4", + "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717", + "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d", + "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198", + "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1", + "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3", + "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb", + "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d", + "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08", + "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf", + "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b", + "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710", + "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c", + "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae", + "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077", + "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00", + "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb", + "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664", + "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014", + "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9", + "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6", + "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e", + "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9", + "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa", + "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611", + "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b", + "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a", + "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8", + "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030", + "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678", + "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015", + "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902", + "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97", + "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845", + "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419", + "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464", + "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be", + "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9", + "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7", + "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be", + "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1", + "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba", + "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5", + "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073", + "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4", + "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a", + "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a", + "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3", + "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599", + "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0", + "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b", + "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec", + "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1", + "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3" ], "markers": "python_version >= '3.9'", - "version": "==7.6.4" + "version": "==7.6.9" }, "cryptography": { "hashes": [ - "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", - "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", - "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", - "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", - "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", - "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", - "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", - "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", - "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", - "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", - "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", - "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", - "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", - "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", - "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", - "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", - "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", - "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", - "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", - "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", - "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", - "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", - "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", - "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", - "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", - "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", - "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7" + "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", + "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", + "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", + "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", + "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", + "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", + "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", + "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", + "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", + "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", + "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", + "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", + "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", + "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", + "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", + "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", + "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", + "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", + "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", + "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", + "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", + "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", + "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", + "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", + "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", + "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", + "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" ], - "markers": "python_version >= '3.7'", - "version": "==43.0.3" + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.0" }, "db": { "editable": true, @@ -812,11 +812,11 @@ }, "mako": { "hashes": [ - "sha256:9ec3a1583713479fae654f83ed9fa8c9a4c16b7bb0daba0e6bbebff50c0d983d", - "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a" + "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627", + "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8" ], "markers": "python_version >= '3.8'", - "version": "==1.3.6" + "version": "==1.3.8" }, "markupsafe": { "hashes": [ @@ -896,11 +896,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "pluggy": { "hashes": [ @@ -910,17 +910,75 @@ "markers": "python_version >= '3.8'", "version": "==1.5.0" }, - "psycopg2": { + "psycopg2-binary": { "hashes": [ - "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4", - "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11", - "sha256:47c4f9875125344f4c2b870e41b6aad585901318068acd01de93f3677a6522c2", - "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e", - "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716", - "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067", - "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442", - "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b", - "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a" + "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", + "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5", + "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", + "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", + "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", + "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", + "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", + "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", + "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", + "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", + "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", + "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", + "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", + "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", + "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", + "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", + "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", + "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", + "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", + "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", + "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", + "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", + "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", + "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", + "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", + "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", + "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", + "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", + "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44", + "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", + "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", + "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", + "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa", + "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", + "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", + "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", + "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", + "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", + "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", + "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", + "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", + "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", + "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", + "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3", + "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", + "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92", + "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", + "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", + "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8", + "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", + "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", + "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864", + "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", + "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", + "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", + "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", + "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", + "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", + "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", + "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4", + "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", + "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", + "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", + "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", + "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", + "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", + "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863" ], "index": "pypi", "markers": "python_version >= '3.8'", @@ -1073,19 +1131,19 @@ }, "s3transfer": { "hashes": [ - "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d", - "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.16.0" + "version": "==1.17.0" }, "sqlalchemy": { "hashes": [ @@ -1154,11 +1212,11 @@ }, "werkzeug": { "hashes": [ - "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c", - "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306" + "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", + "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746" ], - "markers": "python_version >= '3.8'", - "version": "==3.0.4" + "markers": "python_version >= '3.9'", + "version": "==3.1.3" }, "xmltodict": { "hashes": [ diff --git a/lambdas/link_fetcher/Pipfile b/lambdas/link_fetcher/Pipfile index 9bb9bd47..9067a593 100644 --- a/lambdas/link_fetcher/Pipfile +++ b/lambdas/link_fetcher/Pipfile @@ -9,9 +9,12 @@ humanfriendly = "==9.1" requests = "==2.31.0" iso8601 = "==2.0.0" sqlalchemy = "==1.4.0" +fastapi = "*" +starlette = "*" +mangum = "*" [dev-packages] -boto3-stubs = {version = "==1.17.10.0", extras = ["sqs"]} +boto3-stubs = {version = "==1.17.10.0", extras = ["sqs", "ssm"]} freezegun = "==1.0.0" assertpy = "==1.1" pytest = "==7.4.3" @@ -19,13 +22,17 @@ responses = "==0.23.1" alembic = "==1.12.1" moto = "==5.0.17" db = {editable = true, path = "./../../layers/db"} -psycopg2 = "==2.9.10" +psycopg2-binary = "==2.9.10" pytest-docker = "==2.0.1" pytest-cov = "==4.1.0" mypy = "==1.6.0" types-requests = "==2.31.0" types-humanfriendly = "*" ruff = "==0.7.1" +uvicorn = "*" +httpx = "*" +click = "*" +pytest-mock = "*" [requires] python_version = "3.11" diff --git a/lambdas/link_fetcher/Pipfile.lock b/lambdas/link_fetcher/Pipfile.lock index cbd0adf1..b439e0da 100644 --- a/lambdas/link_fetcher/Pipfile.lock +++ b/lambdas/link_fetcher/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "72bdce1436bedb770bbee36ae3ac0b7906c80706726f6a408592f7b945935dfc" + "sha256": "c21f2b40d849814997f95bad4cebdf047b5d1c0972408e08d3bd446f64b6c208" }, "pipfile-spec": 6, "requires": { @@ -16,6 +16,22 @@ ] }, "default": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, + "anyio": { + "hashes": [ + "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48", + "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352" + ], + "markers": "python_version >= '3.9'", + "version": "==4.7.0" + }, "boto3": { "hashes": [ "sha256:18416d07b41e6094101a44f8b881047dcec6b846dad0b9f83b9bbf2f0cd93d07", @@ -27,19 +43,19 @@ }, "botocore": { "hashes": [ - "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a", - "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547" + "sha256:78dd7bf8f49616d00073698d7bbaf5a115208fe730b7b7afae4456adddb3552e", + "sha256:e43b97d8cbf19d35ce3a177f144bd97cc370f0a67d0984c7d7cf105ac198748f" ], "markers": "python_version >= '3.8'", - "version": "==1.35.47" + "version": "==1.35.82" }, "certifi": { "hashes": [ - "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", - "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" + "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", + "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" ], "markers": "python_version >= '3.6'", - "version": "==2024.8.30" + "version": "==2024.12.14" }, "charset-normalizer": { "hashes": [ @@ -152,6 +168,15 @@ "markers": "python_full_version >= '3.7.0'", "version": "==3.4.0" }, + "fastapi": { + "hashes": [ + "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654", + "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==0.115.6" + }, "greenlet": { "hashes": [ "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", @@ -265,6 +290,129 @@ "markers": "python_version >= '3.7'", "version": "==1.0.1" }, + "mangum": { + "hashes": [ + "sha256:e388e7c491b7b67970f8234e46fd4a7b21ff87785848f418de08148f71cf0bd6", + "sha256:e500b35f495d5e68ac98bc97334896d6101523f2ee2c57ba6a61893b65266e59" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.19.0" + }, + "pydantic": { + "hashes": [ + "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d", + "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9" + ], + "markers": "python_version >= '3.8'", + "version": "==2.10.3" + }, + "pydantic-core": { + "hashes": [ + "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9", + "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b", + "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c", + "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529", + "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc", + "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854", + "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d", + "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278", + "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a", + "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c", + "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f", + "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27", + "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f", + "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac", + "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2", + "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97", + "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a", + "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919", + "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9", + "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4", + "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c", + "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131", + "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5", + "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd", + "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089", + "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107", + "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6", + "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60", + "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf", + "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5", + "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08", + "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05", + "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2", + "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e", + "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c", + "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17", + "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62", + "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23", + "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be", + "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067", + "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02", + "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f", + "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235", + "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840", + "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5", + "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807", + "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16", + "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c", + "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864", + "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e", + "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a", + "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35", + "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737", + "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a", + "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3", + "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52", + "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05", + "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31", + "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89", + "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de", + "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6", + "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36", + "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c", + "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154", + "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb", + "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e", + "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd", + "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3", + "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f", + "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78", + "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960", + "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618", + "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08", + "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4", + "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c", + "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c", + "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330", + "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8", + "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792", + "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025", + "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9", + "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f", + "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01", + "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337", + "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4", + "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f", + "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd", + "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51", + "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab", + "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc", + "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676", + "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381", + "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed", + "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb", + "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967", + "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073", + "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae", + "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c", + "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206", + "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b" + ], + "markers": "python_version >= '3.8'", + "version": "==2.27.1" + }, "python-dateutil": { "hashes": [ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", @@ -284,19 +432,27 @@ }, "s3transfer": { "hashes": [ - "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d", - "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.16.0" + "version": "==1.17.0" + }, + "sniffio": { + "hashes": [ + "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", + "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.1" }, "sqlalchemy": { "hashes": [ @@ -339,6 +495,23 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "version": "==1.4.0" }, + "starlette": { + "hashes": [ + "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835", + "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==0.41.3" + }, + "typing-extensions": { + "hashes": [ + "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", + "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" + ], + "markers": "python_version >= '3.8'", + "version": "==4.12.2" + }, "urllib3": { "hashes": [ "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", @@ -358,6 +531,14 @@ "markers": "python_version >= '3.7'", "version": "==1.12.1" }, + "anyio": { + "hashes": [ + "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48", + "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352" + ], + "markers": "python_version >= '3.9'", + "version": "==4.7.0" + }, "assertpy": { "hashes": [ "sha256:acc64329934ad71a3221de185517a43af33e373bb44dc05b5a9b174394ef4833" @@ -367,11 +548,11 @@ }, "attrs": { "hashes": [ - "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", - "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" + "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", + "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308" ], - "markers": "python_version >= '3.7'", - "version": "==24.2.0" + "markers": "python_version >= '3.8'", + "version": "==24.3.0" }, "boto3": { "hashes": [ @@ -384,7 +565,8 @@ }, "boto3-stubs": { "extras": [ - "sqs" + "sqs", + "ssm" ], "hashes": [ "sha256:9d686cbc2147cd323c3b1815d60f7a0b6e98f6f578a69256f252a81347394573", @@ -395,19 +577,19 @@ }, "botocore": { "hashes": [ - "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a", - "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547" + "sha256:78dd7bf8f49616d00073698d7bbaf5a115208fe730b7b7afae4456adddb3552e", + "sha256:e43b97d8cbf19d35ce3a177f144bd97cc370f0a67d0984c7d7cf105ac198748f" ], "markers": "python_version >= '3.8'", - "version": "==1.35.47" + "version": "==1.35.82" }, "certifi": { "hashes": [ - "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", - "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" + "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", + "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" ], "markers": "python_version >= '3.6'", - "version": "==2024.8.30" + "version": "==2024.12.14" }, "cffi": { "hashes": [ @@ -593,109 +775,118 @@ "markers": "python_full_version >= '3.7.0'", "version": "==3.4.0" }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, "coverage": { "extras": [ "toml" ], "hashes": [ - "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376", - "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", - "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111", - "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", - "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", - "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", - "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", - "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", - "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", - "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c", - "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", - "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", - "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", - "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0", - "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", - "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", - "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", - "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", - "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", - "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", - "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", - "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", - "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", - "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", - "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", - "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", - "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", - "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", - "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", - "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901", - "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", - "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", - "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", - "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", - "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", - "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", - "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", - "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", - "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3", - "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", - "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076", - "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", - "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", - "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", - "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", - "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", - "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", - "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09", - "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", - "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", - "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f", - "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", - "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", - "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", - "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", - "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", - "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", - "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", - "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", - "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", - "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", - "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858" + "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4", + "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c", + "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f", + "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b", + "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6", + "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae", + "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692", + "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4", + "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4", + "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717", + "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d", + "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198", + "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1", + "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3", + "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb", + "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d", + "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08", + "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf", + "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b", + "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710", + "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c", + "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae", + "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077", + "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00", + "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb", + "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664", + "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014", + "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9", + "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6", + "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e", + "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9", + "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa", + "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611", + "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b", + "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a", + "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8", + "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030", + "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678", + "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015", + "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902", + "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97", + "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845", + "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419", + "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464", + "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be", + "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9", + "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7", + "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be", + "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1", + "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba", + "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5", + "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073", + "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4", + "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a", + "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a", + "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3", + "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599", + "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0", + "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b", + "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec", + "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1", + "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3" ], "markers": "python_version >= '3.9'", - "version": "==7.6.4" + "version": "==7.6.9" }, "cryptography": { "hashes": [ - "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", - "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", - "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", - "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", - "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", - "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", - "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", - "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", - "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", - "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", - "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", - "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", - "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", - "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", - "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", - "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", - "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", - "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", - "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", - "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", - "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", - "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", - "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", - "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", - "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", - "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", - "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7" + "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", + "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", + "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", + "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", + "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", + "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", + "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", + "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", + "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", + "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", + "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", + "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", + "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", + "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", + "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", + "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", + "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", + "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", + "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", + "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", + "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", + "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", + "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", + "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", + "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", + "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", + "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" ], - "markers": "python_version >= '3.7'", - "version": "==43.0.3" + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.0" }, "db": { "editable": true, @@ -789,6 +980,31 @@ "markers": "python_version >= '3'", "version": "==3.1.1" }, + "h11": { + "hashes": [ + "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", + "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761" + ], + "markers": "python_version >= '3.7'", + "version": "==0.14.0" + }, + "httpcore": { + "hashes": [ + "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", + "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd" + ], + "markers": "python_version >= '3.8'", + "version": "==1.0.7" + }, + "httpx": { + "hashes": [ + "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", + "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==0.28.1" + }, "idna": { "hashes": [ "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", @@ -823,11 +1039,11 @@ }, "mako": { "hashes": [ - "sha256:9ec3a1583713479fae654f83ed9fa8c9a4c16b7bb0daba0e6bbebff50c0d983d", - "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a" + "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627", + "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8" ], "markers": "python_version >= '3.8'", - "version": "==1.3.6" + "version": "==1.3.8" }, "markupsafe": { "hashes": [ @@ -946,6 +1162,13 @@ ], "version": "==1.17.10.0" }, + "mypy-boto3-ssm": { + "hashes": [ + "sha256:2f160ba434f25eb7be696f6a342f829f8e6fe8b6dbcb93b844b913196af1e215", + "sha256:7a4338334cd68b0cb2c213ed2eaba586d8cbddc4c7eaf81cf9d496396f3d5fb6" + ], + "version": "==1.17.10.0" + }, "mypy-extensions": { "hashes": [ "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", @@ -956,11 +1179,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "pluggy": { "hashes": [ @@ -970,17 +1193,75 @@ "markers": "python_version >= '3.8'", "version": "==1.5.0" }, - "psycopg2": { + "psycopg2-binary": { "hashes": [ - "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4", - "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11", - "sha256:47c4f9875125344f4c2b870e41b6aad585901318068acd01de93f3677a6522c2", - "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e", - "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716", - "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067", - "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442", - "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b", - "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a" + "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", + "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5", + "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", + "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", + "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", + "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", + "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", + "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", + "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", + "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", + "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", + "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", + "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", + "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", + "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", + "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", + "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", + "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", + "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", + "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", + "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", + "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", + "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", + "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", + "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", + "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", + "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", + "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", + "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44", + "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", + "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", + "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", + "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa", + "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", + "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", + "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", + "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", + "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", + "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", + "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", + "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", + "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", + "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", + "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3", + "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", + "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92", + "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", + "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", + "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8", + "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", + "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", + "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864", + "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", + "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", + "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", + "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", + "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", + "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", + "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", + "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4", + "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", + "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", + "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", + "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", + "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", + "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", + "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863" ], "index": "pypi", "markers": "python_version >= '3.8'", @@ -1021,6 +1302,15 @@ "markers": "python_version >= '3.6'", "version": "==2.0.1" }, + "pytest-mock": { + "hashes": [ + "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", + "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==3.14.0" + }, "python-dateutil": { "hashes": [ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", @@ -1133,19 +1423,27 @@ }, "s3transfer": { "hashes": [ - "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d", - "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.16.0" + "version": "==1.17.0" + }, + "sniffio": { + "hashes": [ + "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", + "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" + ], + "markers": "python_version >= '3.7'", + "version": "==1.3.1" }, "sqlalchemy": { "hashes": [ @@ -1190,12 +1488,12 @@ }, "types-humanfriendly": { "hashes": [ - "sha256:c0ca654b16ff36ed4c059152085a76c9909272f5ea6ac03cdb7e59b5dcd392c7", - "sha256:c87b2eb8ec2ab1ae51cb3cc2c0efee31622a6711b2a7627d8109e22b4cfd4366" + "sha256:9fd55a80481f3def1a1fa1f057e765f578bc3dde2d9c7034ccf4c6ca9177e8c1", + "sha256:c2cab19b77c7a81e4005a3512c7f352310406d6929f300487c423e4df5a3ec3d" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==10.0.1.11" + "markers": "python_version >= '3.8'", + "version": "==10.0.1.20241105" }, "types-pyyaml": { "hashes": [ @@ -1236,13 +1534,22 @@ "markers": "python_version >= '3.8'", "version": "==2.2.3" }, + "uvicorn": { + "hashes": [ + "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", + "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==0.34.0" + }, "werkzeug": { "hashes": [ - "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c", - "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306" + "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", + "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746" ], - "markers": "python_version >= '3.8'", - "version": "==3.0.4" + "markers": "python_version >= '3.9'", + "version": "==3.1.3" }, "xmltodict": { "hashes": [ diff --git a/lambdas/link_fetcher/README.md b/lambdas/link_fetcher/README.md index 5ec4a546..2994a969 100644 --- a/lambdas/link_fetcher/README.md +++ b/lambdas/link_fetcher/README.md @@ -1,14 +1,23 @@ # Link Fetcher 🔗 -## High level overview +As of April, 2024 the Copernicus Data Space Ecosystem supports two forms of gathering +links to downloadable Sentinel products, + +* Search API ("polling based") +* Subscriptions API ("event based") + +We wish to migrate from a polling to an event driven method of link +fetching. During the transition period, this subdirectory handles both +of these methods and deploys them as two separate Lambda functions. This +README describes both forms of link fetching. + +## High level overview (Polling) ![Link fetcher in S2 Downloader diagram](../../images/hls-s2-downloader-link-fetcher.png) The Link Fetchers purpose is to query Copernicus Data Space Ecosystem for new imagery links to download. It is invoked within the `Link Fetching` Step Function; every invocation is performed on one day in the form `YYYY-MM-DD`. Images to download are stored as records in the `granule` table, the `granule_count` table is also updated with available and fetched link counts. The `To Download` queue is also populated with the images IDs and download URLs. ---- - -## Handler breakdown +## Handler breakdown (Polling) Provided below is some pseudo-code to explain the process happening each time the lambda is invoked: @@ -36,9 +45,53 @@ while there_is_still_imagery_to_process: --- +## High level overview (Event Based) + +![Link Subscription Handler](../../images/hls-s2-downloader-link-subscription.png) + +The link subscription handler's purpose is to handle "push" events from Copernicus Data Space Ecosystem's +Subscriptions API for new imagery links to download. It uses API Gateway to provide a publicly accessible endpoint +that triggers the Lambda function. Images to download are stored as records in the `granule` table. +The `To Download` queue is also populated with the images IDs and download URLs. + +ESA has provided documentation and an example application for handling the "push" subscriptions, + +* https://documentation.dataspace.copernicus.eu/APIs/Subscriptions.html#push-subscriptions +* https://gitlab.cloudferro.com/cat_public/push_subscription_endpoint_example + +## Handler breakdown (Event Based) + +The "push" subscription endpoint handles "granule created" events that ESA sends to our endpoint. +Each subscription "push" event includes a payload describing one (1) new granule that ESA has +published and made available online to download. The following pseudo-code describes what +the subscription handler Lambda function does each time it is invoked, + + +```python +if not user_password_correct(): + raise Unauthorized() + +new_granule = parse_event_payload() + +# bail if newly published granule was acquired too long ago to consider +# (this helps us avoid newly reprocessed images acquired years ago) +if not granule_is_recently_acquired(new_granule): + return + +# bail if newly published granule is not for a tile ID we want to process +if not granule_is_for_desired_mgrs_tile(new_granule): + return + +# record in DB and send to download queue if we've not seen this +# granule ID before (i.e., exactly once processing) +add_results_to_db_and_sqs_queue(filtered_results) +``` + +--- + ## Development -This Lambda makes use of `pipenv` for managing depedencies and for building the function when deploying it. +This Lambda makes use of `pipenv` for managing dependencies and for building the function when deploying it. To get setup for developing this project, run: @@ -76,11 +129,11 @@ A `Makefile` is provided to abstract commonly used commands away: **`make lint`** -> This will perform a dry run of `flake8`, `isort`, and `black` and let you know what issues were found +> This will perform a dry run of `ruff` and let you know what issues were found **`make format`** -> This will peform a run of `isort` and `black`, this **will** modify files if issues were found +> This will perform a run of `ruff`, this **will** modify files if issues were found **`make test`** diff --git a/lambdas/link_fetcher/app/__init__.py b/lambdas/link_fetcher/app/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lambdas/link_fetcher/allowed_tiles.txt b/lambdas/link_fetcher/app/allowed_tiles.txt similarity index 100% rename from lambdas/link_fetcher/allowed_tiles.txt rename to lambdas/link_fetcher/app/allowed_tiles.txt diff --git a/lambdas/link_fetcher/app/common.py b/lambdas/link_fetcher/app/common.py new file mode 100644 index 00000000..f48dab64 --- /dev/null +++ b/lambdas/link_fetcher/app/common.py @@ -0,0 +1,140 @@ +import json +import os +import re +from dataclasses import dataclass +from datetime import datetime +from typing import ( + TYPE_CHECKING, + Callable, + Final, + Sequence, + Set, +) + +import boto3 +from db.models.granule import Granule +from sqlalchemy.exc import IntegrityError +from sqlalchemy.orm import Session +from typing_extensions import TypeAlias + +if TYPE_CHECKING: + from mypy_boto3_sqs.client import SQSClient + +SessionMaker: TypeAlias = Callable[[], Session] + +ACCEPTED_TILE_IDS_FILENAME: Final = "allowed_tiles.txt" + + +@dataclass(frozen=True) +class SearchResult: + image_id: str + filename: str + tileid: str + size: int + beginposition: datetime + endposition: datetime + ingestiondate: datetime + download_url: str + + +def parse_tile_id_from_title(title: str) -> str: + # The tile ID is encoded into the filename (title). It is embedded as + # `_TXXXXX_`, where `XXXXX` is the 5-character alphanumeric tile ID. + # https://sentinels.copernicus.eu/ca/web/sentinel/user-guides/sentinel-2-msi/naming-convention + match = re.search("_T(?P[0-9A-Z]{5})_", title) + tile_id = match["tile_id"] if match else "" + return tile_id + + +def get_accepted_tile_ids() -> Set[str]: + """ + Return MGRS square IDs acceptable for processing within the downloader. + + :returns: set of all acceptable MGRS square IDs + """ + accepted_tile_ids_filepath = os.path.join( + os.path.dirname(os.path.abspath(__file__)), ACCEPTED_TILE_IDS_FILENAME + ) + + with open(accepted_tile_ids_filepath) as tile_ids_in: + return {line.strip() for line in tile_ids_in} + + +def filter_search_results( + search_results: Sequence[SearchResult], + accepted_tile_ids: Set[str], +) -> Sequence[SearchResult]: + """ + Filters the given search results list and returns a list of results that tile ids + are within our accepted list of ids. + + :param search_results: List[SearchResult] representing the results of a query to + search + :param accepted_tile_ids: Set[str] representing acceptable MGRS tile ids + :returns: List[searchResult] representing a filtered version of the given results + """ + return tuple( + search_result + for search_result in search_results + if search_result.tileid in accepted_tile_ids + ) + + +def add_search_results_to_db_and_sqs( + session_maker: SessionMaker, search_results: Sequence[SearchResult] +): + """ + Creates a record in the `granule` table for each of the provided SearchResults and + a SQS Message in the `To Download` Queue. + If a record is already in the `granule` table, it will throw an exception which + when caught, will rollback the insertion and the SQS Message will not be added. + :param session_maker: sessionmaker representing the SQLAlchemy sessionmaker to use + for adding results + :param search_results: list of search results to add to the + `granule` table + """ + sqs_client = boto3.client("sqs") + to_download_queue_url = os.environ["TO_DOWNLOAD_SQS_QUEUE_URL"] + + with session_maker() as session: + for result in search_results: + try: + session.add( + Granule( + id=result.image_id, + filename=result.filename, + tileid=result.tileid, + size=result.size, + beginposition=result.beginposition, + endposition=result.endposition, + ingestiondate=result.ingestiondate, + download_url=result.download_url, + ) # type: ignore + ) + session.commit() + add_search_result_to_sqs(result, sqs_client, to_download_queue_url) + except IntegrityError: + print(f"{result.image_id} already in Database, not adding") + session.rollback() + + +def add_search_result_to_sqs( + search_result: SearchResult, sqs_client: "SQSClient", queue_url: str +): + """ + Creates a message in the provided SQS queue for the provided + SearchResult. The message is in the form {"id": , "download_url": } + :param search_result: search result to add to the SQS queue + :param sqs_client: SQSClient representing a boto3 SQS client + :param queue_url: str presenting the URL of the queue to send the message to + """ + sqs_client.send_message( + QueueUrl=queue_url, + MessageBody=json.dumps( + { + "id": search_result.image_id, + "filename": search_result.filename, + "download_url": search_result.download_url, + } + ), + ) diff --git a/lambdas/link_fetcher/handler.py b/lambdas/link_fetcher/app/search_handler.py similarity index 68% rename from lambdas/link_fetcher/handler.py rename to lambdas/link_fetcher/app/search_handler.py index b34cb0d4..73ebc955 100644 --- a/lambdas/link_fetcher/handler.py +++ b/lambdas/link_fetcher/app/search_handler.py @@ -1,40 +1,32 @@ -import json import os -import re -from dataclasses import dataclass from datetime import date, datetime, timedelta from typing import ( - TYPE_CHECKING, Any, - Callable, Final, Literal, Mapping, Protocol, Sequence, - Set, Tuple, TypedDict, ) -import boto3 import humanfriendly import iso8601 import requests -from db.models.granule import Granule from db.models.granule_count import GranuleCount from db.models.status import Status from db.session import get_session_maker -from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import Session -from typing_extensions import TypeAlias -if TYPE_CHECKING: - from mypy_boto3_sqs.client import SQSClient - -SessionMaker: TypeAlias = Callable[[], Session] +from app.common import ( + SearchResult, + SessionMaker, + add_search_results_to_db_and_sqs, + filter_search_results, + get_accepted_tile_ids, + parse_tile_id_from_title, +) -ACCEPTED_TILE_IDS_FILENAME: Final = "allowed_tiles.txt" MIN_REMAINING_MILLIS: Final = 60_000 SEARCH_URL: Final = os.environ.get( "SEARCH_URL", @@ -43,18 +35,6 @@ Platform = Literal["S2A", "S2B"] -@dataclass(frozen=True) -class SearchResult: - image_id: str - filename: str - tileid: str - size: int - beginposition: datetime - endposition: datetime - ingestiondate: datetime - download_url: str - - class Context(Protocol): def get_remaining_time_in_millis(self) -> int: ... @@ -114,66 +94,6 @@ def _handler( } -def add_search_results_to_db_and_sqs( - session_maker: SessionMaker, search_results: Sequence[SearchResult] -): - """ - Creates a record in the `granule` table for each of the provided SearchResults and - a SQS Message in the `To Download` Queue. - If a record is already in the `granule` table, it will throw an exception which - when caught, will rollback the insertion and the SQS Message will not be added. - :param session_maker: sessionmaker representing the SQLAlchemy sessionmaker to use - for adding results - :param search_results: list of search results to add to the - `granule` table - """ - sqs_client = boto3.client("sqs") - to_download_queue_url = os.environ["TO_DOWNLOAD_SQS_QUEUE_URL"] - - with session_maker() as session: - for result in search_results: - try: - session.add( - Granule( - id=result.image_id, - filename=result.filename, - tileid=result.tileid, - size=result.size, - beginposition=result.beginposition, - endposition=result.endposition, - ingestiondate=result.ingestiondate, - download_url=result.download_url, - ) # type: ignore - ) - session.commit() - add_search_result_to_sqs(result, sqs_client, to_download_queue_url) - except IntegrityError: - print(f"{result.image_id} already in Database, not adding") - session.rollback() - - -def add_search_result_to_sqs( - search_result: SearchResult, sqs_client: "SQSClient", queue_url: str -): - """ - Creates a message in the provided SQS queue for the provided - SearchResult. The message is in the form {"id": , "download_url": } - :param search_result: search result to add to the SQS queue - :param sqs_client: SQSClient representing a boto3 SQS client - :param queue_url: str presenting the URL of the queue to send the message to - """ - sqs_client.send_message( - QueueUrl=queue_url, - MessageBody=json.dumps( - { - "id": search_result.image_id, - "filename": search_result.filename, - "download_url": search_result.download_url, - } - ), - ) - - def get_fetched_links( session_maker: SessionMaker, day: date, platform: Platform ) -> int: @@ -278,40 +198,6 @@ def update_fetched_links( session.commit() -def get_accepted_tile_ids() -> Set[str]: - """ - Return MGRS square IDs acceptable for processing within the downloader. - - :returns: set of all acceptable MGRS square IDs - """ - accepted_tile_ids_filepath = os.path.join( - os.path.dirname(os.path.abspath(__file__)), ACCEPTED_TILE_IDS_FILENAME - ) - - with open(accepted_tile_ids_filepath) as tile_ids_in: - return {line.strip() for line in tile_ids_in} - - -def filter_search_results( - search_results: Sequence[SearchResult], - accepted_tile_ids: Set[str], -) -> Sequence[SearchResult]: - """ - Filters the given search results list and returns a list of results that tile ids - are within our accepted list of ids. - - :param search_results: List[SearchResult] representing the results of a query to - search - :param accepted_tile_ids: Set[str] representing acceptable MGRS tile ids - :returns: List[searchResult] representing a filtered version of the given results - """ - return tuple( - search_result - for search_result in search_results - if search_result.tileid in accepted_tile_ids - ) - - def get_query_parameters( start: int, day: date, platform: Platform ) -> Mapping[str, Any]: @@ -355,12 +241,7 @@ def create_search_result(search_item: Mapping[str, Any]) -> SearchResult: download = properties["services"]["download"] size = humanfriendly.parse_size(str(download["size"]), binary=True) title = properties["title"] - - # The tile ID is encoded into the filename (title). It is embedded as - # `_TXXXXX_`, where `XXXXX` is the 5-character alphanumeric tile ID. - # https://sentinels.copernicus.eu/ca/web/sentinel/user-guides/sentinel-2-msi/naming-convention - match = re.search("_T(?P[0-9A-Z]{5})_", title) - tile_id = match["tile_id"] if match else "" + tile_id = parse_tile_id_from_title(title) return SearchResult( image_id=search_item["id"], diff --git a/lambdas/link_fetcher/app/subscription_endpoint.py b/lambdas/link_fetcher/app/subscription_endpoint.py new file mode 100644 index 00000000..fd8e0c05 --- /dev/null +++ b/lambdas/link_fetcher/app/subscription_endpoint.py @@ -0,0 +1,215 @@ +import json +import logging +import os +import secrets +from dataclasses import asdict, dataclass, field +from datetime import datetime, timedelta, timezone +from typing import TYPE_CHECKING, Any, Callable +from urllib.parse import urljoin + +import boto3 +import iso8601 +from db.session import get_session_maker +from fastapi import Depends, FastAPI, HTTPException, Response +from fastapi.middleware.cors import CORSMiddleware +from fastapi.security import HTTPBasic, HTTPBasicCredentials +from starlette.requests import Request + +from app.common import ( + SearchResult, + SessionMaker, + add_search_results_to_db_and_sqs, + filter_search_results, + get_accepted_tile_ids, + parse_tile_id_from_title, +) + +if TYPE_CHECKING: + from mypy_boto3_ssm.client import SSMClient + +logger = logging.getLogger(__name__) + + +@dataclass +class EndpointConfig: + """Configuration settings for subscription 'push' endpoint""" + + stage: str = field(default_factory=lambda: os.getenv("STAGE")) + + # user auth + notification_username: str = field( + default_factory=lambda: os.getenv("NOTIFICATION_USERNAME") + ) + notification_password: str = field( + default_factory=lambda: os.getenv("NOTIFICATION_PASSWORD") + ) + + def __post_init__(self): + for attr, value in asdict(self).items(): + if value is None: + raise ValueError( + f"EndpointConfig attribute '{attr}' must be defined (got None)" + ) + + @classmethod + def load_from_secrets_manager(cls, stage: str) -> "EndpointConfig": + """Load from AWS Secret Manager for some `stage`""" + secret_id = f"hls-s2-downloader-serverless/{stage}/esa-subscription-credentials" + try: + secrets_manager_client = boto3.client("secretsmanager") + secret = json.loads( + secrets_manager_client.get_secret_value( + SecretId=secret_id, + )["SecretString"] + ) + except Exception as ex: + raise RuntimeError( + "Could not retrieve ESA subscription credentials from Secrets Manager" + ) from ex + + return cls( + stage=stage, + notification_username=secret["notification_username"], + notification_password=secret["notification_password"], + ) + + def get_endpoint_url( + self, + ssm_client: "SSMClient", + ) -> str: + """Return the endpoint URL stored in SSM parameter store""" + param_name = ( + f"/hls-s2-downloader-serverless/{self.stage}/link_subscription_endpoint_url" + ) + result = ssm_client.get_parameter(Name=param_name) + if (url := result["Parameter"].get("Value")) is None: + raise ValueError(f"No such SSM parameter {param_name}") + return urljoin(url, "events") + + +def parse_search_result( + payload: dict, +) -> SearchResult: + """Parse a subscription event payload to a SearchResult""" + # There should only be 1 link to "extracted" data file + extracted_links = [ + location + for location in payload["Locations"] + if location["FormatType"] == "Extracted" + ] + if len(extracted_links) != 1: + raise ValueError( + f"Got {len(extracted_links)} 'Extracted' links, expected just 1" + ) + + # The "extracted" data information looks like, + # * FormatType: "Extracted" + # * DownloadLink: str + # * ContentLength: int + # * Checksum: { "Value": str, "Algorithm": "MD5" | "BLAKE3", "ChecksumDate": datetime} + # * S3Path: str + extracted = extracted_links[0] + + search_result = SearchResult( + image_id=payload["Id"], + filename=payload["Name"], + tileid=parse_tile_id_from_title(payload["Name"]), + size=extracted["ContentLength"], + beginposition=iso8601.parse_date(payload["ContentDate"]["Start"]), + endposition=iso8601.parse_date(payload["ContentDate"]["End"]), + ingestiondate=iso8601.parse_date(payload["PublicationDate"]), + download_url=extracted["DownloadLink"], + ) + return search_result + + +def process_notification( + notification: dict[str, Any], + accepted_tile_ids: set[str], + session_maker: SessionMaker, + now_utc: Callable[[], datetime] = lambda: datetime.now(tz=timezone.utc), +): + """Parse, filter, and potentially add new granule results to download queue""" + # Parse subscription notification to SearchResult + search_result = parse_search_result(notification["value"]) + + # Only consider imagery acquired in the last 30 days to avoid reprocessing of older imagery + oldest_acquisition_date = now_utc() - timedelta(days=30) + if search_result.beginposition < oldest_acquisition_date: + logger.info(f"Rejected {search_result=} (acquisition date too old)") + return + + # Check tile ID + accepted_search_results = filter_search_results( + [search_result], + accepted_tile_ids, + ) + if accepted_search_results: + logger.info(f"Adding {search_result=} to granule download queue") + add_search_results_to_db_and_sqs( + session_maker, + accepted_search_results, + ) + else: + logger.info(f"Rejected {search_result=} (unacceptable tile)") + + +def build_app( + config: EndpointConfig, + now_utc: Callable[[], datetime] = lambda: datetime.now(tz=timezone.utc), +) -> FastAPI: + """Create FastAPI app""" + app = FastAPI() + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + security = HTTPBasic() + + accepted_tile_ids = get_accepted_tile_ids() + + @app.post("/events", status_code=204) + def post_notification( + request: Request, + notification: dict[str, Any], + credentials: HTTPBasicCredentials = Depends(security), + session_maker: SessionMaker = Depends(get_session_maker), + ) -> Response: + """ + Endpoint which uses Basic Auth and processes acquired notification. + """ + # check Basic authorization + if not ( + secrets.compare_digest( + credentials.username.encode("utf-8"), + config.notification_username.encode("utf-8"), + ) + and secrets.compare_digest( + credentials.password.encode("utf-8"), + config.notification_password.encode("utf-8"), + ) + ): + logging.error("Unauthorized") + raise HTTPException(status_code=401, detail="Unauthorized") + + process_notification( + notification=notification, + accepted_tile_ids=accepted_tile_ids, + session_maker=session_maker, + now_utc=now_utc, + ) + return Response(status_code=204) + + return app + + +if __name__ == "__main__": + # for local dev + import uvicorn + + config = EndpointConfig() + app = build_app(config) + uvicorn.run(app) diff --git a/lambdas/link_fetcher/app/subscription_handler.py b/lambdas/link_fetcher/app/subscription_handler.py new file mode 100644 index 00000000..99928933 --- /dev/null +++ b/lambdas/link_fetcher/app/subscription_handler.py @@ -0,0 +1,15 @@ +import logging +import os + +from mangum import Mangum + +from app.subscription_endpoint import ( + EndpointConfig, + build_app, +) + +logging.getLogger("app").setLevel(logging.INFO) + +config = EndpointConfig.load_from_secrets_manager(os.environ["STAGE"]) +app = build_app(config) +handler = Mangum(app) diff --git a/lambdas/link_fetcher/manage_subscription.py b/lambdas/link_fetcher/manage_subscription.py new file mode 100644 index 00000000..8f4c0c07 --- /dev/null +++ b/lambdas/link_fetcher/manage_subscription.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python +""" +This script file is copied with modification from the ESA "push" subscription +example code repository, "push_subscription_endpoint_example": +https://gitlab.cloudferro.com/cat_public/push_subscription_endpoint_example + +This script is originally MIT licensed by CloudFerro, + + Copyright (c) 2024 CloudFerro + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Modifications are largely replacing Pydantic with "dataclasses" from stdlib and +adding a CLI to run list/create/terminate subscriptions. +""" + +import datetime as dt +import json +import os +import urllib.parse +from dataclasses import dataclass +from typing import Literal + +import boto3 +import click +import requests + +from app.subscription_endpoint import EndpointConfig + +ACCESS_TOKEN_REQUEST_DATA: str = ( + "client_id={client_id}&" + "username={user_email}&" + "password={password}&" + "grant_type=password&" +) + +REFRESH_ACCESS_TOKEN_DATA: str = ( + "client_id={client_id}&" + "refresh_token={refresh_token}&" + "grant_type=refresh_token&" +) + + +@dataclass +class SubscriptionAPIConfig: + client_id: str = os.getenv("ESA_CDSE_CLIENT_ID", "cdse-public") + user_email: str = os.getenv("ESA_CDSE_USER_EMAIL") + user_password: str = os.getenv("ESA_CDSE_USER_PASSWORD") + + identity_token_api_url: str = os.getenv( + "ESA_CDSE_TOKEN_API_URL", + "https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token", + ) + subscriptions_api_base_url: str = os.getenv( + "ESA_CDSE_SUBSCRIPTION_API_BASE_URL", + "https://catalogue.dataspace.copernicus.eu/odata/v1/Subscriptions", + ) + + def __post_init__(self): + if self.user_email is None: + raise ValueError("Must set user_email") + if self.user_password is None: + raise ValueError("Must set user_password") + + +@dataclass +class Token: + """ESA token""" + + access_token: str + refresh_token: str + expires_at: dt.datetime + refresh_expires_at: dt.datetime + + @property + def is_expired(self) -> bool: + return dt.datetime.now() >= self.expires_at + + @property + def is_refreshable(self) -> bool: + return dt.datetime.now() >= self.refresh_expires_at + + +@dataclass +class TokenAPI: + config: SubscriptionAPIConfig + + def get_access_token(self) -> Token: + """ + Get access token which will be used in Subscriptions API. + """ + # create data for request to get access token + data = ACCESS_TOKEN_REQUEST_DATA.format( + client_id=urllib.parse.quote(self.config.client_id), + user_email=urllib.parse.quote(self.config.user_email), + password=urllib.parse.quote(self.config.user_password), + ) + + # in headers we provide information in which format we send data + headers = {"Content-Type": "application/x-www-form-urlencoded"} + + # make request for access_token + now = dt.datetime.now() + response = requests.post( + url=self.config.identity_token_api_url, + headers=headers, + data=data, + ) + response.raise_for_status() + + # acquire access token and refresh token for future use + response_json = json.loads(response.content.decode("utf-8")) + + return Token( + access_token=response_json["access_token"], + refresh_token=response_json["refresh_token"], + expires_at=now + dt.timedelta(seconds=response_json["expires_in"]), + refresh_expires_at=now + + dt.timedelta(seconds=response_json["refresh_expires_in"]), + ) + + def refresh_token(self, token: Token) -> Token: + """ + Refresh your access token. + """ + # create data for request to get refresh access token + data = REFRESH_ACCESS_TOKEN_DATA.format( + client_id=urllib.parse.quote(self.config.client_id), + refresh_token=urllib.parse.quote(token.refresh_token), + ) + + # in headers we provide information in which format we send data + headers = {"Content-Type": "application/x-www-form-urlencoded"} + + # make request for refreshing token + now = dt.datetime.now() + response = requests.post( + url=self.config.identity_token_api_url, + headers=headers, + data=data, + ) + response.raise_for_status() + + # acquire access token and refresh token for future use + response_json = json.loads(response.content.decode("utf-8")) + + return Token( + access_token=response_json["access_token"], + refresh_token=response_json["refresh_token"], + expires_at=now + dt.timedelta(seconds=response_json["expires_in"]), + refresh_expires_at=now + + dt.timedelta(seconds=response_json["refresh_expires_in"]), + ) + + +@dataclass +class SubscriptionAPI: + """Create, list, and delete subscriptions""" + + token_api: TokenAPI + endpoint_config: EndpointConfig + + def create_subscription(self) -> str: + """ + Create example subscription, returning subscription ID + """ + token = self.token_api.get_access_token() + endpoint_url = self.endpoint_config.get_endpoint_url( + ssm_client=boto3.client("ssm") + ) + subscription_data = { + "StageOrder": True, + "FilterParam": "Attributes/OData.CSC.StringAttribute/any(att:att/Name eq 'productType' and att/OData.CSC.StringAttribute/Value eq 'S2MSI1C')", + "Priority": 1, + "NotificationEndpoint": endpoint_url, + "NotificationEpUsername": self.endpoint_config.notification_username, + "NotificationEpPassword": self.endpoint_config.notification_password, + "Status": "running", + "SubscriptionEvent": ["created"], + } + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {token.access_token}", + } + response = requests.post( + url=self.token_api.config.subscriptions_api_base_url, + headers=headers, + json=subscription_data, + ) + response.raise_for_status() + subscription_information = response.json() + subscription_id = subscription_information["Id"] + print(f"Subscription created {subscription_id=}") + print("Below is full response:") + print(subscription_information) + return subscription_id + + def list_subscriptions(self) -> list[dict]: + """List subscriptions""" + token = self.token_api.get_access_token() + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {token.access_token}", + } + response = requests.get( + url=f"{self.token_api.config.subscriptions_api_base_url}/Info", + headers=headers, + ) + response.raise_for_status() + subscriptions = response.json() + return subscriptions + + def terminate_subscription(self, subscription_id: str): + """ + Terminate test subscription. + """ + token = self.token_api.get_access_token() + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {token.access_token}", + } + response = requests.delete( + url=f"{self.token_api.config.subscriptions_api_base_url}({subscription_id})", + headers=headers, + ) + response.raise_for_status() + print(f"Subscription terminated {subscription_id=}.") + + +@click.command() +@click.argument("command", type=click.Choice(["create", "list", "terminate"])) +@click.option( + "--email", + default=lambda: os.getenv("ESA_CDSE_EMAIL", ""), + help="CDSE user email for subscription", +) +@click.option( + "--password", + default=lambda: os.getenv("ESA_CDSE_PASSWORD", ""), + prompt=True, + help="CDSE user password for subscription", +) +def main( + command: Literal["create", "list", "terminate"], + email: str, + password: str, +): + """Manage ESA 'push' subscriptions""" + endpoint_cfg = EndpointConfig.load_from_secrets_manager(os.environ["STAGE"]) + subscription_cfg = SubscriptionAPIConfig( + user_email=email, + user_password=password, + ) + + token_api = TokenAPI(subscription_cfg) + + subscription_api = SubscriptionAPI(token_api, endpoint_cfg) + subscriptions = subscription_api.list_subscriptions() + + if command == "create": + if subscriptions: + click.echo("Cannot create a second subscription (only 1 active is allowed)") + raise click.Abort() + subscription = subscription_api.create_subscription() + click.echo(f"Created subscription id={subscription}") + + elif command == "list": + click.echo("Listing subscriptions:") + for subscription in subscriptions: + click.echo(subscription) + + elif command == "terminate": + subscription_id = subscriptions[0]["Id"] + click.echo("Terminating first listed subscription id={subscription_id}") + subscription_api.terminate_subscription(subscription_id) + + click.echo("Complete") + + +if __name__ == "__main__": + main() diff --git a/lambdas/link_fetcher/tests/conftest.py b/lambdas/link_fetcher/tests/conftest.py index 7428109e..a2feaad4 100644 --- a/lambdas/link_fetcher/tests/conftest.py +++ b/lambdas/link_fetcher/tests/conftest.py @@ -18,7 +18,7 @@ from sqlalchemy.exc import OperationalError from sqlalchemy.orm import Session -from handler import SEARCH_URL, SearchResult +from app.search_handler import SEARCH_URL, SearchResult UNIT_TEST_DIR = pathlib.Path(__file__).parent @@ -30,7 +30,7 @@ def mock_search_response(): @pytest.fixture def accepted_tile_ids() -> Set[str]: - with open(UNIT_TEST_DIR.parent / "allowed_tiles.txt") as lines: + with open(UNIT_TEST_DIR.parent / "app" / "allowed_tiles.txt") as lines: return set(map(str.strip, lines)) @@ -131,7 +131,8 @@ def sqs_client(): @pytest.fixture def mock_sqs_queue(request, sqs_resource, monkeysession, sqs_client): - queue = sqs_resource.create_queue(QueueName=f"mock-queue-{request.node.name}"[:80]) + request_name = hash(request.node.name) + queue = sqs_resource.create_queue(QueueName=f"mock-queue-{request_name}"[:80]) monkeysession.setenv("TO_DOWNLOAD_SQS_QUEUE_URL", queue.url) return queue diff --git a/lambdas/link_fetcher/tests/data/push-granule-created-s2-n1.json b/lambdas/link_fetcher/tests/data/push-granule-created-s2-n1.json new file mode 100644 index 00000000..f6cea7e7 --- /dev/null +++ b/lambdas/link_fetcher/tests/data/push-granule-created-s2-n1.json @@ -0,0 +1,209 @@ +{ + "@odata.context": "$metadata#Notification/$entity", + "SubscriptionEvent": "created", + "ProductId": "f867a59a-9336-46d0-93ae-e55bf29403f8", + "ProductName": "S2A_MSIL1C_20240912T112541_N0511_R137_T28PHQ_20240912T133420.SAFE", + "SubscriptionId": "880c4e1a-cfc7-4956-bc4c-4434069a0aa7", + "NotificationDate": "2024-09-12T14:52:52.000Z", + "AckId": "MTcyNjE1Mjc3MjMyMi0wOjg4MGM0ZTFhLWNmYzctNDk1Ni1iYzRjLTQ0MzQwNjlhMGFhNw==", + "value": { + "@odata.context": "$metadata#Products(Attributes())(Assets())(Locations())/$entity", + "@odata.mediaContentType": "application/octet-stream", + "Id": "f867a59a-9336-46d0-93ae-e55bf29403f8", + "Name": "S2A_MSIL1C_20240912T112541_N0511_R137_T28PHQ_20240912T133420.SAFE", + "ContentType": "application/octet-stream", + "ContentLength": 132384463, + "OriginDate": "2024-09-12T14:39:38.000Z", + "PublicationDate": "2024-09-12T14:52:06.118Z", + "ModificationDate": "2024-09-12T14:52:51.828Z", + "Online": true, + "EvictionDate": "9999-12-31T23:59:59.999Z", + "S3Path": "/eodata/Sentinel-2/MSI/L1C/2024/09/12/S2A_MSIL1C_20240912T112541_N0511_R137_T28PHQ_20240912T133420.SAFE", + "Checksum": [ + { + "Value": "700a3f2014ab3670408b94e4310924dd", + "Algorithm": "MD5", + "ChecksumDate": "2024-09-12T14:52:51.426545Z" + }, + { + "Value": "fd35ef962ec7a09b190dc4049fe98906cc89f16e1a4322b622a49fc62e3f6e43", + "Algorithm": "BLAKE3", + "ChecksumDate": "2024-09-12T14:52:51.657328Z" + } + ], + "ContentDate": { + "Start": "2024-09-12T11:25:41.024Z", + "End": "2024-09-12T11:25:41.024Z" + }, + "Footprint": "geography'SRID=4326;POLYGON ((-11.974999865007275 9.033981046236953, -12.271487717962122 9.03659015317746, -12.27853242584367 8.044545576909323, -12.199694512664326 8.043927404167855, -12.18648545337299 8.102176718496475, -12.15282241132278 8.250773715771231, -12.119125071984612 8.399275118590491, -12.085423254585894 8.547673138011769, -12.051758447762111 8.695945283833096, -12.018066565499025 8.844240589995735, -11.984403225613384 8.992620036679964, -11.974999865007275 9.033981046236953))'", + "GeoFootprint": { + "type": "Polygon", + "coordinates": [ + [ + [-11.974999865007275, 9.033981046236953], + [-12.271487717962122, 9.03659015317746], + [-12.27853242584367, 8.044545576909323], + [-12.199694512664326, 8.043927404167855], + [-12.18648545337299, 8.102176718496475], + [-12.15282241132278, 8.250773715771231], + [-12.119125071984612, 8.399275118590491], + [-12.085423254585894, 8.547673138011769], + [-12.051758447762111, 8.695945283833096], + [-12.018066565499025, 8.844240589995735], + [-11.984403225613384, 8.992620036679964], + [-11.974999865007275, 9.033981046236953] + ] + ] + }, + "Attributes": [ + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "origin", + "Value": "ESA", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "tileId", + "Value": "28PHQ", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.DoubleAttribute", + "Name": "cloudCover", + "Value": 97.680833965944, + "ValueType": "Double" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "datastripId", + "Value": "S2A_OPER_MSI_L1C_DS_2APS_20240912T133420_S20240912T112539_N05.11", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.IntegerAttribute", + "Name": "orbitNumber", + "Value": 48182, + "ValueType": "Integer" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "sourceProduct", + "Value": "S2A_OPER_MSI_L1C_TL_2APS_20240912T133420_A048182_T28PHQ_N05.11,S2A_OPER_MSI_L1C_DS_2APS_20240912T133420_S20240912T112539_N05.11,S2A_OPER_MSI_L1C_TC_2APS_20240912T133420_A048182_T28PHQ_N05.11.jp2", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.DateTimeOffsetAttribute", + "Name": "processingDate", + "Value": "2024-09-12T13:34:20+00:00", + "ValueType": "DateTimeOffset" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "productGroupId", + "Value": "GS2A_20240912T112541_048182_N05.11", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "operationalMode", + "Value": "INS-NOBS", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "processingLevel", + "Value": "S2MSI1C", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "processorVersion", + "Value": "05.11", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "granuleIdentifier", + "Value": "S2A_OPER_MSI_L1C_TL_2APS_20240912T133420_A048182_T28PHQ_N05.11", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "platformShortName", + "Value": "SENTINEL-2", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "instrumentShortName", + "Value": "MSI", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.IntegerAttribute", + "Name": "relativeOrbitNumber", + "Value": 137, + "ValueType": "Integer" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "sourceProductOriginDate", + "Value": "2024-09-12T14:39:37Z,2024-09-12T14:39:33Z,2024-09-12T14:39:38Z", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "platformSerialIdentifier", + "Value": "A", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.StringAttribute", + "Name": "productType", + "Value": "S2MSI1C", + "ValueType": "String" + }, + { + "@odata.type": "#OData.CSC.DateTimeOffsetAttribute", + "Name": "beginningDateTime", + "Value": "2024-09-12T11:25:41.024Z", + "ValueType": "DateTimeOffset" + }, + { + "@odata.type": "#OData.CSC.DateTimeOffsetAttribute", + "Name": "endingDateTime", + "Value": "2024-09-12T11:25:41.024Z", + "ValueType": "DateTimeOffset" + } + ], + "Assets": [ + { + "Type": "QUICKLOOK", + "Id": "97cd3cae-9999-4871-9767-9a44305415e6", + "DownloadLink": "https://catalogue.dataspace.copernicus.eu/odata/v1/Assets(97cd3cae-9999-4871-9767-9a44305415e6)/$value", + "S3Path": "/eodata/Sentinel-2/MSI/L1C/2024/09/12/S2A_MSIL1C_20240912T112541_N0511_R137_T28PHQ_20240912T133420.SAFE" + } + ], + "Locations": [ + { + "FormatType": "Extracted", + "DownloadLink": "https://catalogue.dataspace.copernicus.eu/odata/v1/Products(f867a59a-9336-46d0-93ae-e55bf29403f8)/$value", + "ContentLength": 132384463, + "Checksum": [ + { + "Value": "700a3f2014ab3670408b94e4310924dd", + "Algorithm": "MD5", + "ChecksumDate": "2024-09-12T14:52:51.426545Z" + }, + { + "Value": "fd35ef962ec7a09b190dc4049fe98906cc89f16e1a4322b622a49fc62e3f6e43", + "Algorithm": "BLAKE3", + "ChecksumDate": "2024-09-12T14:52:51.657328Z" + } + ], + "S3Path": "/eodata/Sentinel-2/MSI/L1C/2024/09/12/S2A_MSIL1C_20240912T112541_N0511_R137_T28PHQ_20240912T133420.SAFE" + } + ] + } +} diff --git a/lambdas/link_fetcher/tests/docker-compose.yml b/lambdas/link_fetcher/tests/docker-compose.yml index ff7b2f16..fb9b8806 100644 --- a/lambdas/link_fetcher/tests/docker-compose.yml +++ b/lambdas/link_fetcher/tests/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3.9" services: postgres: image: postgres:10.12-alpine diff --git a/lambdas/link_fetcher/tests/test_common.py b/lambdas/link_fetcher/tests/test_common.py new file mode 100644 index 00000000..3fc57119 --- /dev/null +++ b/lambdas/link_fetcher/tests/test_common.py @@ -0,0 +1,103 @@ +import json +from typing import Callable, Sequence +from unittest.mock import patch + +from assertpy import assert_that +from db.models.granule import Granule +from sqlalchemy.orm import Session + +from app.common import ( + SearchResult, + add_search_result_to_sqs, + add_search_results_to_db_and_sqs, + get_accepted_tile_ids, +) + + +def test_that_link_fetcher_handler_correctly_loads_allowed_tiles(): + tile_ids = get_accepted_tile_ids() + assert_that(tile_ids).is_length(18952) + assert_that(tile_ids).contains("01FBE") + assert_that(tile_ids).contains("60WWV") + + +def test_that_link_fetcher_handler_correctly_adds_search_results_to_db( + db_session: Session, + search_result_maker: Callable[[int], Sequence[SearchResult]], + mock_sqs_queue, + db_session_context, +): + search_results = search_result_maker(10) + search_result_id_base = search_results[0].image_id[:-3] + search_result_url_base = search_results[0].download_url[:-3] + + with patch("app.common.add_search_result_to_sqs") as mock_add_to_sqs: + mock_add_to_sqs.return_value = None + add_search_results_to_db_and_sqs(lambda: db_session, search_results) + mock_add_to_sqs.assert_called() + + granules_in_db = db_session.query(Granule).all() + assert_that(granules_in_db).is_length(10) + + for idx, granule in enumerate(granules_in_db): + id_filled = str(idx).zfill(3) + expected_id = f"{search_result_id_base}{id_filled}" + expected_url = f"{search_result_url_base}{id_filled}" + granule_id = granule.id + granule_download_url = granule.download_url + assert_that(expected_id).is_equal_to(granule_id) + assert_that(expected_url).is_equal_to(granule_download_url) + + +def test_that_link_fetcher_handler_correctly_handles_duplicate_db_entry( + db_session: Session, + search_result_maker: Callable[[int], Sequence[SearchResult]], + mock_sqs_queue, +): + search_result = search_result_maker(1)[0] + db_session.add( + Granule( + id=search_result.image_id, + filename=search_result.filename, + tileid=search_result.tileid, + size=search_result.size, + beginposition=search_result.beginposition, + endposition=search_result.endposition, + ingestiondate=search_result.ingestiondate, + download_url=search_result.download_url, + ) # type: ignore + ) + + # Because of how the db sessions are handled in these unit tests, the rollback + # call actually undoes the insert that the test setup does, so we're just asserting + # that rollback is called, not that there is still one entry. + # That's the best we can do without a full e2e test. + with patch.object(db_session, "rollback") as rollback: + add_search_results_to_db_and_sqs(lambda: db_session, [search_result]) + rollback.assert_called_once() + + +def test_that_link_fetcher_handler_correctly_adds_search_result_to_queue( + mock_sqs_queue, + search_result_maker: Callable[[int], Sequence[SearchResult]], + sqs_client, +): + search_result = search_result_maker(1)[0] + search_result_id = search_result.image_id + search_result_url = search_result.download_url + search_result_filename = search_result.filename + + add_search_result_to_sqs(search_result, sqs_client, mock_sqs_queue.url) + + mock_sqs_queue.load() + + number_of_messages_in_queue = mock_sqs_queue.attributes[ + "ApproximateNumberOfMessages" + ] + assert_that(int(number_of_messages_in_queue)).is_equal_to(1) + + message = mock_sqs_queue.receive_messages(MaxNumberOfMessages=1)[0] + message_body = json.loads(message.body) + assert_that(search_result_id).is_equal_to(message_body["id"]) + assert_that(search_result_url).is_equal_to(message_body["download_url"]) + assert_that(search_result_filename).is_equal_to(message_body["filename"]) diff --git a/lambdas/link_fetcher/tests/test_link_fetcher_handler.py b/lambdas/link_fetcher/tests/test_link_fetcher_handler.py index 8d5af463..6840cf06 100644 --- a/lambdas/link_fetcher/tests/test_link_fetcher_handler.py +++ b/lambdas/link_fetcher/tests/test_link_fetcher_handler.py @@ -1,8 +1,5 @@ import dataclasses -import json from datetime import date, datetime, timezone -from typing import Callable, Sequence -from unittest.mock import patch import pytest import responses @@ -13,16 +10,15 @@ from freezegun import freeze_time from sqlalchemy.orm import Session -from handler import ( +from app.common import ( + SearchResult, +) +from app.search_handler import ( MIN_REMAINING_MILLIS, SEARCH_URL, - SearchResult, _handler, - add_search_result_to_sqs, - add_search_results_to_db_and_sqs, create_search_result, filter_search_results, - get_accepted_tile_ids, get_fetched_links, get_page_for_query_and_total_results, get_query_parameters, @@ -32,13 +28,6 @@ ) -def test_that_link_fetcher_handler_correctly_loads_allowed_tiles(): - tile_ids = get_accepted_tile_ids() - assert_that(tile_ids).is_length(18952) - assert_that(tile_ids).contains("01FBE") - assert_that(tile_ids).contains("60WWV") - - def test_that_link_fetcher_handler_generates_correct_query_parameters(): expected_query_parameters = { "processingLevel": "S2MSI1C", @@ -256,88 +245,6 @@ def test_that_link_fetcher_handler_correctly_filters_search_results(accepted_til assert actual_results == expected_results -def test_that_link_fetcher_handler_correctly_adds_search_results_to_db( - db_session: Session, - search_result_maker: Callable[[int], Sequence[SearchResult]], - mock_sqs_queue, - db_session_context, -): - search_results = search_result_maker(10) - search_result_id_base = search_results[0].image_id[:-3] - search_result_url_base = search_results[0].download_url[:-3] - - with patch("handler.add_search_result_to_sqs") as mock_add_to_sqs: - mock_add_to_sqs.return_value = None - add_search_results_to_db_and_sqs(lambda: db_session, search_results) - mock_add_to_sqs.assert_called() - - granules_in_db = db_session.query(Granule).all() - assert_that(granules_in_db).is_length(10) - - for idx, granule in enumerate(granules_in_db): - id_filled = str(idx).zfill(3) - expected_id = f"{search_result_id_base}{id_filled}" - expected_url = f"{search_result_url_base}{id_filled}" - granule_id = granule.id - granule_download_url = granule.download_url - assert_that(expected_id).is_equal_to(granule_id) - assert_that(expected_url).is_equal_to(granule_download_url) - - -def test_that_link_fetcher_handler_correctly_handles_duplicate_db_entry( - db_session: Session, - search_result_maker: Callable[[int], Sequence[SearchResult]], - mock_sqs_queue, -): - search_result = search_result_maker(1)[0] - db_session.add( - Granule( - id=search_result.image_id, - filename=search_result.filename, - tileid=search_result.tileid, - size=search_result.size, - beginposition=search_result.beginposition, - endposition=search_result.endposition, - ingestiondate=search_result.ingestiondate, - download_url=search_result.download_url, - ) # type: ignore - ) - - # Because of how the db sessions are handled in these unit tests, the rollback - # call actually undoes the insert that the test setup does, so we're just asserting - # that rollback is called, not that there is still one entry. - # That's the best we can do without a full e2e test. - with patch.object(db_session, "rollback") as rollback: - add_search_results_to_db_and_sqs(lambda: db_session, [search_result]) - rollback.assert_called_once() - - -def test_that_link_fetcher_handler_correctly_adds_search_result_to_queue( - mock_sqs_queue, - search_result_maker: Callable[[int], Sequence[SearchResult]], - sqs_client, -): - search_result = search_result_maker(1)[0] - search_result_id = search_result.image_id - search_result_url = search_result.download_url - search_result_filename = search_result.filename - - add_search_result_to_sqs(search_result, sqs_client, mock_sqs_queue.url) - - mock_sqs_queue.load() - - number_of_messages_in_queue = mock_sqs_queue.attributes[ - "ApproximateNumberOfMessages" - ] - assert_that(int(number_of_messages_in_queue)).is_equal_to(1) - - message = mock_sqs_queue.receive_messages(MaxNumberOfMessages=1)[0] - message_body = json.loads(message.body) - assert_that(search_result_id).is_equal_to(message_body["id"]) - assert_that(search_result_url).is_equal_to(message_body["download_url"]) - assert_that(search_result_filename).is_equal_to(message_body["filename"]) - - def test_that_link_fetcher_handler_correctly_retrieves_fetched_links_if_in_db( db_session: Session, ): diff --git a/lambdas/link_fetcher/tests/test_subscription_endpoint.py b/lambdas/link_fetcher/tests/test_subscription_endpoint.py new file mode 100644 index 00000000..47de0029 --- /dev/null +++ b/lambdas/link_fetcher/tests/test_subscription_endpoint.py @@ -0,0 +1,301 @@ +import json +from collections.abc import Iterator +from datetime import datetime, timezone +from pathlib import Path +from typing import Callable +from unittest.mock import Mock, patch + +import boto3 +import httpx +import pytest +from db.models.granule import Granule +from fastapi import FastAPI +from moto import mock_aws +from sqlalchemy.orm import Session +from starlette.testclient import TestClient + +import app.subscription_endpoint +from app.common import SearchResult +from app.subscription_endpoint import ( + EndpointConfig, + build_app, + parse_search_result, + process_notification, +) + + +class TestEndpointConfig: + """Test EndpointConfig""" + + def test_basic_init_ennvar(self, monkeypatch): + monkeypatch.setenv("STAGE", "local") + monkeypatch.setenv("NOTIFICATION_USERNAME", "bar") + monkeypatch.setenv("NOTIFICATION_PASSWORD", "baz") + config = EndpointConfig() + assert config.stage == "local" + assert config.notification_username == "bar" + assert config.notification_password == "baz" + + @pytest.fixture + def endpoint_config_secret(self) -> Iterator[EndpointConfig]: + config = EndpointConfig( + stage="local", + notification_username="bar", + notification_password="baz", + ) + + with mock_aws(): + secrets_manager_client = boto3.client("secretsmanager") + secrets_manager_client.create_secret( + Name=f"hls-s2-downloader-serverless/{config.stage}/esa-subscription-credentials", + SecretString=json.dumps( + { + "notification_username": config.notification_username, + "notification_password": config.notification_password, + } + ), + ) + yield config + + def test_local_from_ssm(self, endpoint_config_secret: EndpointConfig): + config = EndpointConfig.load_from_secrets_manager(endpoint_config_secret.stage) + assert config == endpoint_config_secret + + +@pytest.fixture +def event_s2_created() -> dict: + """Load Sentinel-2 "Created" event from ESA's push subscription + + This message contains two types of fields, + * Message metadata (event type, subscription ID, ack ID, notification date, etc) + * Message "body" - `(.value)` + """ + data = Path(__file__).parent / "data" / "push-granule-created-s2-n1.json" + with data.open() as src: + return json.load(src) + + +class TestSearchResultParsing: + """Tests for parsing subscription into a SearchResult""" + + def test_parses_created_event(self, event_s2_created: dict): + """Test happy path of parsing event to SearchResult""" + search_result = parse_search_result(event_s2_created["value"]) + assert isinstance(search_result, SearchResult) + + def test_raises_if_no_extracted_data(self, event_s2_created: dict): + """Test we catch if there's no "extracted" data""" + # this should never happen as newly published data because it'll be "Online" + payload = event_s2_created["value"] + payload["Locations"][0]["FormatType"] = "Archived" + + with pytest.raises( + ValueError, match=r"Got 0 'Extracted' links, expected just 1" + ): + parse_search_result(payload) + + def test_raises_if_multiple_extracted_data(self, event_s2_created: dict): + """Test we catch if there's >1 "extracted" data""" + # this also shouldn't happen, but if it does we want to fail because + # it won't be clear which to download + payload = event_s2_created["value"] + payload["Locations"].append(payload["Locations"][0].copy()) + + with pytest.raises( + ValueError, match=r"Got 2 'Extracted' links, expected just 1" + ): + parse_search_result(payload) + + +class TestProcessNotification: + """Test parsing and filtering of granule created notification""" + + def test_processes_notification( + self, + mock_sqs_queue, + db_session: Session, + event_s2_created: dict, + accepted_tile_ids: set[str], + ): + """Test that a recent S2 granule created event is added to queue""" + process_notification( + event_s2_created, + accepted_tile_ids, + lambda: db_session, + # provide a fake datetime based on publication date to ensure the granule notification + # is recent enough to process + now_utc=lambda: datetime.fromisoformat( + event_s2_created["value"]["PublicationDate"] + ), + ) + + assert len(db_session.query(Granule).all()) == 1 + + mock_sqs_queue.load() + number_of_messages_in_queue = mock_sqs_queue.attributes[ + "ApproximateNumberOfMessages" + ] + assert int(number_of_messages_in_queue) == 1 + + def test_filters_old_imagery( + self, + mock_sqs_queue, + db_session: Session, + event_s2_created: dict, + accepted_tile_ids: set[str], + ): + """Test we filter old imagery and do NOT add to queue or DB""" + event_s2_created["value"]["ContentDate"]["Start"] = "1999-12-31T23:59:59.999Z" + with patch( + "app.subscription_endpoint.add_search_results_to_db_and_sqs" + ) as mock_add_to_db_and_sqs: + process_notification( + event_s2_created, + accepted_tile_ids, + lambda: db_session, + ) + mock_add_to_db_and_sqs.assert_not_called() + + def test_filters_unaccepted_tile_id( + self, + mock_sqs_queue, + db_session: Session, + event_s2_created: dict, + mocker, + ): + """Test we filter unacceptable tile IDs and do NOT add to queue or DB""" + spy_filter_search_results = mocker.spy( + app.subscription_endpoint, "filter_search_results" + ) + with patch( + "app.common.add_search_results_to_db_and_sqs" + ) as mock_add_to_db_and_sqs: + process_notification( + event_s2_created, + {"none"}, + lambda: db_session, + # ensure we granule is recent enough <30 days + now_utc=lambda: datetime.fromisoformat( + event_s2_created["value"]["PublicationDate"] + ), + ) + spy_filter_search_results.assert_called_once() + mock_add_to_db_and_sqs.assert_not_called() + + +class TestApp: + """Test API endpoint""" + + @pytest.fixture() + def config(self) -> EndpointConfig: + return EndpointConfig( + stage="local", + notification_username="test_user", + notification_password="password", + ) + + @pytest.fixture + def now_utc(self, request) -> Callable[[], datetime]: + if callable(getattr(request, "param", None)): + return request.param + return lambda: datetime.now(tz=timezone.utc) + + @pytest.fixture + def test_client( + self, + config: EndpointConfig, + db_connection_secret, + mock_sqs_queue, + now_utc, + ) -> FastAPI: + self.endpoint_config = config + self.db_connection_secret = db_connection_secret + self.mock_sqs_queue = mock_sqs_queue + app = build_app(config, now_utc) + return TestClient(app) + + def test_handles_new_created_event( + self, test_client: TestClient, event_s2_created: dict + ): + """Test happy path for handling subscription event, mocking processing function""" + with patch( + "app.subscription_endpoint.process_notification", Mock() + ) as mock_process_notification: + resp = test_client.post( + "/events", + json=event_s2_created, + auth=( + self.endpoint_config.notification_username, + self.endpoint_config.notification_password, + ), + ) + # processed successfully but no content + resp.raise_for_status() + assert resp.status_code == 204 + mock_process_notification.assert_called_once() + + @pytest.mark.parametrize( + "now_utc", + [lambda: datetime.fromisoformat("2024-09-12T14:52:06.118Z")], + indirect=True, + ) + def test_handles_new_created_event_is_added( + self, + test_client: TestClient, + db_session: Session, + event_s2_created: dict, + now_utc: Callable[[], datetime], + ): + """Test happy path for handling subscription event, mocking DB and SQS + + We ensure the new event is "recent" enough to accept by redefining the + `now_utc` that our application is provided to match the publication + date of the test data. + """ + resp = test_client.post( + "/events", + json=event_s2_created, + auth=( + self.endpoint_config.notification_username, + self.endpoint_config.notification_password, + ), + ) + + # processed successfully but no content + resp.raise_for_status() + assert resp.status_code == 204 + + # Check we have a message in moto's SQS queue + self.mock_sqs_queue.load() + number_of_messages_in_queue = int( + self.mock_sqs_queue.attributes["ApproximateNumberOfMessages"] + ) + assert number_of_messages_in_queue == 1 + + def test_handles_wrong_user(self, test_client: TestClient, event_s2_created: dict): + """Test happy path for handling subscription event""" + resp = test_client.post( + "/events", + json=event_s2_created, + auth=( + "wrong", + self.endpoint_config.notification_password, + ), + ) + with pytest.raises(httpx.HTTPStatusError) as err: + resp.raise_for_status() + assert err.value.response.status_code == 401 # unauthorized + + def test_handles_wrong_pass(self, test_client: TestClient, event_s2_created: dict): + """Test happy path for handling subscription event""" + resp = test_client.post( + "/events", + json=event_s2_created, + auth=( + self.endpoint_config.notification_username, + "wrong", + ), + ) + with pytest.raises(httpx.HTTPStatusError) as err: + resp.raise_for_status() + assert err.value.response.status_code == 401 # unauthorized diff --git a/lambdas/requeuer/Pipfile b/lambdas/requeuer/Pipfile index 1ba25f6b..839259e2 100644 --- a/lambdas/requeuer/Pipfile +++ b/lambdas/requeuer/Pipfile @@ -13,7 +13,7 @@ boto3-stubs = {extras = ["lambda", "sqs", "secretsmanager"], version = "*"} db = {editable = true, path = "./../../layers/db"} moto = "==5.0.17" mypy = "*" -psycopg2 = "==2.9.10" +psycopg2-binary = "==2.9.10" pytest = "*" pytest-cov = "*" pytest-docker = "*" diff --git a/lambdas/requeuer/Pipfile.lock b/lambdas/requeuer/Pipfile.lock index 26e699c6..bc59d525 100644 --- a/lambdas/requeuer/Pipfile.lock +++ b/lambdas/requeuer/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "8a91f65b2cbab84cd0afb3d6cf4603e42c2448136145b065532a7b5fe0f7ea73" + "sha256": "21b33754b2e3a192f4969255b63939699d54534ad359d6daa8eacdfe5d5e0e96" }, "pipfile-spec": 6, "requires": { @@ -27,11 +27,11 @@ }, "botocore": { "hashes": [ - "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a", - "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547" + "sha256:564c2478e50179e0b766e6a87e5e0cdd35e1bc37eb375c1cf15511f5dd13600d", + "sha256:a7b13bbd959bf2d6f38f681676aab408be01974c46802ab997617b51399239f7" ], "markers": "python_version >= '3.8'", - "version": "==1.35.47" + "version": "==1.35.81" }, "iso8601": { "hashes": [ @@ -60,19 +60,19 @@ }, "s3transfer": { "hashes": [ - "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d", - "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.16.0" + "version": "==1.17.0" }, "urllib3": { "hashes": [ @@ -86,20 +86,20 @@ "develop": { "alembic": { "hashes": [ - "sha256:203503117415561e203aa14541740643a611f641517f0209fcae63e9fa09f1a2", - "sha256:908e905976d15235fae59c9ac42c4c5b75cfcefe3d27c0fbf7ae15a37715d80e" + "sha256:99bd884ca390466db5e27ffccff1d179ec5c05c965cfefc0607e69f9e411cb25", + "sha256:b00892b53b3642d0b8dbedba234dbf1924b69be83a9a769d5a624b01094e304b" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.13.3" + "version": "==1.14.0" }, "attrs": { "hashes": [ - "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", - "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2" + "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff", + "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308" ], - "markers": "python_version >= '3.7'", - "version": "==24.2.0" + "markers": "python_version >= '3.8'", + "version": "==24.3.0" }, "boto3": { "hashes": [ @@ -117,35 +117,35 @@ "sqs" ], "hashes": [ - "sha256:5b2887214d0953e7e6d5aeb4276e3b9e3cc3fb7375ef39a9da8aaa0c3e48b223", - "sha256:a5bddda9eaa277b615d9e394f42f51d97e6652166db6810f645cc52a4f4efccb" + "sha256:d690a7286e7bb680ddbd0493f7f4119386f4f4389401cac227b8087b19f688cf", + "sha256:e9b82779553ae5ec3b35d1a6f245b148f6c5313152b9e591f542ff915fb20bfe" ], "markers": "python_version >= '3.8'", - "version": "==1.35.46" + "version": "==1.35.81" }, "botocore": { "hashes": [ - "sha256:05f4493119a96799ff84d43e78691efac3177e1aec8840cca99511de940e342a", - "sha256:f8f703463d3cd8b6abe2bedc443a7ab29f0e2ff1588a2e83164b108748645547" + "sha256:564c2478e50179e0b766e6a87e5e0cdd35e1bc37eb375c1cf15511f5dd13600d", + "sha256:a7b13bbd959bf2d6f38f681676aab408be01974c46802ab997617b51399239f7" ], "markers": "python_version >= '3.8'", - "version": "==1.35.47" + "version": "==1.35.81" }, "botocore-stubs": { "hashes": [ - "sha256:397f78e5c9ed951fbb576cc0c544e29b626bd7a3742d463c015c0cc71e1d292c", - "sha256:f1ef0cd7c263f02f5ad3583f3ab99106e519b0d04d94f84c64c6afe146f83dff" + "sha256:818e1ec37d6a97e1aa7955f85a45c6b5d7e4d7599b00658529e0f070330791d7", + "sha256:d3759683a7834053d074d75fab12fb09fdb6ba73e491db37a3ff97ef3dff5d12" ], "markers": "python_version >= '3.8'", - "version": "==1.35.47" + "version": "==1.35.81" }, "certifi": { "hashes": [ - "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", - "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9" + "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", + "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" ], "markers": "python_version >= '3.6'", - "version": "==2024.8.30" + "version": "==2024.12.14" }, "cffi": { "hashes": [ @@ -336,104 +336,104 @@ "toml" ], "hashes": [ - "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376", - "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", - "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111", - "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172", - "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491", - "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546", - "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", - "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", - "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", - "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c", - "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", - "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", - "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", - "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0", - "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db", - "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf", - "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", - "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", - "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", - "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e", - "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522", - "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25", - "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", - "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea", - "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", - "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", - "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07", - "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", - "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa", - "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901", - "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b", - "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", - "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0", - "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", - "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19", - "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", - "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51", - "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", - "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3", - "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", - "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076", - "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", - "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718", - "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", - "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e", - "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", - "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", - "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09", - "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", - "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", - "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f", - "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72", - "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a", - "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", - "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b", - "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", - "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", - "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", - "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b", - "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", - "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", - "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858" + "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4", + "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c", + "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f", + "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b", + "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6", + "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae", + "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692", + "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4", + "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4", + "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717", + "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d", + "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198", + "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1", + "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3", + "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb", + "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d", + "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08", + "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf", + "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b", + "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710", + "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c", + "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae", + "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077", + "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00", + "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb", + "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664", + "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014", + "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9", + "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6", + "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e", + "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9", + "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa", + "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611", + "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b", + "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a", + "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8", + "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030", + "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678", + "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015", + "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902", + "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97", + "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845", + "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419", + "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464", + "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be", + "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9", + "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7", + "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be", + "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1", + "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba", + "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5", + "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073", + "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4", + "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a", + "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a", + "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3", + "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599", + "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0", + "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b", + "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec", + "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1", + "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3" ], "markers": "python_version >= '3.9'", - "version": "==7.6.4" + "version": "==7.6.9" }, "cryptography": { "hashes": [ - "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362", - "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", - "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa", - "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83", - "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff", - "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", - "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6", - "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664", - "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08", - "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e", - "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18", - "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f", - "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73", - "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", - "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984", - "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd", - "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3", - "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e", - "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", - "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2", - "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c", - "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995", - "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", - "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", - "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7", - "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd", - "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7" + "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", + "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", + "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", + "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", + "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", + "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", + "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", + "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", + "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", + "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", + "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", + "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", + "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", + "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", + "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", + "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", + "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", + "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", + "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", + "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", + "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", + "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", + "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", + "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", + "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", + "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", + "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" ], - "markers": "python_version >= '3.7'", - "version": "==43.0.3" + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.0" }, "db": { "editable": true, @@ -552,11 +552,11 @@ }, "mako": { "hashes": [ - "sha256:9ec3a1583713479fae654f83ed9fa8c9a4c16b7bb0daba0e6bbebff50c0d983d", - "sha256:a91198468092a2f1a0de86ca92690fb0cfc43ca90ee17e15d93662b4c04b241a" + "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627", + "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8" ], "markers": "python_version >= '3.8'", - "version": "==1.3.6" + "version": "==1.3.8" }, "markupsafe": { "hashes": [ @@ -675,10 +675,10 @@ }, "mypy-boto3-lambda": { "hashes": [ - "sha256:8473d71ee83aca8009d317e57cd2094a355ec90c7c536cf26e52db71b2f7528b", - "sha256:e42d9ce7e6a32841e4a6a2980f5f8634e2b0a35698e71d302a78e4d0de4223c6" + "sha256:00499898236fe423c9292f77644102d4bd6699b3c16b8c4062eb759c022447f5", + "sha256:577a9465ac63ac564efc2755a7e72c28a9d2f496747c1faf242cb13d5017b262" ], - "version": "==1.35.28" + "version": "==1.35.68" }, "mypy-boto3-secretsmanager": { "hashes": [ @@ -704,11 +704,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "pluggy": { "hashes": [ @@ -718,17 +718,75 @@ "markers": "python_version >= '3.8'", "version": "==1.5.0" }, - "psycopg2": { + "psycopg2-binary": { "hashes": [ - "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4", - "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11", - "sha256:47c4f9875125344f4c2b870e41b6aad585901318068acd01de93f3677a6522c2", - "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e", - "sha256:5df2b672140f95adb453af93a7d669d7a7bf0a56bcd26f1502329166f4a61716", - "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067", - "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442", - "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b", - "sha256:c6f7b8561225f9e711a9c47087388a97fdc948211c10a4bccbf0ba68ab7b3b5a" + "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", + "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5", + "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", + "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", + "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", + "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", + "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", + "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", + "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", + "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", + "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", + "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", + "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", + "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", + "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", + "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", + "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", + "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", + "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", + "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", + "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", + "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", + "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", + "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", + "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", + "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", + "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", + "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", + "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44", + "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", + "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", + "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", + "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa", + "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", + "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", + "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", + "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", + "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", + "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", + "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", + "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", + "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", + "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", + "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3", + "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", + "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92", + "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", + "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", + "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8", + "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", + "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", + "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864", + "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", + "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", + "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", + "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", + "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", + "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", + "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", + "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4", + "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", + "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", + "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", + "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", + "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", + "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", + "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863" ], "index": "pypi", "markers": "python_version >= '3.8'", @@ -744,21 +802,21 @@ }, "pytest": { "hashes": [ - "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", - "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2" + "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", + "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==8.3.3" + "version": "==8.3.4" }, "pytest-cov": { "hashes": [ - "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", - "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857" + "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", + "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==5.0.0" + "markers": "python_version >= '3.9'", + "version": "==6.0.0" }, "pytest-docker": { "hashes": [ @@ -879,19 +937,19 @@ }, "s3transfer": { "hashes": [ - "sha256:263ed587a5803c6c708d3ce44dc4dfedaab4c1a32e8329bab818933d79ddcf5d", - "sha256:4f50ed74ab84d474ce614475e0b8d5047ff080810aac5d01ea25231cfc944b0c" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", - "version": "==1.16.0" + "version": "==1.17.0" }, "sqlalchemy": { "hashes": [ @@ -935,26 +993,26 @@ }, "types-awscrt": { "hashes": [ - "sha256:3fd1edeac923d1956c0e907c973fb83bda465beae7f054716b371b293f9b5fdc", - "sha256:517d9d06f19cf58d778ca90ad01e52e0489466bf70dcf78c7f47f74fdf151a60" + "sha256:b1b9bb10f337e3fe8f5f508860eb354d9fe093f02e1485955a9e0bdd4e250074", + "sha256:eeb4bd596100927704c8b9f964ec8a246be4943d546f3fd2a8efdddebea422ea" ], "markers": "python_version >= '3.8'", - "version": "==0.23.0" + "version": "==0.23.4" }, "types-s3transfer": { "hashes": [ - "sha256:d34c5a82f531af95bb550927136ff5b737a1ed3087f90a59d545591dfde5b4cc", - "sha256:f761b2876ac4c208e6c6b75cdf5f6939009768be9950c545b11b0225e7703ee7" + "sha256:03123477e3064c81efe712bf9d372c7c72f2790711431f9baa59cf96ea607267", + "sha256:22ac1aabc98f9d7f2928eb3fb4d5c02bf7435687f0913345a97dd3b84d0c217d" ], "markers": "python_version >= '3.8'", - "version": "==0.10.3" + "version": "==0.10.4" }, "typing-extensions": { "hashes": [ "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" ], - "markers": "python_version >= '3.8'", + "markers": "python_version < '3.12'", "version": "==4.12.2" }, "urllib3": { @@ -967,11 +1025,11 @@ }, "werkzeug": { "hashes": [ - "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c", - "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306" + "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", + "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746" ], - "markers": "python_version >= '3.8'", - "version": "==3.0.4" + "markers": "python_version >= '3.9'", + "version": "==3.1.3" }, "xmltodict": { "hashes": [ diff --git a/setup.py b/setup.py index 3c3249ab..811aa5f9 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ f"aws-cdk.aws-lambda-python-alpha=={aws_cdk_version}a0", "boto3", "polling2", - "psycopg2", + "psycopg2-binary==2.9.10", "python-dotenv", ] @@ -27,6 +27,7 @@ "mypy", "pytest", "pytest-cov", + "requests", ], "dev": [ "assertpy",