diff --git a/poetry.lock b/poetry.lock index d39844d..6a0bffa 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1127,13 +1127,13 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -1933,13 +1933,13 @@ files = [ [[package]] name = "typer" -version = "0.12.5" +version = "0.13.0" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" files = [ - {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, - {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, + {file = "typer-0.13.0-py3-none-any.whl", hash = "sha256:d85fe0b777b2517cc99c8055ed735452f2659cd45e451507c76f48ce5c1d00e2"}, + {file = "typer-0.13.0.tar.gz", hash = "sha256:f1c7198347939361eec90139ffa0fd8b3df3a2259d5852a0f7400e476d95985c"}, ] [package.dependencies] @@ -2076,13 +2076,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.30.6" +version = "0.32.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"}, - {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"}, + {file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"}, + {file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"}, ] [package.dependencies] @@ -2248,100 +2248,83 @@ anyio = ">=3.0.0" [[package]] name = "websockets" -version = "13.1" +version = "14.0" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, - {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, - {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, - {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, - {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, - {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, - {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, - {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, - {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, - {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, - {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, - {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, - {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, - {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, - {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, - {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, - {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, - {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, - {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, - {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, - {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, - {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, - {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, - {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, - {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, + {file = "websockets-14.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:064a72c0602c2d2c2586143561e0f179ef9b98e0825dc4a3d5cdf55a81898ed6"}, + {file = "websockets-14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9dc5a2726fd16c266d35838db086fa4e621bb049e3bbe498ab9d54ad5068f726"}, + {file = "websockets-14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e541e4c8983b118a584c306070878e7f9670b7781e04184b6e05f9fc92e8a0e"}, + {file = "websockets-14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23b13edb4df2d4e5d6dc747d83e6b244e267a6615ede90f18ef13dfb2b6feb87"}, + {file = "websockets-14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:288365a33049dae3065cdb2c2dd4b48df4b64839c565761c4f3f0c360460a561"}, + {file = "websockets-14.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79e2494047826a56f2951b2ada9dc139d2c3aff63122e86953cafe64ac0fde75"}, + {file = "websockets-14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a5b76b47b62de16d26439d362b18d71394ca4376eb2c8838352be64b27ba8af"}, + {file = "websockets-14.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7ed4111f305770e35070e49fbb9fbf757a9b6c9a31bb86d352eb4031d4aa976f"}, + {file = "websockets-14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9af48a2f4cc5e2e34cf69969079865100e418c27caa26c1e3369efcc20c81e17"}, + {file = "websockets-14.0-cp310-cp310-win32.whl", hash = "sha256:a97c10043bf74d7667be69383312007d54a507fac8fa101be492cc91e279d94d"}, + {file = "websockets-14.0-cp310-cp310-win_amd64.whl", hash = "sha256:5f86250ee98f6098479936b7d596418b6e4c919dfa156508e9d6ac5f8bfbe764"}, + {file = "websockets-14.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3c12e6c1331ee8833fcb565c033f7eb4cb5642af37cef81211c222b617b170df"}, + {file = "websockets-14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:445a53bce8344e62df4ed9a22fdd1f06cad8e404ead64b2a1f19bd826c8dad1b"}, + {file = "websockets-14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e4be641fed120790241ae15fde27374a62cadaadcc0bd2b4ce35790bd284fb6"}, + {file = "websockets-14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b886b6d14cd089396155e6beb2935268bf995057bf24c3e5fd609af55c584a03"}, + {file = "websockets-14.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b8a85d62709a86a9a55d4720502e88968483ee7f365bd852b75935dec04e0d"}, + {file = "websockets-14.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08d62f438a591c016c5d4c79eaf9a8f7a85b6c3ea88793d676c00c930a41e775"}, + {file = "websockets-14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:189e9f074f2a77f7cf54634797b29be28116ee564ece421c7653030a2cef48f0"}, + {file = "websockets-14.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b406f2387dbaf301996b7b2cf41519c1fbba7d5c9626406dd56f72075a60a00"}, + {file = "websockets-14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a3741f4394ba3d55a64949ee11ffdba19e2a2bdaa1319a96a7ab93bf8bd2b9b2"}, + {file = "websockets-14.0-cp311-cp311-win32.whl", hash = "sha256:b639ea88a46f4629645b398c9e7be0366c92e4910203a6314f78469f5e631dc5"}, + {file = "websockets-14.0-cp311-cp311-win_amd64.whl", hash = "sha256:715b238c1772ed28b98af8830df41c5d68941729e22384fe1433db495b1d5438"}, + {file = "websockets-14.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f988f141a9be7a74d2e98d446b2f5411038bad14cdab80f9d1644b2329a71b48"}, + {file = "websockets-14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7fd212e7022c70b4f8246dee4449dde30ff50c7e8e1d61ac87b7879579badd03"}, + {file = "websockets-14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4c06f014fd8fa3827e5fd03ec012945e2139901f261fcc401e0622476cad9c5c"}, + {file = "websockets-14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad8f03dc976e710db785abf9deb76eb259312fb54d77b568c73f0162cef96e"}, + {file = "websockets-14.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cff048a155024a580fee9f9a66b0ad9fc82683f6470c26eb76dd9280e6f459e"}, + {file = "websockets-14.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56ec8098dcc47817c8aee8037165f0fe30fec8efe543c66e0924781a4bfcbdfd"}, + {file = "websockets-14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee5fb667aec4ae723d40ada9854128df427b35b526c600cd352ca0240aad4dd7"}, + {file = "websockets-14.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2752c98237057f27594a8393d498edd9db37e06abcfb99176d9cb6fb989dc883"}, + {file = "websockets-14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9ff528498d9e5c543bee388023ca91870678ac50724d675853ba85b4f0a459e"}, + {file = "websockets-14.0-cp312-cp312-win32.whl", hash = "sha256:8982909857b09220ee31d9a45699fce26f8e5b94a10efa7fe07004d4f4200a33"}, + {file = "websockets-14.0-cp312-cp312-win_amd64.whl", hash = "sha256:61b60c2a07b6d25f7ce8cc0101d55fb0f1af388bec1eddfe0181085c2206e7b0"}, + {file = "websockets-14.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7cf000319db10a0cb5c7ce91bfd2a8699086b5cc0b5c5b83b92eec22a0448b2f"}, + {file = "websockets-14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0bae3caf386d418e83b62e8c1c4cec1b13348fac43e530b9894d6c7c02d921b5"}, + {file = "websockets-14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8eb46ac94d5c131336dc997a568f5579501958b14a507e6aa4840f6d856da980"}, + {file = "websockets-14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12c345585b1da70cd27a298b0b9a81aa18da7a690672f771b427db59c632d8aa"}, + {file = "websockets-14.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81758da7c76b4e2ddabc4a98a51f3c3aca8585a6d3a8662b5061613303bd5f68"}, + {file = "websockets-14.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eae86193fd667667f35367d292b912685cb22c3f9f1dd6deaa3fdd713ab5976"}, + {file = "websockets-14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7078dd0eac3a1dccf2c6f474004dbe8a4e936dbd19d37bbfb6efa70c923ae04e"}, + {file = "websockets-14.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2a418d596536a470f6f8e94cbb1fde66fe65e03d68c403eee0f2198b129e139a"}, + {file = "websockets-14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d66eeab61956e231f35659e6d5b66dc04a3d51e65f2b8f71862dc6a8ba710d1"}, + {file = "websockets-14.0-cp313-cp313-win32.whl", hash = "sha256:b24f7286a5c4e350284623cf708662f0881fe7bc1146c1a1fe7e6a9be01a8d6b"}, + {file = "websockets-14.0-cp313-cp313-win_amd64.whl", hash = "sha256:fb260539dd2b64e93c9f2c59caa70d36d2020fb8e26fa17f62459ad50ebf6c24"}, + {file = "websockets-14.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0913596e0072202be8729dab05266398b72ee57c4232f48d52fe2a0370d0b53f"}, + {file = "websockets-14.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f2e7710f3c468519f9d5b01a291c407f809f8f831e5a204b238e02447046d78"}, + {file = "websockets-14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ae0e14729038208711d2e2f769280621c22cd253e3dac00f809fa38c6ccb79d"}, + {file = "websockets-14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4875d1c3ab3d1d9a9d8485dc1f4c2aaa63947824af03301911ea58d1e881e096"}, + {file = "websockets-14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:678990bc5a1e4fa36e18d340d439079a21e6b8d249848b7066cad1a6cbd34b82"}, + {file = "websockets-14.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaf3b31f8343dcc6c20d068c10eb29325dd70f5dc321ebb5fbeaa280436e70e"}, + {file = "websockets-14.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:633bbda2d30bc695900f6a07de4e5d92a4e8e8d0d8a536bb3c2051bee4dc3856"}, + {file = "websockets-14.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1c4ca7cc5a02f909789dad259dffe61be4f38ffb26dc5e26ab2dca2c7d7c87de"}, + {file = "websockets-14.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5ade11f4939b885303d28b53d512e96e1a8ea8fbebedd6fef3e2e1afe633cc2a"}, + {file = "websockets-14.0-cp39-cp39-win32.whl", hash = "sha256:281b5ab9514eb241e347a46367a2374cb60cf8f420c4283948aa188f05e7810c"}, + {file = "websockets-14.0-cp39-cp39-win_amd64.whl", hash = "sha256:72fe11675685412917363481b79c56e68175e62352f84ca4788ac264f9ea6ed0"}, + {file = "websockets-14.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3f1a697262e28682222f18fae70eb0800dfa50c6eb96b0561c6beb83d6cf78ca"}, + {file = "websockets-14.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e0e543e0e81c55e68552bd3c081282721c710a6379a2a78e1ec793853479b25"}, + {file = "websockets-14.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2786c74cbcb0263fd541e4a075aa8c932bdcaa91e5bbb8649c65304799acdd64"}, + {file = "websockets-14.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:176b39547950ff3520728bd1eadd0fa02c68492a1fabca636bab7883dd390905"}, + {file = "websockets-14.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86626d560ceb9d846d128b9c7bd2d0f247dbb62fb49c386762d109583140bf48"}, + {file = "websockets-14.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ca447967131023e98fcb4867f05cf8584adb424b9108180b2414745a6ff41c31"}, + {file = "websockets-14.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c4eb304743ab285f8f057344d115259fbe31e42151b9aae7610db83d2a7379b1"}, + {file = "websockets-14.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:cc7dbe53276429b2ca511a04a3979ce27aa2088fdd28c119c6913dccdfd0e909"}, + {file = "websockets-14.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6dd785f7a521189b1233d3c86c0b66fb73d4769a1d253ce5b31081c5946f05f"}, + {file = "websockets-14.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:77697c303b874daf1c76d4e167cd5d6871c26964bc189e4bdb40427067d53a86"}, + {file = "websockets-14.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20979614e4d7266f15018c154255d35dfb9fc828fdf6b4924166b6728fed359f"}, + {file = "websockets-14.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3fb3d9e3940ea15b30404200e768e6111c3ee2956c60ceb001cae057961ab058"}, + {file = "websockets-14.0-py3-none-any.whl", hash = "sha256:1a3bca8cfb66614e23a65aa5d6b87190876ec6f3247094939f9db877db55319c"}, + {file = "websockets-14.0.tar.gz", hash = "sha256:be90aa6dab180fed523c0c10a6729ad16c9ba79067402d01a4d8aa7ce48d4084"}, ] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "b81d876afc0c2ad5130f3ccc0cbf2f4c1436c32e6045b7bc8cd7fa7ab481450a" +content-hash = "715002baeecfedfca10c0bdff27a4016091a1eb1b9d8e6392c8e068499161ee8" diff --git a/pyproject.toml b/pyproject.toml index 759fc2b..3f47661 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "geneweaver-api" -version = "0.11.0" +version = "0.12.0a0" description = "The Geneweaver API" authors = [ "Alexander Berger ", @@ -19,7 +19,7 @@ packages = [ python = "^3.9" geneweaver-core = "^0.10.0a3" fastapi = {extras = ["all"], version = "^0.115.0"} -uvicorn = {extras = ["standard"], version = "^0.30.0"} +uvicorn = {extras = ["standard"], version = "^0.32.0"} geneweaver-db = "^0.6.0a3" psycopg-pool = "^3.1.7" requests = "^2.32.3" diff --git a/src/geneweaver/api/controller/genes.py b/src/geneweaver/api/controller/genes.py index b56f9d1..e7ed566 100644 --- a/src/geneweaver/api/controller/genes.py +++ b/src/geneweaver/api/controller/genes.py @@ -9,11 +9,11 @@ GeneIdMappingAonReq, GeneIdMappingReq, GeneIdMappingResp, - GeneReturn, ) from geneweaver.api.services import genes as genes_service from geneweaver.core.enum import GeneIdentifier, Species from geneweaver.core.schema.gene import Gene +from jax.apiutils import CollectionResponse, Response from typing_extensions import Annotated from . import message as api_message @@ -50,7 +50,7 @@ def get_genes( description=api_message.OFFSET, ), ] = None, -) -> GeneReturn: +) -> CollectionResponse[Gene]: """Get geneweaver list of genes.""" if limit is None: limit = 100 @@ -58,7 +58,7 @@ def get_genes( response = genes_service.get_genes( cursor, reference_id, gene_database, species, preferred, limit, offset ) - return response + return CollectionResponse[Gene](**response) @router.get("/{gene_id}/preferred") @@ -67,10 +67,10 @@ def get_gene_preferred( int, Path(format="int64", minimum=0, maxiumum=9223372036854775807) ], cursor: Optional[deps.Cursor] = Depends(deps.cursor), -) -> Gene: +) -> Response[Gene]: """Get preferred gene for a given gene ode_id.""" response = genes_service.get_gene_preferred(cursor, gene_id) - return response + return Response[Gene](response) @router.post("/homologs", response_model=GeneIdMappingResp, deprecated=True) diff --git a/src/geneweaver/api/controller/genesets.py b/src/geneweaver/api/controller/genesets.py index f1998d9..165dba9 100644 --- a/src/geneweaver/api/controller/genesets.py +++ b/src/geneweaver/api/controller/genesets.py @@ -8,15 +8,16 @@ from fastapi import APIRouter, Depends, HTTPException, Path, Query, Request, Security from fastapi.responses import FileResponse, StreamingResponse from geneweaver.api import dependencies as deps -from geneweaver.api.schemas.apimodels import GeneValueReturn from geneweaver.api.schemas.auth import UserInternal from geneweaver.api.schemas.search import GenesetSearch -from geneweaver.api.services import geneset as genset_service +from geneweaver.api.services import geneset as geneset_service from geneweaver.api.services import publications as publication_service from geneweaver.core.enum import GeneIdentifier, GenesetTier, Species +from geneweaver.core.schema.geneset import GeneValue +from geneweaver.core.schema.publication import Publication from geneweaver.core.schema.score import GenesetScoreType, ScoreType from geneweaver.db import search as db_search -from jax.apiutils import CollectionResponse +from jax.apiutils import CollectionResponse, Response from typing_extensions import Annotated from . import message as api_message @@ -114,9 +115,9 @@ def get_visible_genesets( description=api_message.OFFSET, ), ] = None, -) -> dict: +) -> CollectionResponse: """Get all visible genesets.""" - response = genset_service.get_visible_genesets( + response = geneset_service.get_visible_genesets( cursor=cursor, user=user, gs_id=gs_id, @@ -144,7 +145,7 @@ def get_visible_genesets( raise_http_error(response) - return response + return CollectionResponse(**response) @router.get("/search") @@ -175,20 +176,20 @@ def get_geneset( cursor: Optional[deps.Cursor] = Depends(deps.cursor), gene_id_type: Optional[GeneIdentifier] = None, in_threshold: Optional[bool] = None, -) -> dict: +) -> Response: """Get a geneset by ID. Optional filter results by gene identifier type.""" if gene_id_type: - response = genset_service.get_geneset_w_gene_id_type( + response = geneset_service.get_geneset_w_gene_id_type( cursor, geneset_id, user, gene_id_type ) else: - response = genset_service.get_geneset( + response = geneset_service.get_geneset( cursor=cursor, geneset_id=geneset_id, user=user, in_threshold=in_threshold ) raise_http_error(response) - return response + return Response(response) @router.get("/{geneset_id}/values") @@ -200,9 +201,9 @@ def get_geneset_values( cursor: Optional[deps.Cursor] = Depends(deps.cursor), gene_id_type: Optional[GeneIdentifier] = None, in_threshold: Optional[bool] = None, -) -> GeneValueReturn: +) -> CollectionResponse[GeneValue]: """Get geneset gene values by geneset ID.""" - response = genset_service.get_geneset_gene_values( + response = geneset_service.get_geneset_gene_values( cursor=cursor, geneset_id=geneset_id, user=user, @@ -217,7 +218,7 @@ def get_geneset_values( status_code=404, detail=api_message.INACCESSIBLE_OR_FORBIDDEN ) - return response + return CollectionResponse(**response) @router.get("/{geneset_id}/file", response_class=FileResponse) @@ -236,11 +237,11 @@ def get_export_geneset_by_id_type( # Validate gene identifier type if gene_id_type: - response = genset_service.get_geneset_w_gene_id_type( + response = geneset_service.get_geneset_w_gene_id_type( cursor, geneset_id, user, gene_id_type ) else: - response = genset_service.get_geneset(cursor, geneset_id, user) + response = geneset_service.get_geneset(cursor, geneset_id, user) if "error" in response: raise_http_error(response) @@ -276,15 +277,15 @@ def get_geneset_metadata( user: deps.OptionalFullUserDep, cursor: Optional[deps.Cursor] = Depends(deps.cursor), include_pub_info: Optional[bool] = False, -) -> dict: +) -> Response: """Get a geneset metadata by geneset id.""" - response = genset_service.get_geneset_metadata( + response = geneset_service.get_geneset_metadata( cursor, geneset_id, user, include_pub_info ) raise_http_error(response) - return response + return Response(**response) @router.get("/{geneset_id}/publication") @@ -294,14 +295,15 @@ def get_publication_for_geneset( ], user: deps.OptionalFullUserDep, cursor: Optional[deps.Cursor] = Depends(deps.cursor), -) -> dict: +) -> Response[Publication]: """Get the publication associated with the geneset.""" - geneset_resp = genset_service.get_geneset_metadata(cursor, geneset_id, user, True) + geneset_resp = geneset_service.get_geneset_metadata(cursor, geneset_id, user, True) if "error" in geneset_resp: raise_http_error(geneset_resp) - geneset = geneset_resp.get("geneset") + geneset = geneset_resp.get("object") + if geneset is None: raise HTTPException(status_code=404, detail=api_message.RECORD_NOT_FOUND_ERROR) @@ -314,7 +316,7 @@ def get_publication_for_geneset( if pub_resp is None: raise HTTPException(status_code=404, detail=api_message.RECORD_NOT_FOUND_ERROR) - return pub_resp + return Response[Publication](pub_resp) @router.put("/{geneset_id}/threshold", status_code=204) @@ -327,7 +329,7 @@ def put_geneset_threshold( cursor: Optional[deps.Cursor] = Depends(deps.cursor), ) -> None: """Set geneset threshold for geneset owner.""" - response = genset_service.update_geneset_threshold( + response = geneset_service.update_geneset_threshold( cursor, geneset_id, gene_score_type, user ) @@ -359,15 +361,15 @@ def get_geneset_ontology_terms( description=api_message.OFFSET, ), ] = None, -) -> dict: +) -> CollectionResponse: """Get geneset ontology terms.""" - terms_resp = genset_service.get_geneset_ontology_terms( + terms_resp = geneset_service.get_geneset_ontology_terms( cursor, geneset_id, user, limit, offset ) raise_http_error(terms_resp) - return terms_resp + return CollectionResponse(**terms_resp) @router.put("/{geneset_id}/ontologies", status_code=204) @@ -386,7 +388,7 @@ def put_geneset_ontology_term( cursor: Optional[deps.Cursor] = Depends(deps.cursor), ) -> None: """Set geneset threshold for geneset owner.""" - response = genset_service.add_geneset_ontology_term( + response = geneset_service.add_geneset_ontology_term( cursor=cursor, geneset_id=geneset_id, term_ref_id=ontology_id, @@ -412,7 +414,7 @@ def delete_geneset_ontology_term( cursor: Optional[deps.Cursor] = Depends(deps.cursor), ) -> None: """Set geneset threshold for geneset owner.""" - response = genset_service.delete_geneset_ontology_term( + response = geneset_service.delete_geneset_ontology_term( cursor=cursor, geneset_id=geneset_id, term_ref_id=ontology_id, diff --git a/src/geneweaver/api/controller/monitors.py b/src/geneweaver/api/controller/monitors.py index 40feb9a..163f059 100644 --- a/src/geneweaver/api/controller/monitors.py +++ b/src/geneweaver/api/controller/monitors.py @@ -6,6 +6,7 @@ from fastapi import APIRouter, Depends, Query from geneweaver.api import dependencies as deps from geneweaver.api.services import monitors as monitors_service +from jax.apiutils import Response from typing_extensions import Annotated from . import message as api_message @@ -19,7 +20,7 @@ def get_health_check( db_health_check: Annotated[ Optional[bool], Query(description=api_message.CHECK_DB_HEALTH) ] = False, -) -> dict: +) -> Response: """Return 200 API response if reachable and optionally check db health.""" response = { "status": "UP", @@ -31,4 +32,4 @@ def get_health_check( db_health_response = monitors_service.check_db_health(cursor) response["DB_status"] = db_health_response - return response + return Response(response) diff --git a/src/geneweaver/api/controller/publications.py b/src/geneweaver/api/controller/publications.py index 1595093..8e4b2b6 100644 --- a/src/geneweaver/api/controller/publications.py +++ b/src/geneweaver/api/controller/publications.py @@ -8,6 +8,7 @@ from geneweaver.api.schemas.auth import UserInternal from geneweaver.api.services import publications as publication_service from geneweaver.core.schema.publication import Publication +from jax.apiutils import CollectionResponse, Response from typing_extensions import Annotated from . import message as api_message @@ -51,7 +52,7 @@ def get_publication( description=api_message.OFFSET, ), ] = None, -) -> dict: +) -> CollectionResponse[Publication]: """Get all publication publications with optional filters.""" response = publication_service.get( cursor, @@ -70,7 +71,7 @@ def get_publication( offset=offset, ) - return response + return CollectionResponse(**response) @router.get("/{publication_id}") @@ -80,11 +81,11 @@ def get_publication_by_id( ], as_pubmed_id: Optional[bool] = True, cursor: Optional[deps.Cursor] = Depends(deps.cursor), -) -> Publication: +) -> Response[Publication]: """Get a publication by id.""" if as_pubmed_id: response = publication_service.get_publication_by_pubmed_id( - cursor, publication_id + cursor, str(publication_id) ) else: response = publication_service.get_publication(cursor, publication_id) @@ -92,7 +93,7 @@ def get_publication_by_id( if response is None: raise HTTPException(status_code=404, detail=api_message.RECORD_NOT_FOUND_ERROR) - return response + return Response(response) @router.put("/{publication_id}") @@ -102,10 +103,10 @@ def add_publication( ], user: UserInternal = Security(deps.full_user), cursor: Optional[deps.Cursor] = Depends(deps.cursor), -) -> NewPubmedRecord: +) -> Response[NewPubmedRecord]: """Add pubmed publication endpoint.""" response = publication_service.add_pubmed_record( - cursor=cursor, user=user, pubmed_id=publication_id + cursor=cursor, user=user, pubmed_id=str(publication_id) ) if "error" in response: @@ -120,4 +121,4 @@ def add_publication( else: raise HTTPException(status_code=500, detail=api_message.UNEXPECTED_ERROR) - return response + return Response(response) diff --git a/src/geneweaver/api/controller/search.py b/src/geneweaver/api/controller/search.py index aaf22d6..ff2ecdd 100644 --- a/src/geneweaver/api/controller/search.py +++ b/src/geneweaver/api/controller/search.py @@ -4,10 +4,11 @@ from fastapi import APIRouter, Depends, Query, Security from geneweaver.api import dependencies as deps -from geneweaver.api.schemas.apimodels import CombinedSearchResponse, GsPubSearchType +from geneweaver.api.schemas.apimodels import GsPubSearchType from geneweaver.api.schemas.auth import UserInternal from geneweaver.api.services import publications as publication_service from geneweaver.db import search as db_search +from jax.apiutils import Response from typing_extensions import Annotated from . import message as api_message @@ -41,7 +42,7 @@ def search( description=api_message.OFFSET, ), ] = None, -) -> CombinedSearchResponse: +) -> Response: """Search genesets and publications.""" search_results = {} if "genesets" in entities: @@ -65,6 +66,6 @@ def search( search_results["publications"] = pub_response.get("data") - return CombinedSearchResponse( + return Response( object=search_results, ) diff --git a/src/geneweaver/api/controller/species.py b/src/geneweaver/api/controller/species.py index 04bcbb1..0ae9d21 100644 --- a/src/geneweaver/api/controller/species.py +++ b/src/geneweaver/api/controller/species.py @@ -4,10 +4,10 @@ from fastapi import APIRouter, Depends, Query from geneweaver.api import dependencies as deps -from geneweaver.api.schemas.apimodels import SpeciesReturn from geneweaver.api.services import species as species_service from geneweaver.core.enum import GeneIdentifier, Species from geneweaver.core.schema.species import Species as SpeciesSchema +from jax.apiutils import CollectionResponse, Response from typing_extensions import Annotated router = APIRouter(prefix="/species", tags=["species"]) @@ -20,18 +20,18 @@ def get_species( Optional[int], Query(format="int64", minimum=0, maxiumum=9223372036854775807) ] = None, reference_gene_id_type: Optional[GeneIdentifier] = None, -) -> SpeciesReturn: +) -> CollectionResponse[SpeciesSchema]: """Get species.""" response = species_service.get_species(cursor, taxonomy_id, reference_gene_id_type) - return response + return CollectionResponse(**response) @router.get("/{species_id}") def get_species_by_id( species_id: Species, cursor: Optional[deps.Cursor] = Depends(deps.cursor) -) -> SpeciesSchema: +) -> Response[SpeciesSchema]: """Get species.""" response = species_service.get_species_by_id(cursor, species_id) - return response + return Response(response) diff --git a/src/geneweaver/api/services/geneset.py b/src/geneweaver/api/services/geneset.py index c908a15..cd61d19 100644 --- a/src/geneweaver/api/services/geneset.py +++ b/src/geneweaver/api/services/geneset.py @@ -200,7 +200,7 @@ def get_geneset_metadata( if len(results) <= 0: return {"error": True, "message": message.INACCESSIBLE_OR_FORBIDDEN} - return {"geneset": results[0]} + return {"object": results[0]} except Exception as err: logger.error(err) diff --git a/tests/controllers/conftest.py b/tests/controllers/conftest.py index dfed6d6..b8247f7 100644 --- a/tests/controllers/conftest.py +++ b/tests/controllers/conftest.py @@ -4,6 +4,7 @@ import psycopg import pytest +from fastapi import FastAPI from fastapi.testclient import TestClient from geneweaver.api.core.config_class import GeneweaverAPIConfig @@ -42,7 +43,7 @@ def mock_settings(monkeypatch) -> GeneweaverAPIConfig: @pytest.fixture() -def client(mock_settings) -> TestClient: +def app(mock_settings) -> FastAPI: """Provide a mocked FastAPI application. returns: A mocked FastAPI application. @@ -50,8 +51,16 @@ def client(mock_settings) -> TestClient: from geneweaver.api.dependencies import cursor, full_user from geneweaver.api.main import app - test_app = TestClient(app) - app.dependency_overrides.update({full_user: mock_full_user, cursor: mock_cursor}) - return test_app + return app + + +@pytest.fixture() +def client(mock_settings, app) -> TestClient: + """Provide a mocked FastAPI application. + + returns: A mocked FastAPI application. + """ + test_client = TestClient(app) + return test_client diff --git a/tests/controllers/test_genes.py b/tests/controllers/test_genes.py index 51b8c69..2739225 100644 --- a/tests/controllers/test_genes.py +++ b/tests/controllers/test_genes.py @@ -3,6 +3,8 @@ import json from unittest.mock import patch +from jax.apiutils import Response + from tests.data import test_gene_homolog_data, test_gene_mapping_data, test_genes_data # genes @@ -190,4 +192,4 @@ def test_valid_get_preferred_gene_req(mock_gene_call, client): response = client.get(url="/api/genes/1000/preferred") assert response.status_code == 200 - assert response.json() == gene_preferred_resp_1 + assert response.json() == Response(gene_preferred_resp_1).model_dump() diff --git a/tests/controllers/test_genesets.py b/tests/controllers/test_genesets.py index 0868a10..aca972b 100644 --- a/tests/controllers/test_genesets.py +++ b/tests/controllers/test_genesets.py @@ -23,7 +23,7 @@ def test_get_geneset_response(mock_get_genenset, client): response = client.get("/api/genesets/1234") assert response.status_code == 200 - assert response.json() == geneset_by_id_resp.get("geneset") + assert response.json()["object"] == geneset_by_id_resp.get("geneset") @patch("geneweaver.api.services.geneset.get_geneset") @@ -33,7 +33,7 @@ def test_get_geneset_in_threshold_response(mock_get_genenset, client): response = client.get("/api/genesets/1234?in_threshold=True") assert response.status_code == 200 - assert response.json() == geneset_by_id_resp.get("geneset") + assert response.json()["object"] == geneset_by_id_resp.get("geneset") @patch("geneweaver.api.services.geneset.get_geneset") @@ -56,8 +56,8 @@ def test_get_geneset_w_gene_id_type(mock_service_get_geneset_w_gene_id_type, cli mock_service_get_geneset_w_gene_id_type.return_value = geneset_w_gene_id_type_resp response = client.get("/api/genesets/1234?gene_id_type=2") - assert response.json() == geneset_w_gene_id_type_resp assert response.status_code == 200 + assert response.json()["object"] == geneset_w_gene_id_type_resp @patch("geneweaver.api.services.geneset.get_geneset_w_gene_id_type") @@ -97,11 +97,11 @@ def test_export_geneset_errors(mock_get_geneset, client): @patch("geneweaver.api.services.geneset.get_geneset_metadata") def test_get_geneset_metadata(mock_get_genenset, client): """Test get geneset metadata.""" - mock_get_genenset.return_value = geneset_by_id_resp.get("geneset") + mock_get_genenset.return_value = {"object": geneset_by_id_resp.get("geneset")} response = client.get("/api/genesets/1234/metadata") assert response.status_code == 200 - assert response.json() == geneset_by_id_resp.get("geneset") + assert response.json()["object"] == geneset_by_id_resp.get("geneset") @patch("geneweaver.api.services.geneset.get_geneset_metadata") @@ -124,11 +124,11 @@ def test_get_geneset_metadata_errors(mock_get_geneset_metadata, client): @patch("geneweaver.api.services.geneset.get_geneset_metadata") def test_get_geneset_metadata_w_publication(mock_get_genenset, client): """Test get geneset ID data response.""" - mock_get_genenset.return_value = geneset_metadata_w_pub_info + mock_get_genenset.return_value = {"object": geneset_metadata_w_pub_info["geneset"]} response = client.get("/api/genesets/1234/metadata?include_pub_info=true") assert response.status_code == 200 - assert response.json() == geneset_metadata_w_pub_info + assert response.json()["object"] == geneset_metadata_w_pub_info["geneset"] @patch("geneweaver.api.services.geneset.get_geneset_metadata") @@ -137,14 +137,16 @@ def test_publication_for_geneset( mock_pub_service_call, mock_get_genenset_metadata, client ): """Test valid url request to get publication for a geneset.""" - mock_get_genenset_metadata.return_value = geneset_metadata_w_pub_info + mock_get_genenset_metadata.return_value = { + "object": geneset_metadata_w_pub_info["geneset"] + } mock_pub_service_call.return_value = publication_by_id_resp response = client.get("/api/genesets/1234/publication") assert response.status_code == 200 - assert response.json() == publication_by_id_resp + assert response.json()["object"] == publication_by_id_resp @patch("geneweaver.api.services.geneset.get_geneset_metadata") @@ -189,11 +191,13 @@ def test_get_publication_errors(mock_get_geneset_metadata, client): @patch("geneweaver.api.services.geneset.get_visible_genesets") def test_get_visible_geneset_response(mock_get_visible_genesets, client): """Test get geneset ID data response.""" - mock_get_visible_genesets.return_value = geneset_by_id_resp.get("geneset") + mock_get_visible_genesets.return_value = { + "data": [geneset_by_id_resp.get("geneset")] + } response = client.get("/api/genesets?gs_id=1234") assert response.status_code == 200 - assert response.json() == geneset_by_id_resp.get("geneset") + assert response.json()["data"][0] == geneset_by_id_resp.get("geneset") @patch("geneweaver.api.services.geneset.get_visible_genesets") @@ -220,7 +224,7 @@ def test_get_geneset_gene_values_url_response(mock_get_geneset_gene_values, clie response = client.get("/api/genesets/1234/values") assert response.status_code == 200 - assert response.json() == geneset_genes_values_resp + assert response.json()["data"] == geneset_genes_values_resp["data"] @patch("geneweaver.api.services.geneset.get_geneset_gene_values") @@ -249,7 +253,7 @@ def test_get_geneset_gene_values_w_gs_id_type(mock_get_geneset_gene_values, clie response = client.get("/api/genesets/1234/values?gene_id_type=2") assert response.status_code == 200 - assert response.json() == geneset_genes_values_resp + assert response.json()["data"] == geneset_genes_values_resp["data"] @patch("geneweaver.api.services.geneset.get_geneset_gene_values") @@ -261,7 +265,7 @@ def test_get_geneset_gene_values_w_gs_id_type_and_in_threshold( response = client.get("/api/genesets/1234/values?gene_id_type=2&in_threshold=True") assert response.status_code == 200 - assert response.json() == geneset_genes_values_resp + assert response.json()["data"] == geneset_genes_values_resp["data"] @patch("geneweaver.api.services.geneset.update_geneset_threshold") @@ -297,7 +301,7 @@ def test_get_geneset_ontology_terms_response(mock_get_genenset_onto_terms, clien response = client.get("/api/genesets/1234/ontologies") assert response.status_code == 200 - assert response.json() == mock_resp + assert response.json()["data"] == mock_resp["data"] @patch("geneweaver.api.services.geneset.get_geneset_ontology_terms") @@ -418,11 +422,13 @@ def test_delete_geneset_ontology_terms_errors(mock_delete_genenset_onto_terms, c @patch("geneweaver.api.services.geneset.get_visible_genesets") def test_get_geneset_by_score_type(mock_get_visible_genesets, score_type, client): """Test get geneset data response.""" - mock_get_visible_genesets.return_value = geneset_by_id_resp.get("geneset") + mock_get_visible_genesets.return_value = { + "data": [geneset_by_id_resp.get("geneset")] + } response = client.get("/api/genesets?" + score_type) assert response.status_code == 200 - assert response.json() == geneset_by_id_resp.get("geneset") + assert response.json()["data"][0] == geneset_by_id_resp.get("geneset") @pytest.mark.parametrize( @@ -443,25 +449,29 @@ def test_get_geneset_by_invalid_score_type( @patch("geneweaver.api.services.geneset.get_visible_genesets") def test_get_geneset_by_create_date(mock_get_visible_genesets, client): """Test get geneset data response.""" - mock_get_visible_genesets.return_value = geneset_by_id_resp.get("geneset") + mock_get_visible_genesets.return_value = { + "data": [geneset_by_id_resp.get("geneset")] + } response = client.get( "/api/genesets?created_after=2023-08-01&created_before=2024-07-01" ) assert response.status_code == 200 - assert response.json() == geneset_by_id_resp.get("geneset") + assert response.json().get("data")[0] == geneset_by_id_resp.get("geneset") @patch("geneweaver.api.services.geneset.get_visible_genesets") def test_get_geneset_by_update_date(mock_get_visible_genesets, client): """Test get geneset data response.""" - mock_get_visible_genesets.return_value = geneset_by_id_resp.get("geneset") + mock_get_visible_genesets.return_value = { + "data": [geneset_by_id_resp.get("geneset")] + } response = client.get( "/api/genesets?updated_after=2023-08-01&updated_before=2024-07-01" ) assert response.status_code == 200 - assert response.json() == geneset_by_id_resp.get("geneset") + assert response.json().get("data")[0] == geneset_by_id_resp.get("geneset") @pytest.mark.parametrize("created_before", ["20-23-20", "08-01-2023", "80/01/2022"]) diff --git a/tests/controllers/test_monitor.py b/tests/controllers/test_monitor.py index 90ac327..af72e3a 100644 --- a/tests/controllers/test_monitor.py +++ b/tests/controllers/test_monitor.py @@ -20,4 +20,6 @@ def test_valid_url_req(mock_db_heath_service_call, client): url="/api/monitors/servers/health", params={"db_health_check": True} ) assert response.status_code == 200 - assert response.json().get("DB_status") == db_health_status.get("DB_status") + assert response.json().get("object").get("DB_status") == db_health_status.get( + "DB_status" + ) diff --git a/tests/controllers/test_publications.py b/tests/controllers/test_publications.py index 7278e9e..bd27072 100644 --- a/tests/controllers/test_publications.py +++ b/tests/controllers/test_publications.py @@ -21,7 +21,7 @@ def test_valid_url_req(mock_pub_service_call, client): response = client.get(url="/api/publications/123", params={"as_pubmed_id": False}) assert response.status_code == 200 - assert response.json() == publication_by_id_resp + assert response.json().get("object") == publication_by_id_resp @patch("geneweaver.api.services.publications.get_publication_by_pubmed_id") @@ -34,7 +34,7 @@ def test_valid_pubmed_url_req(mock_pub_service_call, client): ) assert response.status_code == 200 - assert response.json() == publication_by_pubmed_id_resp + assert response.json().get("object") == publication_by_pubmed_id_resp @patch("geneweaver.api.services.publications.get_publication") @@ -78,7 +78,7 @@ def test_add_pubmed_valid_url_req(mock_pub_service_call, client): response = client.put(url="/api/publications/1234") assert response.status_code == 200 - assert response.json() == add_pubmed_resp + assert response.json().get("object") == add_pubmed_resp @patch("geneweaver.api.services.publications.add_pubmed_record") @@ -136,4 +136,4 @@ def test_get_req(mock_pub_service_call, client): ) assert response.status_code == 200 - assert response.json() == get_publications + assert response.json().get("data") == get_publications.get("data") diff --git a/tests/controllers/test_return_schemas.py b/tests/controllers/test_return_schemas.py new file mode 100644 index 0000000..7e6b4e8 --- /dev/null +++ b/tests/controllers/test_return_schemas.py @@ -0,0 +1,72 @@ +"""Test that all GET endpoints return one of the standard responses.""" + +import asyncio +from typing import get_args, get_origin, get_type_hints + +from fastapi import FastAPI +from fastapi.routing import APIRoute +from jax.apiutils import CollectionResponse, Response, StreamingResponse + + +def get_return_type(route: APIRoute) -> type: + """Extract the return type from a FastAPI route's response model.""" + # Get the original function (beneath FastAPI decorators) + original_func = route.endpoint + + # Get type hints for the function + type_hints = get_type_hints(original_func) + + # FastAPI functions should have a return type annotation + if "return" not in type_hints: + raise ValueError(f"Route {route.path} is missing return type annotation") + + return type_hints["return"] + + +def is_valid_return_type(typ: type) -> bool: + """Check if a type is one of the standard response types. + + Check if a type is either Response, CollectionResponse, StreamingResponse, + or an Optional/Union containing them. + + Also handles async return types. + """ + # Handle Optional/Union types + origin = get_origin(typ) + if origin is not None: + # If it's a Union/Optional, check all possible types + if origin in (Union, types.UnionType): # noqa: F821 + return any(is_valid_return_type(arg) for arg in get_args(typ)) + # Handle async return types + if asyncio.iscoroutinefunction(origin): + # For async functions, check the actual return type (last type arg) + args = get_args(typ) + if args: + return is_valid_return_type(args[-1]) + + # Check if type is Response or CollectionResponse + return any( + issubclass(typ, expected_type) + for expected_type in (Response, CollectionResponse, StreamingResponse) + ) + + +def test_get_endpoint_return_types(app: FastAPI): + """Test that all GET endpoints return either Response or CollectionResponse.""" + invalid_routes = [] + + for route in app.routes: + if not isinstance(route, APIRoute) or route.methods != {"GET"}: + continue + + try: + return_type = get_return_type(route) + if not is_valid_return_type(return_type): + invalid_routes.append( + f"GET Route {route.path} ({route.name}) returns {return_type}" + "which is not a Response or CollectionResponse" + ) + except ValueError as e: + invalid_routes.append(str(e)) + + assert not invalid_routes, "\n".join(invalid_routes) diff --git a/tests/controllers/test_species.py b/tests/controllers/test_species.py index 2f9b4b1..28dd18b 100644 --- a/tests/controllers/test_species.py +++ b/tests/controllers/test_species.py @@ -69,4 +69,6 @@ def test_valid_url_species_by_id(mock_species_service_call, client): response = client.get(url="/api/species/5") assert response.status_code == 200 - _validate_species_response(response.json(), species_by_gene_id_type_flybase) + _validate_species_response( + response.json().get("object"), species_by_gene_id_type_flybase + ) diff --git a/tests/data/publications.json b/tests/data/publications.json index a94407c..9c2fe5c 100644 --- a/tests/data/publications.json +++ b/tests/data/publications.json @@ -49,8 +49,8 @@ "volume": "3", "pages": "565-76", "month": null, - "year": "1975", - "pubmed_id": "1234" + "year": 1975, + "pubmed_id": 1234 }, { "id": 79323, @@ -61,8 +61,8 @@ "volume": "3", "pages": "565-76", "month": null, - "year": "1975", - "pubmed_id": "1234" + "year": 1975, + "pubmed_id": 1234 }, { "id": 79324, @@ -73,8 +73,8 @@ "volume": "3", "pages": "565-76", "month": null, - "year": "1975", - "pubmed_id": "1234" + "year": 1975, + "pubmed_id": 1234 }, { "id": 79325, @@ -85,8 +85,8 @@ "volume": "3", "pages": "565-76", "month": null, - "year": "1975", - "pubmed_id": "1234" + "year": 1975, + "pubmed_id": 1234 }, { "id": 79327, @@ -97,8 +97,8 @@ "volume": "3", "pages": "565-76", "month": null, - "year": "1975", - "pubmed_id": "1234" + "year": 1975, + "pubmed_id": 1234 } ] } diff --git a/tests/services/test_genset.py b/tests/services/test_genset.py index d4a9b68..ffd2721 100644 --- a/tests/services/test_genset.py +++ b/tests/services/test_genset.py @@ -171,16 +171,16 @@ def test_get_geneset_metadata(mock_db_geneset): mock_db_geneset.get.return_value = [geneset_by_id_resp.get("geneset")] response = geneset.get_geneset_metadata(None, 1234, mock_user) - assert "geneset" in response - assert response.get("geneset") == geneset_by_id_resp["geneset"] + assert "object" in response + assert response.get("object") == geneset_by_id_resp["geneset"] response = geneset.get_geneset_metadata(None, 1234, None) - assert "geneset" in response - assert response.get("geneset") == geneset_by_id_resp["geneset"] + assert "object" in response + assert response.get("object") == geneset_by_id_resp["geneset"] response = geneset.get_geneset_metadata(None, 1234, User(id=None)) - assert "geneset" in response - assert response.get("geneset") == geneset_by_id_resp["geneset"] + assert "object" in response + assert response.get("object") == geneset_by_id_resp["geneset"] @patch("geneweaver.api.services.geneset.db_geneset") @@ -189,7 +189,7 @@ def test_get_geneset_metadata_w_pub_info(mock_db_geneset): mock_db_geneset.get.return_value = [geneset_metadata_w_pub_info] response = geneset.get_geneset_metadata(None, 1234, mock_user, True) - assert response.get("geneset") == geneset_metadata_w_pub_info + assert response.get("object") == geneset_metadata_w_pub_info @patch("geneweaver.api.services.geneset.db_geneset")