diff --git a/Pipfile.lock b/Pipfile.lock index 5d60453..ee62001 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -18,12 +18,12 @@ "default": { "boto3": { "hashes": [ - "sha256:6257cad97d92c2b5597aec6e5484b9cfed8c0c785297942ed37cfaf2dd0ec23c", - "sha256:f22678bdbdc91ca6022a45696284d236e1fbafa84ca3a69d108d4a155cdd823e" + "sha256:786930d5f1cd13d03db59ff2abbb2b7ffc173fd66646d5d8bee07f316a5f16ca", + "sha256:f7851cb320dcb2a53fc73b4075187ec9b05d51291539601fa238623fdc0e8cd3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.35.85" + "version": "==1.35.92" }, "boto3-stubs": { "extras": [ @@ -31,27 +31,27 @@ "ses" ], "hashes": [ - "sha256:c3c1709603cb9d0fba4667b8408847f05b8f0b92bb74e88e0e97571cb6dd7745", - "sha256:c949abdba605dec649cfceab95f573c8fbce575ed23d8522e965b9eb6da4eeba" + "sha256:8d23b03ab9ca88bedc432adb08fd179bf1efd178128704cad80c138062b8f8a3", + "sha256:f2af463889d37fbab23c7cd08fb1b035f123ad67e4b3efc46f7714f9abee5e57" ], "markers": "python_version >= '3.8'", - "version": "==1.35.85" + "version": "==1.35.92" }, "botocore": { "hashes": [ - "sha256:04c196905b0eebcb29f7594a9e4588772a5222deed1b381f54cab78d0f30e239", - "sha256:5e7e8075e85427c9e0e6d15dcb7d13b3c843011b25d43981571fe1bfb3fd6985" + "sha256:caa7d5d857fed5b3d694b89c45f82b9f938f840e90a4eb7bf50aa65da2ba8f82", + "sha256:f94ae1e056a675bd67c8af98a6858d06e3927d974d6c712ed6e27bb1d11bee1d" ], "markers": "python_version >= '3.8'", - "version": "==1.35.85" + "version": "==1.35.92" }, "botocore-stubs": { "hashes": [ - "sha256:bb0d6fb74161944ab61a66ae2d891b780c163e89336f24462d0e8aed5b9ff4b9", - "sha256:ead1b807a3baa77962bc89ab701616425374ba5d31219fb64c2ca090f33090b3" + "sha256:c02ae70588e20d15a8100b34c1a1ebfa5f08e856f60570db0d16b128dc4c5c24", + "sha256:e116a2b84f67b84bbaa9a00577256664907d7c6a517fa0b1a3be7903fd6d0040" ], "markers": "python_version >= '3.8'", - "version": "==1.35.84.post1" + "version": "==1.35.92" }, "certifi": { "hashes": [ @@ -63,12 +63,12 @@ }, "click": { "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==8.1.7" + "version": "==8.1.8" }, "jmespath": { "hashes": [ @@ -108,17 +108,17 @@ }, "mypy-boto3-rds": { "hashes": [ - "sha256:197e732fc32423d3661845ee663563624c964ae06ccdedb79ee8de4c3272ec22", - "sha256:3f5c84cf922b388c138d7147d250f659eb447df170083a0a7d2594d906faff4f" + "sha256:b982945ac24e8ea0a996de05eafa50c7de2c201df4acde77bed1baaf16b9e72f", + "sha256:d577f9723801bab7078b0be011009dbf7472f6cc6118ba04d83217c1cc890a0c" ], - "version": "==1.35.82" + "version": "==1.35.89" }, "mypy-boto3-s3": { "hashes": [ - "sha256:6af1d815ff2cc8e32ca1190c7387f94341c1607444f958ac283aa10b1d11db08", - "sha256:fe1a6860c0ca7016e24089819433c0d5835d4a57635bb42628645b71271b946c" + "sha256:9ac88dc0f6d87892344ed99b1e5a2884215503afff3859417b6010b31de7cef6", + "sha256:ce302a635da78e1925d8ff4809184ba55618cd7e3707156bea405cde7fdcf67a" ], - "version": "==1.35.81" + "version": "==1.35.92" }, "mypy-boto3-ses": { "hashes": [ @@ -129,10 +129,10 @@ }, "mypy-boto3-sqs": { "hashes": [ - "sha256:61752f1c2bf2efa3815f64d43c25b4a39dbdbd9e472ae48aa18d7c6d2a7a6eb8", - "sha256:9fd6e622ed231c06f7542ba6f8f0eea92046cace24defa95d0d0ce04e7caee0c" + "sha256:012de7b44e0137083e2f5cb49b63c05091d141c32b569b469dfeaa5ea23d1ccd", + "sha256:346a87bc0a447bb4c005b04d3efa0008bfa0ddd498cadd97e0e53a58752f84e9" ], - "version": "==1.35.0" + "version": "==1.35.91" }, "python-dateutil": { "hashes": [ @@ -196,11 +196,11 @@ }, "urllib3": { "hashes": [ - "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", - "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" + "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", + "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" ], - "markers": "python_version >= '3.8'", - "version": "==2.2.3" + "markers": "python_version >= '3.9'", + "version": "==2.3.0" }, "wrapt": { "hashes": [ @@ -306,20 +306,20 @@ }, "boto3": { "hashes": [ - "sha256:6257cad97d92c2b5597aec6e5484b9cfed8c0c785297942ed37cfaf2dd0ec23c", - "sha256:f22678bdbdc91ca6022a45696284d236e1fbafa84ca3a69d108d4a155cdd823e" + "sha256:786930d5f1cd13d03db59ff2abbb2b7ffc173fd66646d5d8bee07f316a5f16ca", + "sha256:f7851cb320dcb2a53fc73b4075187ec9b05d51291539601fa238623fdc0e8cd3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.35.85" + "version": "==1.35.92" }, "botocore": { "hashes": [ - "sha256:04c196905b0eebcb29f7594a9e4588772a5222deed1b381f54cab78d0f30e239", - "sha256:5e7e8075e85427c9e0e6d15dcb7d13b3c843011b25d43981571fe1bfb3fd6985" + "sha256:caa7d5d857fed5b3d694b89c45f82b9f938f840e90a4eb7bf50aa65da2ba8f82", + "sha256:f94ae1e056a675bd67c8af98a6858d06e3927d974d6c712ed6e27bb1d11bee1d" ], "markers": "python_version >= '3.8'", - "version": "==1.35.85" + "version": "==1.35.92" }, "certifi": { "hashes": [ @@ -412,194 +412,181 @@ }, "charset-normalizer": { "hashes": [ - "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", - "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", - "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", - "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", - "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", - "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", - "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", - "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", - "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", - "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", - "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", - "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", - "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", - "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", - "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", - "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", - "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", - "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", - "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", - "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", - "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", - "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", - "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", - "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", - "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", - "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", - "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", - "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", - "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", - "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", - "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", - "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", - "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", - "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", - "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", - "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", - "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", - "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", - "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", - "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", - "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", - "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", - "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", - "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", - "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", - "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", - "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", - "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", - "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", - "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", - "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", - "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", - "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", - "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", - "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", - "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", - "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", - "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", - "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", - "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", - "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", - "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", - "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", - "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", - "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", - "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", - "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", - "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", - "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", - "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", - "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", - "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", - "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", - "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", - "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", - "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", - "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", - "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", - "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", - "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", - "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", - "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", - "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", - "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", - "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", - "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", - "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", - "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", - "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", - "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", - "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", - "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", - "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", - "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", - "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", - "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", - "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", - "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", - "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", - "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", - "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", - "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", - "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", - "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", - "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" - ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.4.0" + "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", + "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa", + "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", + "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", + "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", + "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", + "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", + "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", + "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", + "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", + "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", + "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", + "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", + "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", + "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", + "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", + "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", + "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", + "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", + "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", + "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e", + "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a", + "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", + "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", + "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", + "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", + "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", + "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", + "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", + "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", + "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", + "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", + "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", + "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", + "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", + "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", + "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", + "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", + "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", + "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", + "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", + "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", + "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", + "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf", + "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487", + "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d", + "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd", + "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", + "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534", + "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", + "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", + "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", + "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", + "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", + "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", + "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", + "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", + "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d", + "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", + "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", + "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", + "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", + "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", + "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", + "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", + "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", + "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", + "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", + "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", + "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", + "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", + "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", + "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", + "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", + "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", + "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", + "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", + "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e", + "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", + "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", + "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", + "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", + "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", + "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", + "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", + "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", + "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", + "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089", + "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", + "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e", + "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", + "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616" + ], + "markers": "python_version >= '3.7'", + "version": "==3.4.1" }, "click": { "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==8.1.7" + "version": "==8.1.8" }, "coverage": { "extras": [ "toml" ], "hashes": [ - "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" + "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9", + "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f", + "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273", + "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994", + "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e", + "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50", + "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e", + "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", + "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c", + "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853", + "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8", + "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8", + "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe", + "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165", + "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb", + "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59", + "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609", + "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18", + "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098", + "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd", + "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3", + "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43", + "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", + "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359", + "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90", + "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78", + "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a", + "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99", + "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988", + "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", + "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0", + "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694", + "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377", + "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d", + "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", + "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", + "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf", + "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", + "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b", + "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c", + "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690", + "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a", + "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f", + "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4", + "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25", + "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd", + "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852", + "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0", + "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244", + "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315", + "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078", + "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0", + "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27", + "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132", + "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5", + "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247", + "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022", + "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b", + "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3", + "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18", + "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5", + "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f" ], "markers": "python_version >= '3.9'", - "version": "==7.6.9" + "version": "==7.6.10" }, "coveralls": { "hashes": [ @@ -666,11 +653,11 @@ }, "identify": { "hashes": [ - "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02", - "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd" + "sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566", + "sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc" ], "markers": "python_version >= '3.9'", - "version": "==2.6.3" + "version": "==2.6.5" }, "idna": { "hashes": [ @@ -690,11 +677,11 @@ }, "jinja2": { "hashes": [ - "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", - "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d" + "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", + "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb" ], "markers": "python_version >= '3.7'", - "version": "==3.1.4" + "version": "==3.1.5" }, "jmespath": { "hashes": [ @@ -773,51 +760,57 @@ }, "moto": { "hashes": [ - "sha256:4d826f1574849f18ddd2fcbf614d97f82c8fddfb9d95fac1078da01a39b57c10", - "sha256:dba6426bd770fbb9d892633fbd35253cbc181eeaa0eba97d6f058720a8fe9b42" + "sha256:6829f58a670a087e7c5b63f8183c6b72d64a1444e420c212250b7326b69a9183", + "sha256:803831f427ca6c0452ae4fb898d731cfc19906466a33a88cbc1076abcbfcbba7" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==5.0.24" + "version": "==5.0.26" }, "mypy": { "hashes": [ - "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc", - "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", - "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f", - "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74", - "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a", - "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", - "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", - "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", - "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", - "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", - "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d", - "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6", - "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", - "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", - "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", - "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", - "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a", - "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc", - "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7", - "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb", - "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", - "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732", - "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80", - "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", - "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", - "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", - "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", - "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24", - "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", - "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b", - "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", - "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8" + "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", + "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", + "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", + "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", + "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", + "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", + "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", + "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", + "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319", + "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", + "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", + "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", + "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", + "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", + "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31", + "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", + "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", + "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", + "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", + "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", + "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837", + "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6", + "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", + "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", + "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", + "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", + "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", + "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", + "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b", + "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac", + "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", + "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", + "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", + "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", + "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", + "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", + "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", + "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.13.0" + "version": "==1.14.1" }, "mypy-extensions": { "hashes": [ @@ -978,28 +971,28 @@ }, "ruff": { "hashes": [ - "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8", - "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae", - "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", - "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60", - "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296", - "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf", - "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8", - "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", - "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7", - "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f", - "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111", - "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e", - "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3", - "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643", - "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604", - "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720", - "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d", - "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac" + "sha256:0509e8da430228236a18a677fcdb0c1f102dd26d5520f71f79b094963322ed25", + "sha256:0c000a471d519b3e6cfc9c6680025d923b4ca140ce3e4612d1a2ef58e11f11fe", + "sha256:248b1fb3f739d01d528cc50b35ee9c4812aa58cc5935998e776bf8ed5b251e75", + "sha256:45a56f61b24682f6f6709636949ae8cc82ae229d8d773b4c76c09ec83964a95a", + "sha256:496dd38a53aa173481a7d8866bcd6451bd934d06976a2505028a50583e001b76", + "sha256:52d587092ab8df308635762386f45f4638badb0866355b2b86760f6d3c076188", + "sha256:54799ca3d67ae5e0b7a7ac234baa657a9c1784b48ec954a094da7c206e0365b1", + "sha256:61323159cf21bc3897674e5adb27cd9e7700bab6b84de40d7be28c3d46dc67cf", + "sha256:7ae4478b1471fc0c44ed52a6fb787e641a2ac58b1c1f91763bafbc2faddc5117", + "sha256:7d7fc2377a04b6e04ffe588caad613d0c460eb2ecba4c0ccbbfe2bc973cbc162", + "sha256:91a7ddb221779871cf226100e677b5ea38c2d54e9e2c8ed847450ebbdf99b32d", + "sha256:9257aa841e9e8d9b727423086f0fa9a86b6b420fbf4bf9e1465d1250ce8e4d8d", + "sha256:bc3c083c50390cf69e7e1b5a5a7303898966be973664ec0c4a4acea82c1d4315", + "sha256:dcad24b81b62650b0eb8814f576fc65cfee8674772a6e24c9b747911801eeaa5", + "sha256:defed167955d42c68b407e8f2e6f56ba52520e790aba4ca707a9c88619e580e3", + "sha256:e169ea1b9eae61c99b257dc83b9ee6c76f89042752cb2d83486a7d6e48e8f764", + "sha256:e88b8f6d901477c41559ba540beeb5a671e14cd29ebd5683903572f4b40a9807", + "sha256:f1d70bef3d16fdc897ee290d7d20da3cbe4e26349f62e8a0274e7a3f4ce7a905" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.8.4" + "version": "==0.8.6" }, "s3transfer": { "hashes": [ @@ -1027,19 +1020,19 @@ }, "urllib3": { "hashes": [ - "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", - "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" + "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", + "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" ], - "markers": "python_version >= '3.8'", - "version": "==2.2.3" + "markers": "python_version >= '3.9'", + "version": "==2.3.0" }, "virtualenv": { "hashes": [ - "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0", - "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa" + "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb", + "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329" ], "markers": "python_version >= '3.8'", - "version": "==20.28.0" + "version": "==20.28.1" }, "werkzeug": { "hashes": [ diff --git a/dsc/cli.py b/dsc/cli.py index 2c79328..1126e7b 100644 --- a/dsc/cli.py +++ b/dsc/cli.py @@ -6,27 +6,83 @@ import click from dsc.config import Config +from dsc.workflows.base import BaseWorkflow logger = logging.getLogger(__name__) CONFIG = Config() -@click.command() +@click.group() +@click.pass_context +@click.option( + "-w", + "--workflow-name", + help="The workflow to use for the batch of DSpace submissions", + required=True, +) +@click.option( + "-c", + "--collection-handle", + help="The handle of the DSpace collection to which the batch will be submitted", + required=True, +) +@click.option( + "-b", + "--batch-id", + help="The S3 prefix for the batch of DSpace submissions", + required=True, +) @click.option( "-v", "--verbose", is_flag=True, help="Pass to log at debug level instead of info" ) -def main(*, verbose: bool) -> None: - start_time = perf_counter() +def main( + ctx: click.Context, + workflow_name: str, + collection_handle: str, + batch_id: str, + verbose: bool, # noqa: FBT001 +) -> None: + ctx.ensure_object(dict) + ctx.obj["start_time"] = perf_counter() + + ctx.obj["workflow"] = BaseWorkflow.load(workflow_name, collection_handle, batch_id) + stream = StringIO() root_logger = logging.getLogger() logger.info(CONFIG.configure_logger(root_logger, stream, verbose=verbose)) logger.info(CONFIG.configure_sentry()) CONFIG.check_required_env_vars() + ctx.obj["stream"] = stream + logger.info("Running process") - # Do things here! - elapsed_time = perf_counter() - start_time +@main.result_callback() +@click.pass_context +def post_main_group_subcommand( + ctx: click.Context, + *_args: tuple, + **_kwargs: dict, +) -> None: + """Callback for any work to perform after a main sub-command completes.""" logger.info( - "Total time to complete process: %s", str(timedelta(seconds=elapsed_time)) + "Total time elapsed: %s", + str( + timedelta(seconds=perf_counter() - ctx.obj["start_time"]), + ), ) + + +@main.command() +@click.pass_context +def reconcile(ctx: click.Context) -> None: + """Reconcile bitstreams with item identifiers from the metadata.""" + workflow = ctx.obj["workflow"] + no_bitstreams, no_item_identifiers = workflow.reconcile_bitstreams_and_metadata() + + if no_bitstreams: + logger.error(f"No bitstreams found for these item identifiers: {no_bitstreams}") + if no_item_identifiers: + logger.error( + f"No item identifiers found for these bitstreams: {no_item_identifiers}" + ) diff --git a/dsc/exceptions.py b/dsc/exceptions.py index caed71d..f5f8ee6 100644 --- a/dsc/exceptions.py +++ b/dsc/exceptions.py @@ -6,5 +6,9 @@ class InvalidSQSMessageError(Exception): pass +class InvalidWorkflowNameError(Exception): + pass + + class ItemMetadatMissingRequiredFieldError(Exception): pass diff --git a/dsc/utilities/__init__.py b/dsc/utilities/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dsc/workflows/base/__init__.py b/dsc/workflows/base/__init__.py index 511271a..566db5a 100644 --- a/dsc/workflows/base/__init__.py +++ b/dsc/workflows/base/__init__.py @@ -1,16 +1,21 @@ from __future__ import annotations +import json import logging from abc import ABC, abstractmethod +from collections import defaultdict from typing import TYPE_CHECKING, Any, final from dsc.exceptions import ( InvalidDSpaceMetadataError, + InvalidWorkflowNameError, ItemMetadatMissingRequiredFieldError, ) from dsc.item_submission import ItemSubmission +from dsc.utilities.aws.s3 import S3Client if TYPE_CHECKING: + from _collections_abc import dict_keys from collections.abc import Iterator from mypy_boto3_sqs.type_defs import SendMessageResultTypeDef @@ -21,51 +26,163 @@ class BaseWorkflow(ABC): """A base workflow class from which other workflow classes are derived.""" + workflow_name: str = "base" + submission_system: str = "DSpace@MIT" + email_recipients: tuple[str] = ("None",) + metadata_mapping_path: str = "" + s3_bucket: str = "" + output_queue: str = "" + def __init__( self, - workflow_name: str, - submission_system: str, - email_recipients: list[str], - metadata_mapping: dict, - s3_bucket: str, - batch_id: str, collection_handle: str, - output_queue: str, + batch_id: str, ) -> None: """Initialize base instance. Args: - workflow_name: The name of the workflow. - submission_system: The system to which item submissions will be sent - (e.g. DSpace@MIT). - email_recipients: The email addresses to notify after runs of - the workflow. - metadata_mapping: A mapping file for generating DSpace metadata - from the workflow's source metadata. - s3_bucket: The S3 bucket containing bitstream and metadata files for - the workflow. + collection_handle: The handle of the DSpace collection to which + submissions will be uploaded. batch_id: Unique identifier for a 'batch' deposit that corresponds to the name of a subfolder in the workflow directory of the S3 bucket. This subfolder is where the S3 client will search for bitstream and metadata files. - collection_handle: The handle of the DSpace collection to which - submissions will be uploaded. - output_queue: The SQS output queue used for retrieving result messages - from the workflow's submissions. """ - self.workflow_name: str = workflow_name - self.submission_system: str = submission_system - self.email_recipients: list[str] = email_recipients - self.metadata_mapping: dict = metadata_mapping - self.s3_bucket: str = s3_bucket - self.batch_id: str = batch_id - self.collection_handle: str = collection_handle - self.output_queue: str = output_queue + self.batch_id = batch_id + self.collection_handle = collection_handle @property def batch_path(self) -> str: return f"{self.workflow_name}/{self.batch_id}" + @property + def metadata_mapping(self) -> dict: + with open(self.metadata_mapping_path) as mapping_file: + return json.load(mapping_file) + + @final + @classmethod + def load( + cls, workflow_name: str, collection_handle: str, batch_id: str + ) -> BaseWorkflow: + """Return configured workflow class instance. + + Args: + workflow_name: The label of the workflow. Must match a key from + config.WORKFLOWS. + collection_handle: The handle of the DSpace collection to which the batch will + be submitted. + batch_id: The S3 prefix for the batch of DSpace submissions. + """ + workflow_class = cls.get_workflow(workflow_name) + return workflow_class( + collection_handle=collection_handle, + batch_id=batch_id, + ) + + @final + @classmethod + def get_workflow(cls, workflow_name: str) -> type[BaseWorkflow]: + """Return workflow class. + + Args: + workflow_name: The label of the workflow. Must match a workflow_name attribute + from BaseWorkflow subclass. + """ + for workflow_class in BaseWorkflow.__subclasses__(): + if workflow_name == workflow_class.workflow_name: + return workflow_class + raise InvalidWorkflowNameError(f"Invalid workflow name: {workflow_name} ") + + def reconcile_bitstreams_and_metadata(self) -> tuple[set[str], set[str]]: + """Reconcile bitstreams against metadata. + + Generate a list of bitstreams without item identifiers and item identifiers + without bitstreams. Any discrepancies will be addressed by the engineer and + stakeholders as necessary. + """ + bitstream_dict = self._build_bitstream_dict() + + # extract item identifiers from batch metadata + item_identifiers = [ + self.get_item_identifier(item_metadata) + for item_metadata in self.item_metadata_iter() + ] + + # reconcile item identifiers against bitstreams + item_identifiers_with_bitstreams = self._match_item_identifiers_to_bitstreams( + bitstream_dict.keys(), item_identifiers + ) + + bitstreams_with_item_identifiers = self._match_bitstreams_to_item_identifiers( + bitstream_dict.keys(), item_identifiers + ) + + logger.info( + "Item identifiers from batch metadata with matching bitstreams: " + f"{item_identifiers_with_bitstreams}" + ) + + item_identifiers_without_bitstreams = set(item_identifiers) - set( + item_identifiers_with_bitstreams + ) + bitstreams_without_item_identifiers = set(bitstream_dict.keys()) - set( + bitstreams_with_item_identifiers + ) + + return item_identifiers_without_bitstreams, bitstreams_without_item_identifiers + + def _build_bitstream_dict(self) -> dict: + """Build a dict of potential bitstreams with an item identifier for the key. + + An underscore (if present) serves as the delimiter between the item identifier + and any additional suffixes in the case of multiple matching bitstreams. + """ + s3_client = S3Client() + bitstreams = list( + s3_client.files_iter(bucket=self.s3_bucket, prefix=self.batch_path) + ) + bitstream_dict: dict[str, list[str]] = defaultdict(list) + for bitstream in bitstreams: + file_name = bitstream.split("/")[-1] + item_identifier = file_name.split("_")[0] if "_" in file_name else file_name + bitstream_dict[item_identifier].append(bitstream) + return bitstream_dict + + def _match_bitstreams_to_item_identifiers( + self, bitstreams: dict_keys, item_identifiers: list[str] + ) -> list[str]: + """Create list of bitstreams matched to item identifiers. + + Args: + bitstreams: A dict of S3 files with base file IDs and full URIs. + item_identifiers: A list of item identifiers retrieved from the batch + metadata. + """ + return [ + file_id + for item_identifier in item_identifiers + for file_id in bitstreams + if file_id == item_identifier + ] + + def _match_item_identifiers_to_bitstreams( + self, bitstreams: dict_keys, item_identifiers: list[str] + ) -> list[str]: + """Create list of item identifers matched to bitstreams. + + Args: + bitstreams: A dict of S3 files with base file IDs and full URIs. + item_identifiers: A list of item identifiers retrieved from the batch + metadata. + """ + return [ + item_identifier + for file_id in bitstreams + for item_identifier in item_identifiers + if file_id == item_identifier + ] + @final def run(self) -> Iterator[SendMessageResultTypeDef]: """Run workflow to submit items to the DSpace Submission Service.""" diff --git a/dsc/workflows/base/simple_csv.py b/dsc/workflows/base/simple_csv.py index eb0dad2..4f9f0d8 100644 --- a/dsc/workflows/base/simple_csv.py +++ b/dsc/workflows/base/simple_csv.py @@ -18,6 +18,8 @@ class SimpleCSV(BaseWorkflow): deposit on S3. """ + workflow_name: str = "simple_csv" + def item_metadata_iter( self, metadata_file: str = "metadata.csv" ) -> Iterator[dict[str, Any]]: diff --git a/tests/conftest.py b/tests/conftest.py index ebe9f95..c4e3257 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,6 +16,54 @@ from dsc.workflows.base.simple_csv import SimpleCSV +class TestBaseWorkflow(BaseWorkflow): + + workflow_name: str = "test" + submission_system: str = "Test@MIT" + email_recipients: tuple[str] = ("test@test.test",) + metadata_mapping_path: str = "tests/fixtures/test_metadata_mapping.json" + s3_bucket: str = "dsc" + output_queue: str = "mock-output_queue" + + def item_metadata_iter(self): + yield from [ + { + "title": "Title", + "contributor": "Author 1|Author 2", + "item_identifier": "123", + }, + { + "title": "2nd Title", + "contributor": "Author 3|Author 4", + "item_identifier": "789", + }, + ] + + def get_item_identifier(self, item_metadata): + return item_metadata["item_identifier"] + + def get_bitstream_s3_uris(self, item_identifier): + bitstreams = [ + "s3://dsc/test/batch-aaa/123_01.pdf", + "s3://dsc/test/batch-aaa/123_02.pdf", + "s3://dsc/test/batch-aaa/456_01.pdf", + ] + return [bitstream for bitstream in bitstreams if item_identifier in bitstream] + + def process_deposit_results(self): + pass + + +class TestSimpleCSV(SimpleCSV): + + workflow_name: str = "simple_csv" + submission_system: str = "Test@MIT" + email_recipients: tuple[str] = ("test@test.test",) + metadata_mapping_path: str = "tests/fixtures/test_metadata_mapping.json" + s3_bucket: str = "dsc" + output_queue: str = "mock-output_queue" + + @pytest.fixture(autouse=True) def _test_env(monkeypatch): monkeypatch.setenv("SENTRY_DSN", "None") @@ -29,48 +77,17 @@ def _test_env(monkeypatch): @pytest.fixture def base_workflow_instance(item_metadata, metadata_mapping, mocked_s3): - class TestBaseWorkflow(BaseWorkflow): - - def item_metadata_iter(self): - yield from [item_metadata] - - def get_item_identifier(self, item_metadata): - return item_metadata["item_identifier"] - - def get_bitstream_s3_uris(self, item_identifier): - bitstreams = [ - "s3://dsc/base/batch-aaa/123_01.pdf", - "s3://dsc/base/batch-aaa/123_02.pdf", - "s3://dsc/base/batch-aaa/456_01.pdf", - ] - return [bitstream for bitstream in bitstreams if item_identifier in bitstream] - - def process_deposit_results(self): - pass - return TestBaseWorkflow( - workflow_name="base", - submission_system="Test@MIT", - email_recipients=["test@test.test"], - metadata_mapping=metadata_mapping, - s3_bucket="dsc", - batch_id="batch-aaa", collection_handle="123.4/5678", - output_queue="mock-output_queue", + batch_id="batch-aaa", ) @pytest.fixture -def simple_csv_workflow_instance(): - return SimpleCSV( - workflow_name="simple_csv", - submission_system="Test@MIT", - email_recipients=["test@test.test"], - metadata_mapping=metadata_mapping, - s3_bucket="dsc", - batch_id="batch-aaa", +def simple_csv_workflow_instance(metadata_mapping): + return TestSimpleCSV( collection_handle="123.4/5678", - output_queue="mock-output_queue", + batch_id="batch-aaa", ) @@ -125,25 +142,8 @@ def item_submission_instance(dspace_metadata): @pytest.fixture def metadata_mapping(): - return { - "item_identifier": { - "source_field_name": "item_identifier", - "language": None, - "delimiter": "", - "required": True, - }, - "dc.title": { - "source_field_name": "title", - "language": "en_US", - "delimiter": "", - "required": True, - }, - "dc.contributor": { - "source_field_name": "contributor", - "language": None, - "delimiter": "|", - }, - } + with open("tests/fixtures/test_metadata_mapping.json") as mapping_file: + return json.load(mapping_file) @pytest.fixture diff --git a/tests/fixtures/test_metadata_mapping.json b/tests/fixtures/test_metadata_mapping.json new file mode 100644 index 0000000..0b3e3dd --- /dev/null +++ b/tests/fixtures/test_metadata_mapping.json @@ -0,0 +1,19 @@ +{ + "item_identifier": { + "source_field_name": "item_identifier", + "language": null, + "delimiter": "", + "required": true + }, + "dc.title": { + "source_field_name": "title", + "language": "en_US", + "delimiter": "", + "required": true + }, + "dc.contributor": { + "source_field_name": "contributor", + "language": null, + "delimiter": "|" + } +} \ No newline at end of file diff --git a/tests/test_base_workflow.py b/tests/test_base_workflow.py index 51a28a4..5ba28a1 100644 --- a/tests/test_base_workflow.py +++ b/tests/test_base_workflow.py @@ -4,11 +4,95 @@ from dsc.exceptions import ( InvalidDSpaceMetadataError, + InvalidWorkflowNameError, ItemMetadatMissingRequiredFieldError, ) from dsc.item_submission import ItemSubmission +def test_base_workflow_load_success(base_workflow_instance): + workflow_instance = base_workflow_instance.load( + workflow_name="test", + collection_handle="123.4/5678", + batch_id="batch-aaa", + ) + assert workflow_instance.workflow_name == "test" + assert workflow_instance.submission_system == "Test@MIT" + assert workflow_instance.email_recipients == ("test@test.test",) + assert ( + workflow_instance.metadata_mapping_path + == "tests/fixtures/test_metadata_mapping.json" + ) + assert workflow_instance.s3_bucket == "dsc" + assert workflow_instance.output_queue == "mock-output_queue" + assert workflow_instance.collection_handle == "123.4/5678" + assert workflow_instance.batch_id == "batch-aaa" + + +def test_base_workflow_get_workflow_success(base_workflow_instance): + workflow_class = base_workflow_instance.get_workflow("test") + assert workflow_class.workflow_name == "test" + + +def test_base_workflow_get_workflow_invalid_workflow_name_raises_error( + base_workflow_instance, +): + with pytest.raises(InvalidWorkflowNameError): + base_workflow_instance.get_workflow("tast") + + +def test_base_workflow_reconcile_bitstreams_and_metadata_success( + caplog, base_workflow_instance, mocked_s3, s3_client +): + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/123_01.pdf") + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/123_02.jpg") + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/456_01.pdf") + assert base_workflow_instance.reconcile_bitstreams_and_metadata() == ( + {"789"}, + {"456"}, + ) + assert ( + "Item identifiers from batch metadata with matching bitstreams: ['123']" + in caplog.text + ) + + +def test_build_bitstream_dict_success(mocked_s3, s3_client, base_workflow_instance): + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/123_01.pdf") + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/123_02.pdf") + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/456_01.pdf") + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/789_01.jpg") + assert base_workflow_instance._build_bitstream_dict() == { # noqa: SLF001 + "123": ["test/batch-aaa/123_01.pdf", "test/batch-aaa/123_02.pdf"], + "456": ["test/batch-aaa/456_01.pdf"], + "789": ["test/batch-aaa/789_01.jpg"], + } + + +def test_match_item_identifiers_to_bitstreams_success(base_workflow_instance): + bitstream_dict = {"test": "test_01.pdf"} + item_identifiers = ["test", "tast"] + item_identifier_matches = ( + base_workflow_instance._match_item_identifiers_to_bitstreams( # noqa: SLF001 + bitstream_dict.keys(), item_identifiers + ) + ) + assert len(item_identifier_matches) == 1 + assert "test" in item_identifier_matches + + +def test_match_bitstreams_to_item_identifiers_success(base_workflow_instance): + bitstream_dict = {"test": "test_01.pdf", "tast": "tast_01.pdf"} + item_identifiers = ["test"] + file_matches = ( + base_workflow_instance._match_bitstreams_to_item_identifiers( # noqa: SLF001 + bitstream_dict, item_identifiers + ) + ) + assert len(file_matches) == 1 + assert "test" in file_matches + + def test_base_workflow_run_success( caplog, base_workflow_instance, mocked_s3, mocked_sqs_input, mocked_sqs_output ): @@ -16,7 +100,7 @@ def test_base_workflow_run_success( response = next(base_workflow_instance.run()) assert "Processing submission for '123'" in caplog.text assert ( - "Metadata uploaded to S3: s3://dsc/base/batch-aaa/123_metadata.json" + "Metadata uploaded to S3: s3://dsc/test/batch-aaa/123_metadata.json" in caplog.text ) assert response["ResponseMetadata"]["HTTPStatusCode"] == HTTPStatus.OK @@ -32,8 +116,8 @@ def test_base_workflow_item_submission_iter_success(base_workflow_instance): ] }, bitstream_s3_uris=[ - "s3://dsc/base/batch-aaa/123_01.pdf", - "s3://dsc/base/batch-aaa/123_02.pdf", + "s3://dsc/test/batch-aaa/123_01.pdf", + "s3://dsc/test/batch-aaa/123_02.pdf", ], item_identifier="123", ) diff --git a/tests/test_cli.py b/tests/test_cli.py index 8609448..42a577b 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,17 +1,28 @@ from dsc.cli import main -def test_cli_no_options(caplog, runner): - result = runner.invoke(main) +def test_reconcile_success(caplog, runner, mocked_s3, base_workflow_instance, s3_client): + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/123_01.pdf") + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/123_02.jpg") + s3_client.put_file(file_content="", bucket="dsc", key="test/batch-aaa/456_01.pdf") + result = runner.invoke( + main, + [ + "--workflow-name", + "test", + "--collection-handle", + "123.4/5678", + "--batch-id", + "batch-aaa", + "reconcile", + ], + ) + assert result.output == "" assert result.exit_code == 0 - assert "Logger 'root' configured with level=INFO" in caplog.text - assert "Running process" in caplog.text - assert "Total time to complete process" in caplog.text - - -def test_cli_all_options(caplog, runner): - result = runner.invoke(main, ["--verbose"]) - assert result.exit_code == 0 - assert "Logger 'root' configured with level=DEBUG" in caplog.text - assert "Running process" in caplog.text - assert "Total time to complete process" in caplog.text + assert ( + "Item identifiers from batch metadata with matching bitstreams: ['123']" + in caplog.text + ) + assert "No bitstreams found for these item identifiers: {'789'}" in caplog.text + assert "No item identifiers found for these bitstreams: {'456'}" in caplog.text + assert "Total time elapsed" in caplog.text