asyncio style refactoring

This commit is contained in:
2026-08-05 14:46:10 +03:00
parent 2da58c7483
commit 5ec033a21d
16 changed files with 280 additions and 237 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ app.include_router(router=protected_user_action_routes)
@app.get("") @app.get("")
def root()->dict: async def root()->dict:
return {"root":"hello, this is root"} return {"root":"hello, this is root"}
Generated
+116 -82
View File
@@ -1,5 +1,21 @@
# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. # This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand.
[[package]]
name = "aiosqlite"
version = "0.22.1"
description = "asyncio bridge to the standard sqlite3 module"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "aiosqlite-0.22.1-py3-none-any.whl", hash = "sha256:21c002eb13823fad740196c5a2e9d8e62f6243bd9e7e4a1f87fb5e44ecb4fceb"},
{file = "aiosqlite-0.22.1.tar.gz", hash = "sha256:043e0bd78d32888c0a9ca90fc788b38796843360c855a7262a532813133a0650"},
]
[package.extras]
dev = ["attribution (==1.8.0)", "black (==25.11.0)", "build (>=1.2)", "coverage[toml] (==7.10.7)", "flake8 (==7.3.0)", "flake8-bugbear (==24.12.12)", "flit (==3.12.0)", "mypy (==1.19.0)", "ufmt (==2.8.0)", "usort (==1.0.8.post1)"]
docs = ["sphinx (==8.1.3)", "sphinx-mdinclude (==0.6.2)"]
[[package]] [[package]]
name = "alembic" name = "alembic"
version = "1.18.5" version = "1.18.5"
@@ -567,92 +583,91 @@ standard-no-fastapi-cloud-cli = ["email-validator (>=2.0.0)", "fastapi-cli[stand
[[package]] [[package]]
name = "greenlet" name = "greenlet"
version = "3.5.3" version = "3.5.4"
description = "Lightweight in-process concurrent programming" description = "Lightweight in-process concurrent programming"
optional = false optional = false
python-versions = ">=3.10" python-versions = ">=3.10"
groups = ["main"] groups = ["main"]
markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""
files = [ files = [
{file = "greenlet-3.5.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c180d22d325fb613956b443c3c6f4406eb70e6defc70d3974da2a7b59e06f48c"}, {file = "greenlet-3.5.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:ac5bf81d79d2c8eeb2ef6359b2e1687a1e9ebf46c2b1f970da9a9255df51d190"},
{file = "greenlet-3.5.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:483d08c11181c83a6ce1a7a61df0f624a208ec40817a3bb2302714592eee4f04"}, {file = "greenlet-3.5.4-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89f3738167bab8c1084b94e23023d41d247117ac149fa0fbcb5bd4cf6262b353"},
{file = "greenlet-3.5.3-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1dae6e0091eae084317e411f047f0b7cb241c6db570f7c45fd6b900a274914ce"}, {file = "greenlet-3.5.4-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e9a5e3406e3ed8125ae1a3b37c12f3434e2b1f0fa053197c5557895b4fb09606"},
{file = "greenlet-3.5.3-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f6ff50ff8dbd51fae9b37f4101648b04ea0df19b3f50ab2beb5061e7716a5c8"}, {file = "greenlet-3.5.4-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a2d614cb2372c7101a12ea8b96dd56f81c986d247c5a73db67063f3ed1ca4a52"},
{file = "greenlet-3.5.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bcd2d72ccd70a1ec68ba6ef93e7fbb4420ef9997dabc7010d893bd4015e0bec"}, {file = "greenlet-3.5.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ab9f0704bccf6d3b38e0d2130b7b33271cff11453690da074fa280c3aa8e8e7"},
{file = "greenlet-3.5.3-cp310-cp310-manylinux_2_39_riscv64.whl", hash = "sha256:37bf9c538f5ae6e63d643f88dec37c0c83bdf0e2ebc62961dedcf458822f7b71"}, {file = "greenlet-3.5.4-cp310-cp310-manylinux_2_39_riscv64.whl", hash = "sha256:188e4d142f243051d92a1f5c244a741da02dddc070a0620c842804d7b56d008c"},
{file = "greenlet-3.5.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:73f152c895e09907e0dbe24f6c2db37beb085cd63db91c3825a0fcd0064124a8"}, {file = "greenlet-3.5.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2cdaadc3d31445a8f782bde3cd37e49a2c2a9c6da6daf76a3e34c683b271a3c7"},
{file = "greenlet-3.5.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8bdb43e1a1d1873721acab2be99c5befd4d2044ddfd52e4d610801019880a702"}, {file = "greenlet-3.5.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:70bdfacdc183dac838b2a0aaff2dd6134a457c52fe68a9c6bbab435483d2b9df"},
{file = "greenlet-3.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:0909f9355a9f24845d3299f3112e266a06afb68302041989fd26bd68894933db"}, {file = "greenlet-3.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:69173331fbc5d64bfac0065d7e22c39cfcd089e9b18d125bdcd5079363b09616"},
{file = "greenlet-3.5.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:aca9b4ce85b152b5524ef7d88170efdff80dc0032aa8b75f9aaf7f3479ea95b4"}, {file = "greenlet-3.5.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e883de250e299654b1f1680f72a1a9f9ba62c9bd1bce84099c90657349a8dfbb"},
{file = "greenlet-3.5.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f71be4920368fe1fabeeaa53d1e3548337e2b223d9565f8ad5e392a75ba23fc"}, {file = "greenlet-3.5.4-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32802705c2c1ff25e8237b3bdacf2594fa02be80af8a66703eb7853ea7e68686"},
{file = "greenlet-3.5.3-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d77e67f65f98449e3fb83f795b5d0a8437aead2f874ca89c96576caf4be3af6"}, {file = "greenlet-3.5.4-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57aa201b351f7c7c75627c60d29e4d5b97a07d37efeb62b903466fca42c097d7"},
{file = "greenlet-3.5.3-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e18619ba655ac05d78d80fc83cac4ba892bd6927b99e3b8237aee861aaacc8bb"}, {file = "greenlet-3.5.4-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9667862a2e38ad379f11b845daeda22c8989186def44f06962c9c4c05e556da7"},
{file = "greenlet-3.5.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8540f1e6205bd13ca0ce685581037219ca54a1b41a0a15d228c6c9b8ad5903d7"}, {file = "greenlet-3.5.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c3fe76c2cac86b4f7a1e92865ac0a54384deb05c92986287c1a7110d9bd53071"},
{file = "greenlet-3.5.3-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:d27c0c653a60d9535f690226474a5cc1036a8b0d7b57504d1c4f89c44a07a80c"}, {file = "greenlet-3.5.4-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:ae53534b5dec0f4c2ec26f898f538dc8ea1ca3ef2927d597a9439e40a09da937"},
{file = "greenlet-3.5.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ef56fe650f50575bf843acde967b9c567687f3c22340941a899b7bc56e956a8"}, {file = "greenlet-3.5.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1e1a4a684b16c45ba324e60b32a4386a87722bcb815d2a149d2182f9b401ca72"},
{file = "greenlet-3.5.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5121af01cf911e70056c00d4b46d5e9b5d1415550038573d744138bacb59e6b8"}, {file = "greenlet-3.5.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e849e6e139b9671adeac505f72fc05f4af7fd1921faef40295e214fc3b361b59"},
{file = "greenlet-3.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:0f41e4a05a3c0cb31b17023eff28dd111e1d16bf7d7d00406cd7df23f31398a7"}, {file = "greenlet-3.5.4-cp311-cp311-win_amd64.whl", hash = "sha256:dc418cf4c873357964d6624445ed09472e50def990c65dd4e76fc3ba8cd9cef6"},
{file = "greenlet-3.5.3-cp311-cp311-win_arm64.whl", hash = "sha256:ec6f1af59f6b5f3fc9678e2ea062d8377d22ac644f7844cb7a292910cf12ff44"}, {file = "greenlet-3.5.4-cp311-cp311-win_arm64.whl", hash = "sha256:c38c902a0986eba1f6e7ba1ab39ad5195926abde90f3fe080e08212db62176da"},
{file = "greenlet-3.5.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:719757059f5a53fd0dde23f78cffeafcdd97b21c850ddb7ca684a3c1a1f122e2"}, {file = "greenlet-3.5.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3d66250e8b09f182ede05490998c818b5961f7a3640332d44c4927caec7bbfe4"},
{file = "greenlet-3.5.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:efa9f765dd09f9d0cdac651ffdf631ee59ec5dc6ee7a73e0c012ba9c52fbdf5b"}, {file = "greenlet-3.5.4-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c90e930c9c192e5b3ee9fb8bcd920ea3926155e2e3ded39fc697323addecee17"},
{file = "greenlet-3.5.3-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7faba15ac005376e02a0384504e0243be3370ce010296a44a820feb342b505ab"}, {file = "greenlet-3.5.4-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:791fdfeeb9c6e0c7b10fa151bf110d2a6974866f13dcb5b1c7efae698245893a"},
{file = "greenlet-3.5.3-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5795cd1101371140551c645f2d408b8d3c01a5a29cf8a9bce6e759c983682d23"}, {file = "greenlet-3.5.4-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b7c895310363f310361e0fe2072af85269d2a2a285cd04c0c59e79a5e3670dcf"},
{file = "greenlet-3.5.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:87142215824be6ac05e2e8e2786eec307ccbc27c36723c3881959df654af6861"}, {file = "greenlet-3.5.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f88193799d43dbf8c8a806d6405c9c52fe2af40bf75072a606357b33cc336c7f"},
{file = "greenlet-3.5.3-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:af4923b3096e26a36d7e9cf24ab88083a20f97d191e3b97f253731ce9b41b28c"}, {file = "greenlet-3.5.4-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:13b980043cb1b3134e81ea469da1250ddcc6bfe6d245bbaa59168d9cdc8f228f"},
{file = "greenlet-3.5.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215275b1b49320987352e6c1b054acca0064f965a2c66992bed9a6f7d913f149"}, {file = "greenlet-3.5.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b7a5f095767c4493afcd06067f2bb3b8716e3f3f9e92b99c88e7e99f885b3d4d"},
{file = "greenlet-3.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6b1b0eed82364b0e32c4ea0f221452d33e6bb17ae094d9f72aed9851812747ea"}, {file = "greenlet-3.5.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:42afdc1ab5f66da8c586c32af9224a74a706b4f0ea0dc3a4188a0860a09c65c9"},
{file = "greenlet-3.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:cde8adafa2365676f74a979744629589999093bc86e2484214f58e61df08902c"}, {file = "greenlet-3.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:60149df8f462d1b230038e6590c23c3b4768bb5d6c022b3b6e82532b34b0b8a3"},
{file = "greenlet-3.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:c4e7b79d83805475f0102008843f6eb45fd3bb0b2e88c774adab5fbaab27117d"}, {file = "greenlet-3.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:77d6ce04fed0d9aeed42e0f37923cc43eba9b027bdd9c34546bb4ccd143d0fe0"},
{file = "greenlet-3.5.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c8d87c2134d871df96ecdea9cec7cbaab286dadab0f56476e57aaf9e8ac11550"}, {file = "greenlet-3.5.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:24e61b88cb7e1b1d794b32a10cc346ac779681d6d74ff137a3e0a444d2bf1f02"},
{file = "greenlet-3.5.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2d185dd1621757e70c3861cceffd5317ab4e7ed7eb09c82994828468527ade5"}, {file = "greenlet-3.5.4-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:870d730fec833f5a06906a32596cc099b9161594642a92a520b7a88911c95356"},
{file = "greenlet-3.5.3-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1c514a468149bf8fbbab874188a3535cd8a48a3e353eb53a3d424296f8dbacd3"}, {file = "greenlet-3.5.4-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec5ff0d1878df6af3bf9b638a5a92a7d5693291de77c91bff10fa48519c604ef"},
{file = "greenlet-3.5.3-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9ad04dd75458c6300b047c61b8639092433d205a25a14e310d6582a480efcca1"}, {file = "greenlet-3.5.4-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07bd44616608d873d06735b63ef1a88191d6ca57c8d291d6559c71bc14c0893c"},
{file = "greenlet-3.5.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:915f887cf2682b66419b879423a2e072634aa7b7dce6f3ada4957cfced3f1e9a"}, {file = "greenlet-3.5.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d84d993f6e575c950d91a23c1345d18fe1a4310d447bf630849d7809196b52f0"},
{file = "greenlet-3.5.3-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:afaabdd554cd7ae9bbb3ca070b0d7fdfd207dbf1d16865f7233837709d354bda"}, {file = "greenlet-3.5.4-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:3529a8a933582ad19e224792cac7372489526576b75b4c124e8e4f29948f4861"},
{file = "greenlet-3.5.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:766cfd421c13e450feb340cd472a3ed9957d438727b7b4593ad7c76c5d2b0deb"}, {file = "greenlet-3.5.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:58023945f421093de5e6fa108c0985a8659d43f49e0216da25099369a121bcbd"},
{file = "greenlet-3.5.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2ecda9ec22edf38fa389369eaed8c3d37c05f3c54e69f69438dbb2cc1de1458b"}, {file = "greenlet-3.5.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bae2728e1897aa8df8cb1af38cd48b3a743aefe29372de7b8b7a9f532501e69f"},
{file = "greenlet-3.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:c82304750f057167ff60d188df1d0cc1764ce9567eadf03e6a7443bcedd0b30b"}, {file = "greenlet-3.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:ca5726c0b08ca35ae873557266a78b2c3f3b2b7d7401aa5ff886c2045dd0111c"},
{file = "greenlet-3.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:dc133a1569ee667b2a6ef56ce551084aeefd87a5acbc4736d336d1e2edc6cfc4"}, {file = "greenlet-3.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:7c1303791d603080cac6fc3b34df51c3b75b723739c282c8029e48a0d241672f"},
{file = "greenlet-3.5.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:fd2e02fa07485778536a036222d616ab957b1d533f36b3ed98ce725d9c9d3117"}, {file = "greenlet-3.5.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:7e8afa5eac028f8140ceafe5ceec66e6aa127ddcb21452d2a564dcd2900b5f22"},
{file = "greenlet-3.5.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df0a0628d1597eb0897b62f55d1343f772405fd25f3b2a796c76874b0c2e22e8"}, {file = "greenlet-3.5.4-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73b37afe369021423ea53dd3123e04bffa7e93ac64429b9f50835b2e4fcae7cf"},
{file = "greenlet-3.5.3-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ebd933a6adabc298bab47731a130fe6bfb888bd934eee37810f151159544540d"}, {file = "greenlet-3.5.4-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3ef964f56dfcb6f9bbef2a190d9126795eac408716aeae47b5e7c73c32aafca9"},
{file = "greenlet-3.5.3-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8d19fe6c39ebff9259f07bcc685d3290f8fa4ea2278e51dd0008e4d6b0f2d814"}, {file = "greenlet-3.5.4-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cef589bc65fae02d10bca2ac341191c5b33acc2967892ebf4fcbd10eabb7a74c"},
{file = "greenlet-3.5.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b9d501b40e80b70e32323c799dd9b420a5577a9601469d362ae1ffb690f3a7c"}, {file = "greenlet-3.5.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c53ff01a5c53a40f2c16820ebc56d7c61a77f5fbe009dadd96292d5682f80f8"},
{file = "greenlet-3.5.3-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:962c5df2db8cb446da51edf1ca5296c389d93b99c9d8aa2ee4c7d0d8f1218260"}, {file = "greenlet-3.5.4-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:dfc41ae893d9ceaf22c824f2153a88b30651b20e8758c2cd9ac143f23640563c"},
{file = "greenlet-3.5.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a1fad1d11e7d6aab184107baa8e4ece11ccba3ec9599cd7efa5ff4d70d43256a"}, {file = "greenlet-3.5.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecca4d80d55a01ad6b23b33262662956149fbb7b2c6be2910f1705921958cbf3"},
{file = "greenlet-3.5.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fad5aec764399f1b5cc347ad250a59660f20c8f8888ea6bae1f93b769cce1154"}, {file = "greenlet-3.5.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2ffbc533e0eaf8e80d8471411646ab88fe58f641d508c0b02b24494479f4d9ec"},
{file = "greenlet-3.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:7669aa24cf2a1041d6f7899575b494a3ab4cf68bfcc8609b1dc0be7272db835e"}, {file = "greenlet-3.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:305f69e6c4523d7f6979ed001cff4e5853c063e5da04880296603aa0227e544c"},
{file = "greenlet-3.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:5b4807c4082c9d1b6d9eed56fcd041863e37f2228106eef24c30ca096e238605"}, {file = "greenlet-3.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:f260930bbbbcf9caee661211235a5111c86dfe5832fdf6ae4570da1e0995320f"},
{file = "greenlet-3.5.3-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:271a8ea7c1024e8a0d7dd2be66dd66dda8a07193f41a17b9e924f7600f5b62be"}, {file = "greenlet-3.5.4-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:41ddab54e4b238f4a6c323f39b4e59e176affd5a94d461a9fb7583dac74240a3"},
{file = "greenlet-3.5.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19131729ae0ddc3c2e1ef85e650169b5e37ee32e400f215f78b94d7b0d567310"}, {file = "greenlet-3.5.4-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3dabe3e2809013052c68bdf0b7fa5f5f2859c43a80803131ad61af9cabd7867"},
{file = "greenlet-3.5.3-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1540dd8e5fc2a5aec40fbb98ef8e149fa47c89a4b4a1cf2575a14d3d1869d7a8"}, {file = "greenlet-3.5.4-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:27d3f00718634d4520a3a150154ac5da36f257869d41321953375b90bfbbc72c"},
{file = "greenlet-3.5.3-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b897d97759425953f69a9c0fac67f8fe333ec0ce7377ef186fb2b0c3ad5e354d"}, {file = "greenlet-3.5.4-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:39169a11d87a6a263afda3e9a27d1df16d0f919d40a4837cc73986c9884c0dd8"},
{file = "greenlet-3.5.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e81fa194a1d20967877bdf9c7794db2bc99063e5be36aee710c08f04c5bb087f"}, {file = "greenlet-3.5.4-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cbd60b5763c6543c1827e48faaf14ea9bfbad245f52b1a4d76a2a2d8884c6c66"},
{file = "greenlet-3.5.3-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:3236754d423955ea08e9bb5f6c04a7895f9e22c290b66aa7653fcb922d839eb0"}, {file = "greenlet-3.5.4-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:bd3d1145f603b2db19feb9078c2e6855eb7c67e15580c010ed815cee519b86fd"},
{file = "greenlet-3.5.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:55cf4d777485d43110e47133cbba6d74a8885a87ec1227ef0267f9ee80c5aa21"}, {file = "greenlet-3.5.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f00f910f0e7b35416c63b23ad78b769aeccfc1775f712b43c4ee525624a2eef7"},
{file = "greenlet-3.5.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:12a248ba75f6a9a236375f52296c498c89ff1d8badf32deb9eca7abd5853f7da"}, {file = "greenlet-3.5.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:91c26423753b92caf41ab3f98fd547d7374d4d9fc2d85be041886c1579d9255e"},
{file = "greenlet-3.5.3-cp314-cp314t-win_amd64.whl", hash = "sha256:efc6bd60ea02e085862c74a3ef64b147ffc6f1a5ea7d9f26e7a939943f68c1e3"}, {file = "greenlet-3.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:ee032b91fd8ec29ec6c4cea2b8c561b178435134bd0752c7334b94e9c736c132"},
{file = "greenlet-3.5.3-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:ea03f2f04367845d6b58eeed276e1e56e51f0b97d8ad5a88a7d20a91dc9056cc"}, {file = "greenlet-3.5.4-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:178111881dd7a6c946471fda85485ec796e1043c2b939f694b096e2ecf986809"},
{file = "greenlet-3.5.3-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78dbef602fda6d97d957eb7937f70c9ce9e9527330347f8f6b6f9e554a9e7a47"}, {file = "greenlet-3.5.4-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d92df08dd65fede97fc37aad36c2e9dcda3b31c467f8e0c2c096456cb818e927"},
{file = "greenlet-3.5.3-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f73857adb8fee13fa56c172bd11262f888c0c648f9fea113e777bb2c7904a81"}, {file = "greenlet-3.5.4-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99e8f8c4ebc4fd80aa26c1280ae9ad43a0976e786349703a181cf0bae60413e5"},
{file = "greenlet-3.5.3-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cefa9cef4b371f9844c6053db71f1138bc6807bab1578b0dae5149c1f1141357"}, {file = "greenlet-3.5.4-cp315-cp315-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1f17e362d78e37559e0506c5a7d066bdd45073c36a0127a543e8a0df27242ff3"},
{file = "greenlet-3.5.3-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:232fec92e823addaf02d9472cf7381e24a1d046a6ced1103c5caa4c21b9dfc1d"}, {file = "greenlet-3.5.4-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:394de08dad5ffcb1f50c2159d93e398d9d2da3ed437645eaa54771fa720db9f0"},
{file = "greenlet-3.5.3-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:6219b6d04dbf6ba6084d77dc609e8473060dc55f759cbf626d512122781fa128"}, {file = "greenlet-3.5.4-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:cd320d998cbaa032932830448e39abf3c6a12901295e386e8114db926e10cffb"},
{file = "greenlet-3.5.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:2421c3564da9429d5586d46ca31ebb26516b5498a802cf65c041a8e8a8980d34"}, {file = "greenlet-3.5.4-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:c883d61f2282d72c767a14936641b3efcbde9d82f1080712aaea0b1d3126cb88"},
{file = "greenlet-3.5.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e0f0d160f0b2e558e6c75f7930967183255dc9735e5f5b8cae58ee09c9576d8b"}, {file = "greenlet-3.5.4-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2a924f15d17957e252a810acefcb5942f5ca712298e8b6fcaed9a307d357522c"},
{file = "greenlet-3.5.3-cp315-cp315-win_amd64.whl", hash = "sha256:dd99329bbc15ca78dcc583dba05d0b1b0bae01ab6c2174989f5aaee3e41ac930"}, {file = "greenlet-3.5.4-cp315-cp315-win_amd64.whl", hash = "sha256:ed17e5f3420360d5b459de8462efb52060399a5326a613d4cde31cef63ef95da"},
{file = "greenlet-3.5.3-cp315-cp315-win_arm64.whl", hash = "sha256:499fef2acede88c1864a57bb586b4bf533c81e1b82df7ab93451cdb47dfec227"}, {file = "greenlet-3.5.4-cp315-cp315-win_arm64.whl", hash = "sha256:f908898d6fa484ce4b6f447ce70ea99b52c503fee419e53cf74d60a16bc9e667"},
{file = "greenlet-3.5.3-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:176bc16a721fa5fc294d70b87b4dfa5fbdd251b3da5d5372735ecef9bd7d6d0c"}, {file = "greenlet-3.5.4-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:1833637f17d5e7472548a48575c394fe39f1b1890d676d162d86593610f44d8c"},
{file = "greenlet-3.5.3-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:629b614d2b786e89c50440e246f33eea78f58a962d0bdbbcc809e6d13605903f"}, {file = "greenlet-3.5.4-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12cda9122e03341f1cb6b8207a19d7a9d375e52f1b4e9243918375f40fd7b4b9"},
{file = "greenlet-3.5.3-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b2e857ae16f5f72142edf75f9f176fe7526ba19a2841df1420516f83831c9f2"}, {file = "greenlet-3.5.4-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d83ae0e32d14957ab7170785a20f582635c8474deab1bfbb552b17e769a6ce25"},
{file = "greenlet-3.5.3-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:16d192579ed281051396dddd7f7754dac6259e6b1fb26378c87b66622f8e3f91"}, {file = "greenlet-3.5.4-cp315-cp315t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:123aa379c962ed5fe90a880327e0c3066124ac64ec99e12a238be9fd8eb3db3d"},
{file = "greenlet-3.5.3-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e515757e2e36bcbf1fad09a46e1557e8b1ae1797d4b44d09da7deed88ad28608"}, {file = "greenlet-3.5.4-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f1467de1bb767f75db0aa34c195e3a496d8d1278c796e70c24ce205d3e99cde"},
{file = "greenlet-3.5.3-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:4399eb8d041f20b68d943918bc55502a93d6fdc0a37c14da7881c04139acee9d"}, {file = "greenlet-3.5.4-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:adf2244d7f69409925a8f22ed22cc5f93cdfe5c9dc87ff3476be2c2aaae61a05"},
{file = "greenlet-3.5.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:b363d46ed1ea431825fdb01471bb024fc08399bad1572a616e853c7684415adb"}, {file = "greenlet-3.5.4-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:0fa53040b78b578120eecdc0265e3f1051487cc425d11a2b7c761daadf4feaa8"},
{file = "greenlet-3.5.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:e44da2f5bbdaabaf7d80b73dbb430c7035771e9f244e3c8b769715c9d8fa0a16"}, {file = "greenlet-3.5.4-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:60e0bc961d367df506660e9ac0177a76bc6d81305300704b0977d1634f76efe2"},
{file = "greenlet-3.5.3-cp315-cp315t-win_amd64.whl", hash = "sha256:8ff8bed3e3baa20a3ea261ce00526f1898ad4801d4886fd2220580ee0ad8fadf"}, {file = "greenlet-3.5.4-cp315-cp315t-win_amd64.whl", hash = "sha256:f680e549edb3eaf21eea4e7fe101e15ec180c74b7879ab46adc080f22d4015d2"},
{file = "greenlet-3.5.3-cp315-cp315t-win_arm64.whl", hash = "sha256:b7068bd09f761f3f5b4d214c2bed063186b2a86148c740b3873e3f56d79bac31"}, {file = "greenlet-3.5.4-cp315-cp315t-win_arm64.whl", hash = "sha256:08fc36de8442d5c3e95b044550dbea9bf144d31ec0cc58e36fb241cb6ef6a994"},
{file = "greenlet-3.5.3.tar.gz", hash = "sha256:a61efc018fd3eb317eeca31aba90ee9e7f26f22884a79b6c6ec715bf71bb62f1"}, {file = "greenlet-3.5.4.tar.gz", hash = "sha256:0232ae1de90a8e07867bb127d7a6ba2301e859145489f25cda8a6096dabe1d20"},
] ]
[package.extras] [package.extras]
@@ -1684,6 +1699,25 @@ pygments = ">=2.7.2"
[package.extras] [package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-asyncio"
version = "1.4.0"
description = "Pytest support for asyncio"
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1"},
{file = "pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42"},
]
[package.dependencies]
pytest = ">=8.4,<10"
[package.extras]
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)", "sphinx-tabs (>=3.5)"]
testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
[[package]] [[package]]
name = "pytest-cov" name = "pytest-cov"
version = "7.1.0" version = "7.1.0"
@@ -2151,4 +2185,4 @@ files = [
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.13" python-versions = ">=3.13"
content-hash = "ebbba0a60f3f121f79117d05b5a6dbd8914ca1d971776808c7d1cfd146b562bc" content-hash = "9f5afa235b9e6861253603be40e4b5c3ae8d59b6d8bff7669d4e7670183ff336"
+4 -1
View File
@@ -25,7 +25,10 @@ dependencies = [
"httpie (>=3.2.4,<4.0.0)", "httpie (>=3.2.4,<4.0.0)",
"pytest-cov (>=7.1.0,<8.0.0)", "pytest-cov (>=7.1.0,<8.0.0)",
"allure-pytest (>=2.16.0,<3.0.0)", "allure-pytest (>=2.16.0,<3.0.0)",
"pytest-mock (>=3.15.1,<4.0.0)" "pytest-mock (>=3.15.1,<4.0.0)",
"aiosqlite (>=0.22.1,<0.23.0)",
"greenlet (>=3.5.4,<4.0.0)",
"pytest-asyncio (>=1.4.0,<2.0.0)"
] ]
+2
View File
@@ -10,3 +10,5 @@ markers=
integra: integrations test integra: integrations test
e2e: e2e tests e2e: e2e tests
smoke: smoke tests smoke: smoke tests
asyncio_mode = auto
+19 -18
View File
@@ -2,6 +2,7 @@
from uuid import UUID from uuid import UUID
from sqlalchemy import and_, not_, select, update from sqlalchemy import and_, not_, select, update
from sqlalchemy.ext.asyncio import async_sessionmaker
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from src.models.database_models.model import RefreshTokens, engine from src.models.database_models.model import RefreshTokens, engine
@@ -10,32 +11,32 @@ from src.models.pydantic_models.model import RefreshTokensOut
class JwtCrudActions: class JwtCrudActions:
def __init__(self) -> None: def __init__(self) -> None:
self.Session=sessionmaker(bind=engine) self.Session=async_sessionmaker(bind=engine)
def get_token_by_user_id(self, user_id:UUID)->RefreshTokensOut|None: async def get_token_by_user_id(self, user_id:UUID)->RefreshTokensOut|None:
with self.Session() as session, session.begin(): async with self.Session() as session, session.begin():
query=select(RefreshTokens).where(and_(RefreshTokens.user_id==user_id, not_(RefreshTokens.is_revoked))) query=select(RefreshTokens).where(and_(RefreshTokens.user_id==user_id, not_(RefreshTokens.is_revoked)))
response=session.scalars(query).first() response= (await session.scalars(query)).first()
if response is None: if response is None:
return None return None
return RefreshTokensOut.model_validate(response) return RefreshTokensOut.model_validate(response)
def get_token_by_id(self, token_id:UUID)->RefreshTokensOut|None: async def get_token_by_id(self, token_id:UUID)->RefreshTokensOut|None:
with self.Session() as session, session.begin(): async with self.Session() as session, session.begin():
query=select(RefreshTokens).where(RefreshTokens.id==token_id) query=select(RefreshTokens).where(RefreshTokens.id==token_id)
response=session.scalars(query).one_or_none() response= (await session.scalars(query)).one_or_none()
if response is None: if response is None:
return None return None
return RefreshTokensOut.model_validate(response) return RefreshTokensOut.model_validate(response)
def create_token(self, data:dict)->None: async def create_token(self, data:dict)->None:
with self.Session() as session, session.begin(): async with self.Session() as session, session.begin():
new_token=RefreshTokens(**data) new_token=RefreshTokens(**data)
session.add(new_token) session.add(new_token)
def create_and_update_token(self, data: dict, old_jti: UUID, new_jti: UUID) -> bool: async def create_and_update_token(self, data: dict, old_jti: UUID, new_jti: UUID) -> bool:
with self.Session() as session, session.begin(): async with self.Session() as session, session.begin():
new_token = RefreshTokens(**data) new_token = RefreshTokens(**data)
query = ( query = (
@@ -44,7 +45,7 @@ class JwtCrudActions:
.values(is_revoked=True, replaced_by=new_jti) .values(is_revoked=True, replaced_by=new_jti)
.returning(RefreshTokens.id) .returning(RefreshTokens.id)
) )
updated_id = session.execute(query).scalar_one_or_none() updated_id = (await session.execute(query)).scalar_one_or_none()
if updated_id is None: if updated_id is None:
return False return False
@@ -53,15 +54,15 @@ class JwtCrudActions:
return True return True
def revoke_all(self, user_id:UUID)->bool: async def revoke_all(self, user_id:UUID)->bool:
with self.Session() as session, session.begin(): async with self.Session() as session, session.begin():
session.execute(update(RefreshTokens).where(RefreshTokens.user_id==user_id).values(is_revoked=True)) #bulk update await session.execute(update(RefreshTokens).where(RefreshTokens.user_id==user_id).values(is_revoked=True)) #bulk update
return True return True
def logout(self,token_id:UUID)->bool: async def logout(self,token_id:UUID)->bool:
with self.Session() as session, session.begin(): async with self.Session() as session, session.begin():
query=select(RefreshTokens).where(RefreshTokens.id == token_id) query=select(RefreshTokens).where(RefreshTokens.id == token_id)
response=session.scalars(query).one_or_none() response= (await session.scalars(query)).one_or_none()
if response is None: if response is None:
return False return False
else: else:
+8 -8
View File
@@ -1,7 +1,7 @@
from uuid import UUID from uuid import UUID
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.asyncio import async_sessionmaker
from src.models.database_models.model import User, engine from src.models.database_models.model import User, engine
from src.models.pydantic_models.model import UserOutDB from src.models.pydantic_models.model import UserOutDB
@@ -10,20 +10,20 @@ from src.models.pydantic_models.model import UserOutDB
class UsersCrudActions: class UsersCrudActions:
def __init__(self) -> None: def __init__(self) -> None:
self.Session=sessionmaker(bind=engine) self.Session=async_sessionmaker(bind=engine)
def get_user_by_email(self, email:str)->UserOutDB|None: async def get_user_by_email(self, email:str)->UserOutDB|None:
with self.Session() as session, session.begin(): async with self.Session() as session, session.begin():
query=select(User).where(User.email==email) query=select(User).where(User.email==email)
response=session.scalars(query).one_or_none() response=(await session.scalars(query)).one_or_none()
if response is None: if response is None:
return None return None
return UserOutDB.model_validate(response) return UserOutDB.model_validate(response)
def get_user_by_id(self, id:UUID)->UserOutDB|None: async def get_user_by_id(self, id:UUID)->UserOutDB|None:
with self.Session() as session, session.begin(): async with self.Session() as session, session.begin():
query=select(User).where(User.id==id) query=select(User).where(User.id==id)
response=session.scalars(query).one_or_none() response=(await session.scalars(query)).one_or_none()
if response is None: if response is None:
return None return None
return UserOutDB.model_validate(response) return UserOutDB.model_validate(response)
+2 -2
View File
@@ -10,12 +10,12 @@ from sqlalchemy import (
String, String,
Table, Table,
Uuid, Uuid,
create_engine,
func, func,
) )
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
engine = create_engine("sqlite:///DB/database.db", echo=True) engine = create_async_engine("sqlite+aiosqlite:///DB/database.db", echo=True)
'''remember as a boilerplate, or just cp/pst''' '''remember as a boilerplate, or just cp/pst'''
class Model(DeclarativeBase): class Model(DeclarativeBase):
+31 -30
View File
@@ -1,3 +1,4 @@
import asyncio
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
from uuid import UUID from uuid import UUID
@@ -21,14 +22,14 @@ class CurrentUserService:
self.jwt_db_actions=JwtCrudActions() self.jwt_db_actions=JwtCrudActions()
self.error=Errors() self.error=Errors()
def _check(self, form_data_email:str, form_data_password:str,): async def _check(self, form_data_email:str, form_data_password:str,):
'''check user by email''' '''check user by email'''
user=self.crud_db_actions.get_user_by_email(form_data_email) user=await self.crud_db_actions.get_user_by_email(form_data_email)
if user is None: if user is None:
raise self.error.credentials_error(detail="Wrong credentials") raise self.error.credentials_error(detail="Wrong credentials")
if not self.hash.verify_password(plain_password=form_data_password, hashed_password=user.hashed_password): if not await asyncio.to_thread(self.hash.verify_password, plain_password=form_data_password, hashed_password=user.hashed_password):
raise self.error.credentials_error(detail="Wrong credentials") raise self.error.credentials_error(detail="Wrong credentials")
if user.status is False: if user.status is False:
@@ -36,7 +37,7 @@ class CurrentUserService:
return user return user
def _token_record_create(self, jti:UUID,user_id:UUID,token:str, request:Request)->RefreshTokensCreate: async def _token_record_create(self, jti:UUID,user_id:UUID,token:str, request:Request)->RefreshTokensCreate:
return RefreshTokensCreate( return RefreshTokensCreate(
id=jti, id=jti,
@@ -48,9 +49,9 @@ class CurrentUserService:
) )
def get_current_user(self, token:str)->UserOut: async def get_current_user(self, token:str)->UserOut:
payload=self.jwt_service.jwt_decode(token) payload= await self.jwt_service.jwt_decode(token)
sub=payload.get("sub") sub=payload.get("sub")
try: try:
@@ -61,7 +62,7 @@ class CurrentUserService:
if not (payload.get("token_type")=="access"): if not (payload.get("token_type")=="access"):
raise self.error.credentials_error(detail="Jwt token type is incorrect") raise self.error.credentials_error(detail="Jwt token type is incorrect")
user=self.crud_db_actions.get_user_by_id(sub) user=await self.crud_db_actions.get_user_by_id(sub)
if user is None: if user is None:
raise self.error.not_found_error(detail="User with this email address not found") raise self.error.not_found_error(detail="User with this email address not found")
@@ -72,15 +73,15 @@ class CurrentUserService:
def create_access_token(self, user_id:UUID)->str: async def create_access_token(self, user_id:UUID)->str:
'''create new access token if all the checks are successful''' '''create new access token if all the checks are successful'''
return self.jwt_service.create_access_token({"sub":str(user_id)}) return await self.jwt_service.create_access_token({"sub":str(user_id)})
def create_refresh_token(self,user_id:UUID, request:Request)->str: async def create_refresh_token(self,user_id:UUID, request:Request)->str:
token, jti=self.jwt_service.create_refresh_token({"sub":str(user_id)}) token, jti= await self.jwt_service.create_refresh_token({"sub":str(user_id)})
try: try:
jti=UUID(jti) jti=UUID(jti)
@@ -88,18 +89,18 @@ class CurrentUserService:
raise self.error.credentials_error(detail="Jwt token is incorrect") from e raise self.error.credentials_error(detail="Jwt token is incorrect") from e
'''create new refresh token if all the checks are successful''' '''create new refresh token if all the checks are successful'''
token_record=self._token_record_create(jti=jti, user_id=user_id, token=token, request=request) token_record=await self._token_record_create(jti=jti, user_id=user_id, token=token, request=request)
self.jwt_db_actions.create_token(RefreshTokensCreate.model_dump(token_record)) await self.jwt_db_actions.create_token(RefreshTokensCreate.model_dump(token_record))
return token return token
def refresh_token(self, refresh_token:str, request:Request)->tuple[str, str]: async def refresh_token(self, refresh_token:str, request:Request)->tuple[str, str]:
'''decode old refresh token''' '''decode old refresh token'''
old_refresh_token=self.jwt_service.jwt_decode(refresh_token) old_refresh_token= await self.jwt_service.jwt_decode(refresh_token)
sub=old_refresh_token.get("sub") sub=old_refresh_token.get("sub")
if (old_jti:=old_refresh_token.get("jti")) is None: if (old_jti:=old_refresh_token.get("jti")) is None:
@@ -118,7 +119,7 @@ class CurrentUserService:
raise self.error.credentials_error(detail="Jwt token type is incorrect") raise self.error.credentials_error(detail="Jwt token type is incorrect")
old_record=self.jwt_db_actions.get_token_by_id(old_jti) old_record=await self.jwt_db_actions.get_token_by_id(old_jti)
if old_record is None: if old_record is None:
raise self.error.not_found_error(detail="Token not found") raise self.error.not_found_error(detail="Token not found")
@@ -131,7 +132,7 @@ class CurrentUserService:
raise self.error.credentials_error(detail="Token expired") raise self.error.credentials_error(detail="Token expired")
'''user check''' '''user check'''
user = self.crud_db_actions.get_user_by_id(sub) user = await self.crud_db_actions.get_user_by_id(sub)
if user is None: if user is None:
raise self.error.not_found_error(detail="User not found") raise self.error.not_found_error(detail="User not found")
if user.status is False: if user.status is False:
@@ -139,8 +140,8 @@ class CurrentUserService:
'''create new refresh token if all the checks are successful''' '''create new refresh token if all the checks are successful'''
new_refresh_token, new_jti=self.jwt_service.create_refresh_token({"sub":str(sub)}) new_refresh_token, new_jti= await self.jwt_service.create_refresh_token({"sub":str(sub)})
new_access_token=self.create_access_token(user_id=sub) new_access_token=await self.create_access_token(user_id=sub)
try: try:
new_jti=UUID(new_jti) new_jti=UUID(new_jti)
@@ -149,9 +150,9 @@ class CurrentUserService:
'''create database record with the new token''' '''create database record with the new token'''
new_token_record=self._token_record_create(jti=new_jti, user_id=sub, token=new_refresh_token, request=request) new_token_record=await self._token_record_create(jti=new_jti, user_id=sub, token=new_refresh_token, request=request)
success = self.jwt_db_actions.create_and_update_token(RefreshTokensCreate.model_dump(new_token_record), old_jti, new_jti) success = await self.jwt_db_actions.create_and_update_token(RefreshTokensCreate.model_dump(new_token_record), old_jti, new_jti)
if not success: if not success:
raise self.error.not_found_error(detail="Token not found") raise self.error.not_found_error(detail="Token not found")
@@ -160,10 +161,10 @@ class CurrentUserService:
def logout(self, refresh_token:str)->bool: async def logout(self, refresh_token:str)->bool:
'''decode current refresh token''' '''decode current refresh token'''
payload=self.jwt_service.jwt_decode(refresh_token) payload=await self.jwt_service.jwt_decode(refresh_token)
if (jti:=payload.get("jti")) is None: if (jti:=payload.get("jti")) is None:
raise self.error.credentials_error(detail="Jwt token is incorrect") raise self.error.credentials_error(detail="Jwt token is incorrect")
@@ -174,23 +175,23 @@ class CurrentUserService:
raise self.error.credentials_error(detail="Jwt token is incorrect") from e raise self.error.credentials_error(detail="Jwt token is incorrect") from e
'''logout by assigning revoked flag''' '''logout by assigning revoked flag'''
if self.jwt_db_actions.logout(jti): if await self.jwt_db_actions.logout(jti):
return True return True
else: else:
raise self.error.not_found_error(detail="Refresh Token Not Found") raise self.error.not_found_error(detail="Refresh Token Not Found")
def login(self, form_data_email:str, form_data_password:str, request:Request)->tuple[str, str]: async def login(self, form_data_email:str, form_data_password:str, request:Request)->tuple[str, str]:
'''revoke all the old refresh tokens''' '''revoke all the old refresh tokens'''
user = self._check(form_data_email, form_data_password) user = await self._check(form_data_email, form_data_password)
self.jwt_db_actions.revoke_all(user_id=user.id) await self.jwt_db_actions.revoke_all(user_id=user.id)
'''create access and refresh tokens''' '''create access and refresh tokens'''
access_token=self.create_access_token(user_id=user.id) access_token=await self.create_access_token(user_id=user.id)
refresh_token=self.create_refresh_token(user_id=user.id,request=request) refresh_token=await self.create_refresh_token(user_id=user.id,request=request)
return (access_token, refresh_token) return (access_token, refresh_token)
def auth_service()->CurrentUserService: async def auth_service()->CurrentUserService:
return CurrentUserService() return CurrentUserService()
+6 -6
View File
@@ -31,27 +31,27 @@ class JwtService:
self.error=Errors() self.error=Errors()
def _validate_sub(self,data:dict)->None: async def _validate_sub(self,data:dict)->None:
if not (data.get("sub")) or data.get("sub") == "": if not (data.get("sub")) or data.get("sub") == "":
raise self.error.credentials_error(detail="Jwt token is incorrect") raise self.error.credentials_error(detail="Jwt token is incorrect")
def create_access_token(self, data:dict)->str: async def create_access_token(self, data:dict)->str:
user_info=data.copy() user_info=data.copy()
self._validate_sub(user_info) await self._validate_sub(user_info)
user_info.update({"exp": datetime.now(UTC)+timedelta(minutes=env_settings.ACCESS_TOKEN_EXPIRE_MINUTES), user_info.update({"exp": datetime.now(UTC)+timedelta(minutes=env_settings.ACCESS_TOKEN_EXPIRE_MINUTES),
"token_type":"access"}) "token_type":"access"})
return jwt.encode(user_info, env_settings.SECRET_KEY, env_settings.ALGORITHM) return jwt.encode(user_info, env_settings.SECRET_KEY, env_settings.ALGORITHM)
def create_refresh_token(self, data:dict)->tuple[str, str]: async def create_refresh_token(self, data:dict)->tuple[str, str]:
user_info=data.copy() user_info=data.copy()
jti=str(uuid4()) jti=str(uuid4())
self._validate_sub(user_info) await self._validate_sub(user_info)
user_info.update({"exp":datetime.now(UTC)+timedelta(days=env_settings.REFRESH_TOKEN_EXPIRE_DAYS), user_info.update({"exp":datetime.now(UTC)+timedelta(days=env_settings.REFRESH_TOKEN_EXPIRE_DAYS),
"token_type":"refresh", "token_type":"refresh",
@@ -62,7 +62,7 @@ class JwtService:
def jwt_decode(self, token:str)->dict: async def jwt_decode(self, token:str)->dict:
try: try:
payload=jwt.decode(token, env_settings.SECRET_KEY, algorithms=[env_settings.ALGORITHM], options={"require_exp": True} ) payload=jwt.decode(token, env_settings.SECRET_KEY, algorithms=[env_settings.ALGORITHM], options={"require_exp": True} )
+4 -3
View File
@@ -9,10 +9,11 @@ class CrudService:
self.errors=Errors() self.errors=Errors()
self.crud_db_actions=UsersCrudActions() self.crud_db_actions=UsersCrudActions()
def get_user_by_email(self, email:str)->UserOut: async def get_user_by_email(self, email:str)->UserOut:
user_entity=self.crud_db_actions.get_user_by_email(email) user_entity=await self.crud_db_actions.get_user_by_email(email)
if not user_entity: if not user_entity:
raise self.errors.not_found_error(detail="User wasn't found") raise self.errors.not_found_error(detail="User wasn't found")
return UserOut.model_validate(user_entity) return UserOut.model_validate(user_entity)
crud_service=CrudService() async def crud_service()->CrudService:
return CrudService()
+4 -4
View File
@@ -11,7 +11,7 @@ oauth2_schema=OAuth2PasswordBearer(tokenUrl="/protected/token", refreshUrl="/pro
@router.post("/token") @router.post("/token")
async def get_access_token(request: Request,response:Response,auth:CurrentUserService=Depends(auth_service), form_data:OAuth2PasswordRequestForm=Depends())->dict: # noqa: B008 async def get_access_token(request: Request,response:Response,auth:CurrentUserService=Depends(auth_service), form_data:OAuth2PasswordRequestForm=Depends())->dict: # noqa: B008
access_token, refresh_token=auth.login(form_data_email=form_data.username, form_data_password=form_data.password, request=request) access_token, refresh_token=await auth.login(form_data_email=form_data.username, form_data_password=form_data.password, request=request)
response.set_cookie( response.set_cookie(
key="refresh_token", key="refresh_token",
@@ -27,7 +27,7 @@ async def get_access_token(request: Request,response:Response,auth:CurrentUserSe
@router.post("/refresh") @router.post("/refresh")
async def get_refresh_token(request:Request,response:Response, refresh_token: str = Cookie(), auth:CurrentUserService=Depends(auth_service))->dict: # noqa: B008 async def get_refresh_token(request:Request,response:Response, refresh_token: str = Cookie(), auth:CurrentUserService=Depends(auth_service))->dict: # noqa: B008
access_token, refresh_token= auth.refresh_token(refresh_token=refresh_token,request=request) access_token, refresh_token= await auth.refresh_token(refresh_token=refresh_token,request=request)
response.set_cookie( response.set_cookie(
key="refresh_token", key="refresh_token",
@@ -42,13 +42,13 @@ async def get_refresh_token(request:Request,response:Response, refresh_token: st
async def get_current_user(token:str = Depends(oauth2_schema), auth:CurrentUserService=Depends(auth_service)) -> UserOut: # noqa: B008 async def get_current_user(token:str = Depends(oauth2_schema), auth:CurrentUserService=Depends(auth_service)) -> UserOut: # noqa: B008
return UserOut.model_validate(auth.get_current_user(token)) return UserOut.model_validate(await auth.get_current_user(token))
@router.get("/logout") @router.get("/logout")
async def logout(response:Response,refresh_token: str = Cookie(),auth:CurrentUserService=Depends(auth_service),current_user:UserOut=Depends(get_current_user))->bool: # noqa: B008 async def logout(response:Response,refresh_token: str = Cookie(),auth:CurrentUserService=Depends(auth_service),current_user:UserOut=Depends(get_current_user))->bool: # noqa: B008
response.delete_cookie("refresh_token") response.delete_cookie("refresh_token")
return auth.logout(refresh_token) return await auth.logout(refresh_token)
@router.get("") @router.get("")
async def protected(current_user:UserOut=Depends(get_current_user))->dict: # noqa: B008 async def protected(current_user:UserOut=Depends(get_current_user))->dict: # noqa: B008
@@ -1,12 +1,11 @@
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from src.models.pydantic_models.model import UserOut from src.models.pydantic_models.model import UserOut
from src.service.users_crud.users_crud import crud_service from src.service.users_crud.users_crud import CrudService, crud_service
from src.web.protected_routes.auth_routes import get_current_user from src.web.protected_routes.auth_routes import get_current_user
router=APIRouter(prefix="/user") router=APIRouter(prefix="/user")
@router.get("/get_by_email") @router.get("/get_by_email")
async def get_current_user_by_email(email:str, current_user=Depends(get_current_user))->UserOut: # noqa: B008 async def get_current_user_by_email(email:str, crud:CrudService=Depends(crud_service), current_user=Depends(get_current_user))->UserOut: # noqa: B008
return crud_service.get_user_by_email(email) return await crud.get_user_by_email(email)
+5 -5
View File
@@ -1,14 +1,14 @@
import pytest import pytest_asyncio
from src.service.auth.jwt import HashService, JwtService from src.service.auth.jwt import HashService, JwtService
@pytest.fixture @pytest_asyncio.fixture
def jwt_service()->JwtService: async def jwt_service()->JwtService:
jwt_service=JwtService() jwt_service=JwtService()
return jwt_service return jwt_service
@pytest.fixture @pytest_asyncio.fixture
def hash_service()->HashService: async def hash_service()->HashService:
hash_service=HashService() hash_service=HashService()
return hash_service return hash_service
+5 -5
View File
@@ -1,16 +1,16 @@
import pytest import pytest_asyncio
from fastapi import Request from fastapi import Request
from src.service.auth.auth import CurrentUserService from src.service.auth.auth import CurrentUserService
@pytest.fixture @pytest_asyncio.fixture
def current_user_service()->CurrentUserService: async def current_user_service()->CurrentUserService:
current_user_service=CurrentUserService() current_user_service=CurrentUserService()
return current_user_service return current_user_service
@pytest.fixture @pytest_asyncio.fixture
def requests(mocker): async def requests(mocker):
fake_request = mocker.MagicMock(spec=Request) fake_request = mocker.MagicMock(spec=Request)
fake_request.headers = {"user-agent": "pytest-agent", "x-forwarded-for":"127.0.0.1"} fake_request.headers = {"user-agent": "pytest-agent", "x-forwarded-for":"127.0.0.1"}
return fake_request return fake_request
+45 -43
View File
@@ -11,7 +11,9 @@ from pydantic import ValidationError
from src.models.configs_read.env import env_settings from src.models.configs_read.env import env_settings
from src.service.auth.auth import CurrentUserService from src.service.auth.auth import CurrentUserService
from src.service.auth.jwt import HashService, JwtService from src.service.auth.jwt import HashService, JwtService
from unittest.mock import AsyncMock
current_user_service=CurrentUserService()
@pytest.mark.integra @pytest.mark.integra
class TestAuth: class TestAuth:
@@ -19,19 +21,19 @@ class TestAuth:
@pytest.mark.parametrize("user_data",[ @pytest.mark.parametrize("user_data",[
pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True), id="correct_data") pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True), id="correct_data")
]) ])
def test_get_current_user_positive(self,current_user_service:CurrentUserService, jwt_service:JwtService, monkeypatch, user_data:SimpleNamespace)->None: async def test_get_current_user_positive(self,current_user_service:CurrentUserService, jwt_service:JwtService, monkeypatch, user_data:SimpleNamespace)->None:
with allure.step("create token"): with allure.step("create token"):
token=jwt_service.create_access_token({"sub":str(uuid4())}) token=await jwt_service.create_access_token({"sub":str(uuid4())})
with allure.step("patching db call functions"): with allure.step("patching db call functions"):
monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_id", lambda user:user_data) monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_id", AsyncMock(return_value=user_data))
with allure.step("test get_current_user_with_fake_token"): with allure.step("test get_current_user_with_fake_token"):
test_result= current_user_service.get_current_user(token) test_result= await current_user_service.get_current_user(token)
assert test_result.first_name==user_data.first_name assert test_result.first_name==user_data.first_name
assert test_result.last_name==user_data.last_name assert test_result.last_name==user_data.last_name
@@ -46,37 +48,37 @@ class TestAuth:
pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True),1234, HTTPException, id="wrong_id"), pytest.param(SimpleNamespace(first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True),1234, HTTPException, id="wrong_id"),
pytest.param(SimpleNamespace(status=True),uuid4(), ValidationError,id="empty_model_data") pytest.param(SimpleNamespace(status=True),uuid4(), ValidationError,id="empty_model_data")
]) ])
def test_get_current_user_negative(self,current_user_service:CurrentUserService, jwt_service:JwtService, monkeypatch, user_data:SimpleNamespace, expected_exception, uuid)->None: async def test_get_current_user_negative(self,current_user_service:CurrentUserService, jwt_service:JwtService, monkeypatch, user_data:SimpleNamespace, expected_exception, uuid)->None:
with allure.step("create token"): with allure.step("create token"):
token=jwt_service.create_access_token({"sub":str(uuid)}) token=await jwt_service.create_access_token({"sub":str(uuid)})
with allure.step("patching db call functions"): with allure.step("patching db call functions"):
monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_id", lambda user:user_data) monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_id", AsyncMock(return_value=user_data))
with allure.step("test get_current_user_with_fake_token"), pytest.raises(expected_exception): with allure.step("test get_current_user_with_fake_token"), pytest.raises(expected_exception):
current_user_service.get_current_user(token) await current_user_service.get_current_user(token)
@pytest.mark.parametrize("user_data, form_data_email,form_data_password",[ @pytest.mark.parametrize("user_data, form_data_email,form_data_password",[
pytest.param(SimpleNamespace(id=uuid4(),hashed_password="1234", first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True), "d@d.d", "1234", id="correct_data"), pytest.param(SimpleNamespace(id=uuid4(),hashed_password="1234", first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True), "d@d.d", "1234", id="correct_data"),
]) ])
def test_login_positive(self, jwt_service:JwtService,current_user_service:CurrentUserService, monkeypatch, user_data:SimpleNamespace, hash_service:HashService, form_data_email:str, form_data_password:str, requests)->None: async def test_login_positive(self, jwt_service:JwtService,current_user_service:CurrentUserService, monkeypatch, user_data:SimpleNamespace, hash_service:HashService, form_data_email:str, form_data_password:str, requests)->None:
with allure.step("patching db call functions"): with allure.step("patching db call functions"):
user_data.hashed_password=hash_service.plain_to_hash(user_data.hashed_password) user_data.hashed_password=hash_service.plain_to_hash(user_data.hashed_password)
monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_email", lambda user:user_data) monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_email", AsyncMock(return_value=user_data))
monkeypatch.setattr(current_user_service.jwt_db_actions, "revoke_all", lambda user_id: True) monkeypatch.setattr(current_user_service.jwt_db_actions, "revoke_all", AsyncMock(return_value=True))
monkeypatch.setattr(current_user_service.jwt_db_actions, "create_token", lambda token: True ) monkeypatch.setattr(current_user_service.jwt_db_actions, "create_token", AsyncMock(return_value=True))
fake_request = requests fake_request = requests
with allure.step("test login_with_fake_data"): with allure.step("test login_with_fake_data"):
access, refresh=current_user_service.login(form_data_email, form_data_password,fake_request) access, refresh=await current_user_service.login(form_data_email, form_data_password,fake_request)
parts_a=access.split(".") parts_a=access.split(".")
parts_b=refresh.split(".") parts_b=refresh.split(".")
assert isinstance(access, str) assert isinstance(access, str)
@@ -90,36 +92,36 @@ class TestAuth:
pytest.param(SimpleNamespace(id=uuid4(),hashed_password="1234", first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=False), "d@d.d", "1234",HTTPException, id="false_status"), pytest.param(SimpleNamespace(id=uuid4(),hashed_password="1234", first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=False), "d@d.d", "1234",HTTPException, id="false_status"),
pytest.param(SimpleNamespace(id=1234,hashed_password="1234", first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True), "d@d.d", "1234",ValidationError, id="wrong_id"), pytest.param(SimpleNamespace(id=1234,hashed_password="1234", first_name="test",last_name="test",middle_name="test",email="d@d.d",direct_permissions=[],group=[],status=True), "d@d.d", "1234",ValidationError, id="wrong_id"),
]) ])
def test_login_negative(self, current_user_service:CurrentUserService, user_data:SimpleNamespace, jwt_service:JwtService, monkeypatch, requests, hash_service:HashService, form_data_email:str, form_data_password:str, expected_exception): async def test_login_negative(self, current_user_service:CurrentUserService, user_data:SimpleNamespace, jwt_service:JwtService, monkeypatch, requests, hash_service:HashService, form_data_email:str, form_data_password:str, expected_exception):
with allure.step("patching db call functions"): with allure.step("patching db call functions"):
user_data.hashed_password=hash_service.plain_to_hash(user_data.hashed_password) user_data.hashed_password=hash_service.plain_to_hash(user_data.hashed_password)
monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_email", lambda user:user_data) monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_email", AsyncMock(return_value=user_data))
monkeypatch.setattr(current_user_service.jwt_db_actions, "revoke_all", lambda user_id: True) monkeypatch.setattr(current_user_service.jwt_db_actions, "revoke_all", AsyncMock(return_value=True))
monkeypatch.setattr(current_user_service.jwt_db_actions, "create_token", lambda token: True ) monkeypatch.setattr(current_user_service.jwt_db_actions, "create_token", AsyncMock(return_value=True))
fake_request = requests fake_request = requests
with allure.step("test login_with_fake_data"), pytest.raises(expected_exception): with allure.step("test login_with_fake_data"), pytest.raises(expected_exception):
current_user_service.login(form_data_email, form_data_password,fake_request) await current_user_service.login(form_data_email, form_data_password,fake_request)
def test_logout_positive(self, jwt_service:JwtService, monkeypatch, current_user_service:CurrentUserService)->None: async def test_logout_positive(self, jwt_service:JwtService, monkeypatch, current_user_service:CurrentUserService)->None:
with allure.step("create fake refresh token"): with allure.step("create fake refresh token"):
token=jwt_service.create_refresh_token({"sub":str(uuid4())}) token=await jwt_service.create_refresh_token({"sub":str(uuid4())})
with allure.step("patching db call functions"): with allure.step("patching db call functions"):
monkeypatch.setattr(current_user_service.jwt_db_actions, "logout", lambda jti: True ) monkeypatch.setattr(current_user_service.jwt_db_actions, "logout", AsyncMock(return_value=True))
with allure.step("test logout with fake data"): with allure.step("test logout with fake data"):
status=current_user_service.logout(token[0]) status=await current_user_service.logout(token[0])
assert status is True assert status is True
@pytest.mark.parametrize("jti,db_result, expected_exception",[ @pytest.mark.parametrize("jti,db_result, expected_exception",[
@@ -127,13 +129,13 @@ class TestAuth:
pytest.param(1234, True, HTTPException, id="jti_int"), pytest.param(1234, True, HTTPException, id="jti_int"),
pytest.param(str(uuid4()), False, HTTPException, id="db_result_none"), pytest.param(str(uuid4()), False, HTTPException, id="db_result_none"),
]) ])
def test_logout_negative(self, jwt_service:JwtService, monkeypatch, current_user_service:CurrentUserService, expected_exception, jti, db_result)->None: async def test_logout_negative(self, jwt_service:JwtService, monkeypatch, current_user_service:CurrentUserService, expected_exception, jti, db_result)->None:
with allure.step("patching db call functions"): with allure.step("patching db call functions"):
monkeypatch.setattr(current_user_service.jwt_db_actions, "logout", lambda jti: db_result ) monkeypatch.setattr(current_user_service.jwt_db_actions, "logout", AsyncMock(return_value=db_result) )
def fake_create_refresh_token(data:dict)->str: async def fake_create_refresh_token(data:dict)->str:
return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM) return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM)
monkeypatch.setattr(jwt_service, "create_refresh_token", fake_create_refresh_token) monkeypatch.setattr(jwt_service, "create_refresh_token", fake_create_refresh_token)
@@ -141,39 +143,39 @@ class TestAuth:
with allure.step("create fake refresh token"): with allure.step("create fake refresh token"):
token=fake_create_refresh_token({"sub":str(uuid4()), "jti":jti, "token_type":"refresh", "exp":datetime.now(UTC)+timedelta(days=45)}) token=await fake_create_refresh_token({"sub":str(uuid4()), "jti":jti, "token_type":"refresh", "exp":datetime.now(UTC)+timedelta(days=45)})
with allure.step("test logout with fake data"), pytest.raises(expected_exception): with allure.step("test logout with fake data"), pytest.raises(expected_exception):
current_user_service.logout(token) await current_user_service.logout(token)
@pytest.mark.parametrize("db_result_token, user_data_result_db", [ @pytest.mark.parametrize("db_result_token, user_data_result_db", [
pytest.param(SimpleNamespace(is_revoked=False, expires_at=datetime.now(UTC)+timedelta(days=15)),SimpleNamespace(status=True), id="correct_data") pytest.param(SimpleNamespace(is_revoked=False, expires_at=datetime.now(UTC)+timedelta(days=15)),SimpleNamespace(status=True), id="correct_data")
]) ])
def test_refresh_token_positive(self, monkeypatch, current_user_service:CurrentUserService, db_result_token, requests:Request, jwt_service:JwtService,user_data_result_db )->None: async def test_refresh_token_positive(self, monkeypatch, current_user_service:CurrentUserService, db_result_token, requests:Request, jwt_service:JwtService,user_data_result_db )->None:
with allure.step("patching db call functions"): with allure.step("patching db call functions"):
monkeypatch.setattr(current_user_service.jwt_db_actions,"get_token_by_id", lambda jti: db_result_token) monkeypatch.setattr(current_user_service.jwt_db_actions,"get_token_by_id", AsyncMock(return_value=db_result_token))
monkeypatch.setattr(current_user_service.jwt_db_actions, "revoke_all", lambda data:True) monkeypatch.setattr(current_user_service.jwt_db_actions, "revoke_all", AsyncMock(return_value=True))
monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_id", lambda user:user_data_result_db) monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_id", AsyncMock(return_value=user_data_result_db))
monkeypatch.setattr(current_user_service.jwt_db_actions,"create_and_update_token", lambda data0, data1, data2: True) monkeypatch.setattr(current_user_service.jwt_db_actions,"create_and_update_token", AsyncMock(return_value=True))
fake_request = requests fake_request = requests
def fake_create_refresh_token(data:dict)->str: async def fake_create_refresh_token(data:dict)->str:
return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM) return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM)
monkeypatch.setattr(jwt_service, "create_refresh_token", fake_create_refresh_token) monkeypatch.setattr(jwt_service, "create_refresh_token", fake_create_refresh_token)
with allure.step("create fake refresh token"): with allure.step("create fake refresh token"):
token=fake_create_refresh_token({"sub":str(uuid4()), "jti":str(uuid4()), "token_type":"refresh", "exp":datetime.now(UTC)+timedelta(days=45)}) token=await fake_create_refresh_token({"sub":str(uuid4()), "jti":str(uuid4()), "token_type":"refresh", "exp":datetime.now(UTC)+timedelta(days=45)})
with allure.step("test refresh token with fake data"): with allure.step("test refresh token with fake data"):
new_access_token, new_refresh_token = current_user_service.refresh_token(token, fake_request) new_access_token, new_refresh_token = await current_user_service.refresh_token(token, fake_request)
parts_a=new_access_token.split(".") parts_a=new_access_token.split(".")
parts_b=new_refresh_token.split(".") parts_b=new_refresh_token.split(".")
assert isinstance(new_access_token, str) assert isinstance(new_access_token, str)
@@ -192,22 +194,22 @@ class TestAuth:
pytest.param(SimpleNamespace(is_revoked=False, user_id="123",expires_at=datetime.now(UTC)+timedelta(days=15)),None,True,{"sub":str(uuid4()), "jti":str(uuid4()), "token_type":"refresh", "exp":datetime.now(UTC)+timedelta(days=45)}, HTTPException,id="user_missing"), pytest.param(SimpleNamespace(is_revoked=False, user_id="123",expires_at=datetime.now(UTC)+timedelta(days=15)),None,True,{"sub":str(uuid4()), "jti":str(uuid4()), "token_type":"refresh", "exp":datetime.now(UTC)+timedelta(days=45)}, HTTPException,id="user_missing"),
pytest.param(SimpleNamespace(is_revoked=False,user_id="123", expires_at=datetime.now(UTC)-timedelta(days=15)),SimpleNamespace(status=True),True,{"sub":str(uuid4()), "jti":str(uuid4()), "token_type":"refresh", "exp":datetime.now(UTC)+timedelta(days=45)}, HTTPException,id="wrong_exp") pytest.param(SimpleNamespace(is_revoked=False,user_id="123", expires_at=datetime.now(UTC)-timedelta(days=15)),SimpleNamespace(status=True),True,{"sub":str(uuid4()), "jti":str(uuid4()), "token_type":"refresh", "exp":datetime.now(UTC)+timedelta(days=45)}, HTTPException,id="wrong_exp")
]) ])
def test_refresh_token_negative(self, monkeypatch, current_user_service:CurrentUserService, db_result_token, requests, jwt_service:JwtService,user_data_result_db, expected_exception, fake_token_data, update_result)->None: async def test_refresh_token_negative(self, monkeypatch, current_user_service:CurrentUserService, db_result_token, requests, jwt_service:JwtService,user_data_result_db, expected_exception, fake_token_data, update_result)->None:
with allure.step("patching db call functions"): with allure.step("patching db call functions"):
monkeypatch.setattr(current_user_service.jwt_db_actions,"get_token_by_id", lambda jti: db_result_token) monkeypatch.setattr(current_user_service.jwt_db_actions,"get_token_by_id", AsyncMock(return_value=db_result_token))
monkeypatch.setattr(current_user_service.jwt_db_actions, "revoke_all", lambda user_id:True) monkeypatch.setattr(current_user_service.jwt_db_actions, "revoke_all", AsyncMock(return_value=True))
monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_id", lambda user:user_data_result_db) monkeypatch.setattr(current_user_service.crud_db_actions, "get_user_by_id", AsyncMock(return_value=user_data_result_db))
monkeypatch.setattr(current_user_service.jwt_db_actions, "create_and_update_token", lambda old_jti, new_jti, new_token_record:update_result) monkeypatch.setattr(current_user_service.jwt_db_actions, "create_and_update_token", AsyncMock(return_value=update_result))
fake_request = requests fake_request = requests
def fake_create_refresh_token(data:dict)->str: async def fake_create_refresh_token(data:dict)->str:
return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM) return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM)
monkeypatch.setattr(jwt_service, "create_refresh_token", fake_create_refresh_token) monkeypatch.setattr(jwt_service, "create_refresh_token", fake_create_refresh_token)
with allure.step("create fake refresh token"): with allure.step("create fake refresh token"):
token=fake_create_refresh_token(fake_token_data) token=await fake_create_refresh_token(fake_token_data)
with allure.step("test refresh token with fake data"), pytest.raises(expected_exception): with allure.step("test refresh token with fake data"), pytest.raises(expected_exception):
current_user_service.refresh_token(token, fake_request) await current_user_service.refresh_token(token, fake_request)
+24 -24
View File
@@ -15,10 +15,10 @@ class TestJwt:
@pytest.mark.parametrize("data", [ @pytest.mark.parametrize("data", [
pytest.param({"sub": "123"}, id="full_sub") pytest.param({"sub": "123"}, id="full_sub")
]) ])
def test_access_create_positive(self, jwt_service:JwtService, data:dict)->None: async def test_access_create_positive(self, jwt_service:JwtService, data:dict)->None:
with allure.step("create correct access token"): with allure.step("create correct access token"):
token = jwt_service.create_access_token(data) token = await jwt_service.create_access_token(data)
parts=token.split(".") parts=token.split(".")
assert isinstance(token, str) assert isinstance(token, str)
assert len(parts)==3 assert len(parts)==3
@@ -30,19 +30,19 @@ class TestJwt:
pytest.param({"sub":""},HTTPException, id="empty_value"), pytest.param({"sub":""},HTTPException, id="empty_value"),
pytest.param({"":""},HTTPException, id="empty_key_value") pytest.param({"":""},HTTPException, id="empty_key_value")
]) ])
def test_access_create_negative(self, jwt_service:JwtService, data:dict, expected_exception)->None: async def test_access_create_negative(self, jwt_service:JwtService, data:dict, expected_exception)->None:
with allure.step("create invalid access token"),pytest.raises(expected_exception): with allure.step("create invalid access token"),pytest.raises(expected_exception):
jwt_service.create_access_token(data) await jwt_service.create_access_token(data)
@pytest.mark.parametrize("data", [ @pytest.mark.parametrize("data", [
pytest.param({"sub": "123"}, id="full_sub") pytest.param({"sub": "123"}, id="full_sub")
]) ])
def test_refresh_create_positive(self, jwt_service:JwtService, data:dict)->None: async def test_refresh_create_positive(self, jwt_service:JwtService, data:dict)->None:
with allure.step("create correct access token"): with allure.step("create correct access token"):
token = jwt_service.create_refresh_token(data) token = await jwt_service.create_refresh_token(data)
parts=token[0].split(".") parts=token[0].split(".")
assert isinstance(token[0], str) assert isinstance(token[0], str)
assert isinstance(token[1], str) assert isinstance(token[1], str)
@@ -54,24 +54,24 @@ class TestJwt:
pytest.param({"sub":""},HTTPException, id="empty_value"), pytest.param({"sub":""},HTTPException, id="empty_value"),
pytest.param({"":""},HTTPException, id="empty_key_value") pytest.param({"":""},HTTPException, id="empty_key_value")
]) ])
def test_refresh_create_negative(self, jwt_service:JwtService, data, expected_exception)->None: async def test_refresh_create_negative(self, jwt_service:JwtService, data, expected_exception)->None:
with allure.step("create invalid access token"), pytest.raises(expected_exception): with allure.step("create invalid access token"), pytest.raises(expected_exception):
jwt_service.create_refresh_token(data) await jwt_service.create_refresh_token(data)
@pytest.mark.parametrize("data", [ @pytest.mark.parametrize("data", [
pytest.param({"sub": "123", "exp":datetime.now(UTC)+timedelta(minutes=15), "token_type":"access"}, id="correct_data"), pytest.param({"sub": "123", "exp":datetime.now(UTC)+timedelta(minutes=15), "token_type":"access"}, id="correct_data"),
]) ])
def test_jwt_decode_positive(self, data:dict, monkeypatch, jwt_service:JwtService)->None: async def test_jwt_decode_positive(self, data:dict, monkeypatch, jwt_service:JwtService)->None:
with allure.step("patch a create token function"): with allure.step("patch a create token function"):
def fake_create_access_token(data:dict)->str: async def fake_create_access_token(data:dict)->str:
return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM) return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM)
monkeypatch.setattr(jwt_service, "create_access_token", fake_create_access_token) monkeypatch.setattr(jwt_service, "create_access_token", fake_create_access_token)
with allure.step("create and decode correct token"): with allure.step("create and decode correct token"):
fake_token = jwt_service.create_access_token(data) fake_token = await jwt_service.create_access_token(data)
payload=jwt_service.jwt_decode(fake_token) payload=await jwt_service.jwt_decode(fake_token)
assert payload.get("sub") assert payload.get("sub")
assert payload.get("exp") assert payload.get("exp")
assert payload.get("token_type") assert payload.get("token_type")
@@ -83,15 +83,15 @@ class TestJwt:
pytest.param({}, HTTPException, id="empty_data"), pytest.param({}, HTTPException, id="empty_data"),
pytest.param("", AttributeError, id="not_dict_data") pytest.param("", AttributeError, id="not_dict_data")
]) ])
def test_jwt_decode_invalid(self,jwt_service:JwtService, expected_exception, data, monkeypatch)->None: async def test_jwt_decode_invalid(self,jwt_service:JwtService, expected_exception, data, monkeypatch)->None:
with allure.step("patch a create token function"): with allure.step("patch a create token function"):
def fake_create_access_token(data:dict)->str: async def fake_create_access_token(data:dict)->str:
return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM) return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM)
monkeypatch.setattr(jwt_service, "create_access_token", fake_create_access_token) monkeypatch.setattr(jwt_service, "create_access_token", fake_create_access_token)
with allure.step("create and decode invalid token"), pytest.raises(expected_exception): with allure.step("create and decode invalid token"), pytest.raises(expected_exception):
fake_token=jwt_service.create_access_token(data) fake_token=await jwt_service.create_access_token(data)
jwt_service.jwt_decode(fake_token) await jwt_service.jwt_decode(fake_token)
@pytest.mark.parametrize("time, key, algorithm", [ @pytest.mark.parametrize("time, key, algorithm", [
@@ -99,23 +99,23 @@ class TestJwt:
pytest.param(-15, "correct_key", "HS256", id="wrong_time"), pytest.param(-15, "correct_key", "HS256", id="wrong_time"),
pytest.param(15, "correct_key", "HS512", id="wrong_algorithm"), pytest.param(15, "correct_key", "HS512", id="wrong_algorithm"),
]) ])
def test_jwt_decode_wrong_env(self, monkeypatch, time:int, key:str, algorithm:str, jwt_service)->None: async def test_jwt_decode_wrong_env(self, monkeypatch, time:int, key:str, algorithm:str, jwt_service)->None:
with allure.step("patch a create token function"): with allure.step("patch a create token function"):
def fake_create_access_token(data:dict, key:str, algorithm:str)->str: async def fake_create_access_token(data:dict, key:str, algorithm:str)->str:
data.update({"exp":datetime.now(UTC)+timedelta(minutes=time)}) data.update({"exp":datetime.now(UTC)+timedelta(minutes=time)})
return jwt.encode(data, key, algorithm) return jwt.encode(data, key, algorithm)
monkeypatch.setattr(jwt_service, "create_access_token", fake_create_access_token) monkeypatch.setattr(jwt_service, "create_access_token", fake_create_access_token)
with allure.step("create and decode token with wrong data inside"), pytest.raises(HTTPException): with allure.step("create and decode token with wrong data inside"), pytest.raises(HTTPException):
fake_token=jwt_service.create_access_token({"sub": "123", "token_type":"access"}, key, algorithm) fake_token=await jwt_service.create_access_token({"sub": "123", "token_type":"access"}, key, algorithm)
jwt_service.jwt_decode(fake_token) await jwt_service.jwt_decode(fake_token)
@pytest.mark.parametrize("password",[ @pytest.mark.parametrize("password",[
pytest.param("plain_password", id="correct_plain_password") pytest.param("plain_password", id="correct_plain_password")
]) ])
def test_hash_and_veryfy_positive(self, password:str, hash_service:HashService)->None: async def test_hash_and_veryfy_positive(self, password:str, hash_service:HashService)->None:
with allure.step("encode password"): with allure.step("encode password"):
encoded_password=hash_service.plain_to_hash(password) encoded_password=hash_service.plain_to_hash(password)
@@ -127,7 +127,7 @@ class TestJwt:
def test_verify_wrong_password(self, hash_service:HashService)->None: async def test_verify_wrong_password(self, hash_service:HashService)->None:
with allure.step("encode password"): with allure.step("encode password"):
encoded_password=hash_service.plain_to_hash("plain_password") encoded_password=hash_service.plain_to_hash("plain_password")
@@ -136,9 +136,9 @@ class TestJwt:
assert hash_service.verify_password("wrong_password", encoded_password) is False assert hash_service.verify_password("wrong_password", encoded_password) is False
def test_token_to_hash_determistic(self, hash_service:HashService)->None: async def test_token_to_hash_determistic(self, hash_service:HashService)->None:
assert hash_service.token_to_hash("abc")==hash_service.token_to_hash("abc") assert hash_service.token_to_hash("abc")==hash_service.token_to_hash("abc")
def test_token_to_hash_different_input(self, hash_service:HashService)->None: async def test_token_to_hash_different_input(self, hash_service:HashService)->None:
assert hash_service.token_to_hash("abc")!=hash_service.token_to_hash("xyz") assert hash_service.token_to_hash("abc")!=hash_service.token_to_hash("xyz")