From d10e132b4b0792c680c59cf7103f8f6474f1075d Mon Sep 17 00:00:00 2001 From: Matias Chediek Date: Tue, 11 Jul 2023 15:38:13 -0300 Subject: [PATCH 1/6] ClickUp-85ztfjmg3 Add dockerfile poetry bdd makefile dependencies --- .gitignore | 2 + .vscode/settings.json | 9 + Dockerfile | 47 + makefile | 17 + poetry.lock | 962 ++++++++++++++++++ pyproject.toml | 26 + src/app/.keep | 0 src/tests/__init__.py | 0 .../integration_tests/features/__init__.py | 0 .../features/arguments.feature | 7 + .../features/test_publish_article.py | 19 + src/tests/unit_tests/__init__.py | 0 src/tests/unit_tests/test_sysexit.py | 10 + 13 files changed, 1099 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 Dockerfile create mode 100644 makefile create mode 100644 poetry.lock create mode 100644 pyproject.toml create mode 100644 src/app/.keep create mode 100644 src/tests/__init__.py create mode 100644 src/tests/integration_tests/features/__init__.py create mode 100644 src/tests/integration_tests/features/arguments.feature create mode 100644 src/tests/integration_tests/features/test_publish_article.py create mode 100644 src/tests/unit_tests/__init__.py create mode 100644 src/tests/unit_tests/test_sysexit.py diff --git a/.gitignore b/.gitignore index 68bc17f..d2b1ff6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +reports/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e8701f0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "makefile.extensionOutputFolder": "./.vscode", + "python.linting.enabled": true, + "python.linting.mypyEnabled": true, + "python.formatting.provider": "black" +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7b1abff --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +FROM python:3.11-slim-buster + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +ARG GH_USER +ARG GH_PAT + +# Configure env +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + POETRY_VERSION=1.4.2 \ + POETRY_HOME="/usr/src/poetry" \ + POETRY_CACHE_DIR="/usr/src/poetry/cache" \ + POETRY_VIRTUALENVS_IN_PROJECT="true" + +ENV PATH="$POETRY_HOME/bin:$PATH" + +# Install OS dependencies +RUN set -ex; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + build-essential \ + git \ + vim \ + openssh-server \ + curl; \ + \ + apt-get clean; \ + rm -rf /var/lib/apt/lists/* + +# Install Poetry +RUN curl -sSL https://install.python-poetry.org | python3 - && \ + poetry config http-basic.fastapi-auth-utils "$GH_USER" "$GH_PAT" + +WORKDIR /code + +COPY pyproject.toml poetry.lock ./ + +# Install dependencies +RUN poetry install + +# Set commands +COPY src/app . + +ENTRYPOINT ["/usr/src/entrypoint"] + +CMD ["uvicorn", "app.main:app", "--host 0.0.0.0", "--port 8000", "--reload"] \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..88d9de4 --- /dev/null +++ b/makefile @@ -0,0 +1,17 @@ +PY = python +VENV = .venv +BIN=$(VENV)/bin + +setup: requirements.txt + $(PY) -m venv $(VENV) + $(BIN)/pip install --upgrade -r requirements.txt + +build: + $(BIN)/pylint ./src/app + +tests: + $(BIN)/pytest --cov=src/app/ --cov-report=html:reports/html_dir --cov-report=xml:reports/coverage.xml --feature features -vv src/tests/ + +clean: + rm -rf __pycache__ + rm -rf .venv diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..6e8e598 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,962 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.5.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.7" +files = [ + {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"}, + {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + +[[package]] +name = "anyio" +version = "3.7.1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, + {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, +] + +[package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] + +[[package]] +name = "astroid" +version = "2.15.6" +description = "An abstract syntax tree for Python with inference support." +optional = false +python-versions = ">=3.7.2" +files = [ + {file = "astroid-2.15.6-py3-none-any.whl", hash = "sha256:389656ca57b6108f939cf5d2f9a2a825a3be50ba9d589670f393236e0a03b91c"}, + {file = "astroid-2.15.6.tar.gz", hash = "sha256:903f024859b7c7687d7a7f3a3f73b17301f8e42dfd9cc9df9d4418172d3e2dbd"}, +] + +[package.dependencies] +lazy-object-proxy = ">=1.4.0" +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} +wrapt = [ + {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, + {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.2.7" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, + {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, + {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, + {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, + {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, + {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, + {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, + {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, + {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, + {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, + {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, + {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, + {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, + {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, + {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, + {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, + {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, + {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "dill" +version = "0.3.6" +description = "serialize all of python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dill-0.3.6-py3-none-any.whl", hash = "sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0"}, + {file = "dill-0.3.6.tar.gz", hash = "sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373"}, +] + +[package.extras] +graph = ["objgraph (>=1.7.2)"] + +[[package]] +name = "dnspython" +version = "2.3.0" +description = "DNS toolkit" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "dnspython-2.3.0-py3-none-any.whl", hash = "sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46"}, + {file = "dnspython-2.3.0.tar.gz", hash = "sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9"}, +] + +[package.extras] +curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] +dnssec = ["cryptography (>=2.6,<40.0)"] +doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.11.0)"] +doq = ["aioquic (>=0.9.20)"] +idna = ["idna (>=2.1,<4.0)"] +trio = ["trio (>=0.14,<0.23)"] +wmi = ["wmi (>=1.5.1,<2.0.0)"] + +[[package]] +name = "exceptiongroup" +version = "1.1.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, + {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "fastapi" +version = "0.100.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.7" +files = [ + {file = "fastapi-0.100.0-py3-none-any.whl", hash = "sha256:271662daf986da8fa98dc2b7c7f61c4abdfdccfb4786d79ed8b2878f172c6d5f"}, + {file = "fastapi-0.100.0.tar.gz", hash = "sha256:acb5f941ea8215663283c10018323ba7ea737c571b67fc7e88e9469c7eb1d12e"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<3.0.0" +starlette = ">=0.27.0,<0.28.0" +typing-extensions = ">=4.5.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "isort" +version = "5.12.0" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, +] + +[package.extras] +colors = ["colorama (>=0.4.3)"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] +plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] + +[[package]] +name = "lazy-object-proxy" +version = "1.9.0" +description = "A fast and thorough lazy object proxy." +optional = false +python-versions = ">=3.7" +files = [ + {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, + {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, + {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, + {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, + {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, + {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, +] + +[[package]] +name = "mako" +version = "1.2.4" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Mako-1.2.4-py3-none-any.whl", hash = "sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818"}, + {file = "Mako-1.2.4.tar.gz", hash = "sha256:d60a3903dc3bb01a18ad6a89cdbe2e4eadc69c0bc8ef1e3773ba53d44c3f7a34"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markupsafe" +version = "2.1.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "parse" +version = "1.19.1" +description = "parse() is the opposite of format()" +optional = false +python-versions = "*" +files = [ + {file = "parse-1.19.1-py2.py3-none-any.whl", hash = "sha256:371ed3800dc63983832159cc9373156613947707bc448b5215473a219dbd4362"}, + {file = "parse-1.19.1.tar.gz", hash = "sha256:cc3a47236ff05da377617ddefa867b7ba983819c664e1afe46249e5b469be464"}, +] + +[[package]] +name = "parse-type" +version = "0.6.2" +description = "Simplifies to build parse types based on the parse module" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*" +files = [ + {file = "parse_type-0.6.2-py2.py3-none-any.whl", hash = "sha256:06d39a8b70fde873eb2a131141a0e79bb34a432941fb3d66fad247abafc9766c"}, + {file = "parse_type-0.6.2.tar.gz", hash = "sha256:79b1f2497060d0928bc46016793f1fca1057c4aacdf15ef876aa48d75a73a355"}, +] + +[package.dependencies] +parse = {version = ">=1.18.0", markers = "python_version >= \"3.0\""} +six = ">=1.15" + +[package.extras] +develop = ["build (>=0.5.1)", "coverage (>=4.4)", "pylint", "pytest (<5.0)", "pytest (>=5.0)", "pytest-cov", "pytest-html (>=1.19.0)", "ruff", "tox (>=2.8,<4.0)", "twine (>=1.13.0)", "virtualenv (<20.22.0)", "virtualenv (>=20.0.0)"] +docs = ["Sphinx (>=1.6)", "sphinx-bootstrap-theme (>=0.6.0)"] +testing = ["pytest (<5.0)", "pytest (>=5.0)", "pytest-html (>=1.19.0)"] + +[[package]] +name = "pika" +version = "1.3.2" +description = "Pika Python AMQP Client Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pika-1.3.2-py3-none-any.whl", hash = "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f"}, + {file = "pika-1.3.2.tar.gz", hash = "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f"}, +] + +[package.extras] +gevent = ["gevent"] +tornado = ["tornado"] +twisted = ["twisted"] + +[[package]] +name = "platformdirs" +version = "3.8.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.8.1-py3-none-any.whl", hash = "sha256:cec7b889196b9144d088e4c57d9ceef7374f6c39694ad1577a0aab50d27ea28c"}, + {file = "platformdirs-3.8.1.tar.gz", hash = "sha256:f87ca4fcff7d2b0f81c6a748a77973d7af0f4d526f98f308477c3c436c74d528"}, +] + +[package.extras] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.2.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pydantic" +version = "2.0.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-2.0.2-py3-none-any.whl", hash = "sha256:f5581e0c79b2ec2fa25a9d30d766629811cdda022107fa73d022ab5578873ae3"}, + {file = "pydantic-2.0.2.tar.gz", hash = "sha256:b802f5245b8576315fe619e5989fd083448fa1258638ef9dac301ca60878396d"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.1.2" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.1.2" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.1.2-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:b4815720c266e832b20e27a7a5f3772bb09fdedb31a9a34bab7b49d98967ef5a"}, + {file = "pydantic_core-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8884a1dbfc5cb8c54b48446ca916d4577c1f4d901126091e4ab25d00194e065f"}, + {file = "pydantic_core-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74a33aa69d476773230396396afb8e11908f8dafdcfd422e746770599a3f889d"}, + {file = "pydantic_core-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af832edd384755826e494ffdcf1fdda86e4babc42a0b18d342943fb18181040e"}, + {file = "pydantic_core-2.1.2-cp310-cp310-manylinux_2_24_armv7l.whl", hash = "sha256:017700236ea2e7afbef5d3803559c80bd8720306778ebd49268de7ce9972e83e"}, + {file = "pydantic_core-2.1.2-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:c2d00a96fdf26295c6f25eaf9e4a233f353146a73713cd97a5f5dc6090c3aef2"}, + {file = "pydantic_core-2.1.2-cp310-cp310-manylinux_2_24_s390x.whl", hash = "sha256:2575664f0a559a7b951a518f6f34c23cab7190f34f8220b8c8218c4f403147ee"}, + {file = "pydantic_core-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24c3c9180a2d19d640bacc2d00f497a9a1f2abadb2a9ee201b56bb03bc5343bd"}, + {file = "pydantic_core-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:88a56f0f6d020b4d17641f4b4d1f9540a536d4146768d059c430e97bdb485fc1"}, + {file = "pydantic_core-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fa38a76e832743866aed6b715869757074b06357d1a260163ec26d84974245fe"}, + {file = "pydantic_core-2.1.2-cp310-none-win32.whl", hash = "sha256:a772c652603855d7180015849d483a1f539351a263bb9b81bfe85193a33ce124"}, + {file = "pydantic_core-2.1.2-cp310-none-win_amd64.whl", hash = "sha256:b4673d1f29487608d613ebcc5caa99ba15eb58450a7449fb6d800f29d90bebc1"}, + {file = "pydantic_core-2.1.2-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:76c9c55462740d728b344e3a087775846516c3fee31ec56e2075faa7cfcafcbf"}, + {file = "pydantic_core-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb854ec52e6e2e05b83d647695f4d913452fdd45a3dfa8233d7dab5967b3908f"}, + {file = "pydantic_core-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ac140d54da366672f6b91f9a1e8e2d4e7e72720143353501ae886d3fca03272"}, + {file = "pydantic_core-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:818f5cb1b209ab1295087c45717178f4bbbd2bd7eda421f7a119e7b9b736a3cb"}, + {file = "pydantic_core-2.1.2-cp311-cp311-manylinux_2_24_armv7l.whl", hash = "sha256:db4564aea8b3cb6cf1e5f3fd80f1ced73a255d492396d1bd8abd688795b34d63"}, + {file = "pydantic_core-2.1.2-cp311-cp311-manylinux_2_24_ppc64le.whl", hash = "sha256:2ca2d2d5ab65fb40dd05259965006edcc62a9d9b30102737c0a6f45bcbd254e8"}, + {file = "pydantic_core-2.1.2-cp311-cp311-manylinux_2_24_s390x.whl", hash = "sha256:7c7ad8958aadfbcd664078002246796ecd5566b64b22f6af4fd1bbcec6bf8f60"}, + {file = "pydantic_core-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:080a7af828388284a68ad7d3d3eac3bcfff6a580292849aff087e7d556ec42d4"}, + {file = "pydantic_core-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bad7029fb2251c1ac7d3acdd607e540d40d137a7d43a5e5acdcfdbd38db3fc0a"}, + {file = "pydantic_core-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1635a37137fafbc6ee0a8c879857e05b30b1aabaa927e653872b71f1501b1502"}, + {file = "pydantic_core-2.1.2-cp311-none-win32.whl", hash = "sha256:eb4301f009a44bb5db5edfe4e51a8175a4112b566baec07f4af8b1f8cb4649a2"}, + {file = "pydantic_core-2.1.2-cp311-none-win_amd64.whl", hash = "sha256:ebf583f4d9b52abd15cc59e5f6eeca7e3e9741c6ea62d8711c00ac3acb067875"}, + {file = "pydantic_core-2.1.2-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:90b06bb47e60173d24c7cb79670aa8dd6081797290353b9d3c66d3a23e88eb34"}, + {file = "pydantic_core-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e5761ce986ec709897b1b965fad9743f301500434bea3cbab2b6e662571580f"}, + {file = "pydantic_core-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9f8bf1d7008a58fbb6eb334dc6e2f2905400cced8dadb46c4ca28f005a8562"}, + {file = "pydantic_core-2.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a014ee88980013d192a718cbb88e8cea20acd3afad69bc6d15672d05a49cdb6"}, + {file = "pydantic_core-2.1.2-cp312-cp312-manylinux_2_24_armv7l.whl", hash = "sha256:8125152b03dd91deca5afe5b933a1994b39405adf6be2fe8dce3632319283f85"}, + {file = "pydantic_core-2.1.2-cp312-cp312-manylinux_2_24_ppc64le.whl", hash = "sha256:dc737506b4a0ba2922a2626fc6d620ce50a46aebd0fe2fbcad1b93bbdd8c7e78"}, + {file = "pydantic_core-2.1.2-cp312-cp312-manylinux_2_24_s390x.whl", hash = "sha256:bb471ea8650796060afc99909d9b75da583d317e52f660faf64c45f70b3bf1e2"}, + {file = "pydantic_core-2.1.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1fad38db1744d27061df516e59c5025b09b0a50a337c04e6eebdbddc18951bc"}, + {file = "pydantic_core-2.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:94d368af9e6563de6e7170a74710a2cbace7a1e9c8e507d9e3ac34c7065d7ae3"}, + {file = "pydantic_core-2.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bd95d223de5162811a7b36c73d48eac4fee03b075132f3a1b73c132ce157a60c"}, + {file = "pydantic_core-2.1.2-cp312-none-win32.whl", hash = "sha256:cd62f73830d4715bc643ae39de0bd4fb9c81d6d743530074da91e77a2cccfe67"}, + {file = "pydantic_core-2.1.2-cp312-none-win_amd64.whl", hash = "sha256:51968887d6bd1eaa7fc7759701ea8ccb470c04654beaa8ede6835b0533f206a9"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:7ff6bfe63f447a509ed4d368a7f4ba6a7abc03bc4744fc3fb30f2ffab73f3821"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:4e67f9b9dfda2e42b39459cbf99d319ccb90da151e35cead3521975b2afbf673"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b815a769b019dd96be6571096f246b74f63330547e9b30244c51b4a2eb0277fc"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4aff436c23c68449601b3fba7075b4f37ef8fbb893c8c1ed3ef898f090332b1e"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-manylinux_2_24_armv7l.whl", hash = "sha256:2ee3ae58f271851362f6c9b33e4c9f9e866557ec7d8c03dc091e9b5aa5566cec"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:cf92dccca8f66e987f6c4378700447f82b79e86407912ab1ee06b16b82f05120"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-manylinux_2_24_s390x.whl", hash = "sha256:4663293a36a851a860b1299c50837914269fca127434911297dd39fea9667a01"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c917f7a41d9d09b8b024a5d65cf37e5588ccdb6e610d2df565fb7186b1f3b1c"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:06ae67547251135a1b3f8dd465797b13146295a3866bc12ddd73f7512787bb7c"}, + {file = "pydantic_core-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4938b32c09dbcecbeb652327cb4a449b1ef1a1bf6c8fc2c8241aa6b8f6d63b54"}, + {file = "pydantic_core-2.1.2-cp37-none-win32.whl", hash = "sha256:682ff9228c838018c47dfa89b3d84cca45f88cacde28807ab8296ec221862af4"}, + {file = "pydantic_core-2.1.2-cp37-none-win_amd64.whl", hash = "sha256:6e3bcb4a9bc209a61ea2aceb7433ce2ece32c7e670b0c06848bf870c9b3e7d87"}, + {file = "pydantic_core-2.1.2-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:2278ca0b0dfbcfb1e12fa58570916dc260dc72bee5e6e342debf5329d8204688"}, + {file = "pydantic_core-2.1.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:87cff210af3258ca0c829e3ebc849d7981bfde23a99d6cb7a3c17a163b3dbad2"}, + {file = "pydantic_core-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7684b5fb906b37e940c5df3f57118f32e033af5e4770e5ae2ae56fbd2fe1a30a"}, + {file = "pydantic_core-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3747a4178139ebf3f19541285b2eb7c886890ca4eb7eec851578c02a13cc1385"}, + {file = "pydantic_core-2.1.2-cp38-cp38-manylinux_2_24_armv7l.whl", hash = "sha256:e17056390068afd4583d88dcf4d4495764e4e2c7d756464468e0d21abcb8931e"}, + {file = "pydantic_core-2.1.2-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:c720e55cef609d50418bdfdfb5c44a76efc020ae7455505788d0113c54c7df55"}, + {file = "pydantic_core-2.1.2-cp38-cp38-manylinux_2_24_s390x.whl", hash = "sha256:b59a64c367f350873c40a126ffe9184d903d2126c701380b4b55753484df5948"}, + {file = "pydantic_core-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68a2a767953c707d9575dcf14d8edee7930527ee0141a8bb612c22d1f1059f9a"}, + {file = "pydantic_core-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4ae46769d9a7138d58cd190441cac14ce954010a0081f28462ed916c8e55a4f"}, + {file = "pydantic_core-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fc909f62325a631e1401dd07dfc386986dbcac15f98c9ff2145d930678a9d25a"}, + {file = "pydantic_core-2.1.2-cp38-none-win32.whl", hash = "sha256:b4038869ba1d8fa33863b4b1286ab07e6075a641ae269b865f94d7e10b3e800e"}, + {file = "pydantic_core-2.1.2-cp38-none-win_amd64.whl", hash = "sha256:5948af62f323252d56acaec8ebfca5f15933f6b72f8dbe3bf21ee97b2d10e3f0"}, + {file = "pydantic_core-2.1.2-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:8e6ce261ccb9a986953c4dce070327e4954f9dd4cd214746dfc70efbc713b6a1"}, + {file = "pydantic_core-2.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d35d634d9d1ed280c87bc2a7a6217b8787eedc86f368fc2fa1c0c8c78f7d3c93"}, + {file = "pydantic_core-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be2e2812a43205728a06c9d0fd090432cd76a9bb5bff2bfcfdf8b0e27d51851"}, + {file = "pydantic_core-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0eb54b11cd4fe0c6404611eef77086ade03eb1457e92910bbb4f3479efa3f79"}, + {file = "pydantic_core-2.1.2-cp39-cp39-manylinux_2_24_armv7l.whl", hash = "sha256:087ddbb754575618a8832ee4ab52fe7eb332f502e2a56088b53dbeb5c4efdf9f"}, + {file = "pydantic_core-2.1.2-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:b74906e01c7fc938ac889588ef438de812989817095c3c4904721f647d64a4d1"}, + {file = "pydantic_core-2.1.2-cp39-cp39-manylinux_2_24_s390x.whl", hash = "sha256:60b7239206a2f61ad89c7518adfacb3ccd6662eaa07c5e437317aea2615a1f18"}, + {file = "pydantic_core-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:be3419204952bbe9b72b90008977379c52f99ae1c6e640488de4be783c345d71"}, + {file = "pydantic_core-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:804cf8f6a859620f8eb754c02f7770f61c3e9c519f8338c331d555b3d6976e3c"}, + {file = "pydantic_core-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cbba32fb14e199d0493c6b9c44870dab0a9c37af9f0f729068459d1849279ffd"}, + {file = "pydantic_core-2.1.2-cp39-none-win32.whl", hash = "sha256:6bf00f56a4468f5b03dadb672a5f1d24aea303d4ccffe8a0f548c9e36017edd3"}, + {file = "pydantic_core-2.1.2-cp39-none-win_amd64.whl", hash = "sha256:ac462a28218ea7d592c7ad51b517558f4ac6565a4e53db7a4811eeaf9c9660b0"}, + {file = "pydantic_core-2.1.2-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:047e782b9918f35ef534ced36f1fd2064f5581229b7a15e4d3177387a6b53134"}, + {file = "pydantic_core-2.1.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c0213891898fa5b404cf3edf4797e3ac7819a0708ea5473fc6432a2aa27c189"}, + {file = "pydantic_core-2.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0f481aaf0119f77b200e5a5e2799b3e14c015a317eaa948f42263908735cc9f"}, + {file = "pydantic_core-2.1.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15eb4cb543ed36f6a4f16e3bee7aa7ed1c3757be95a3f3bbb2b82b9887131e0f"}, + {file = "pydantic_core-2.1.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ef71e73a81a4cd7e87c93e8ff0170140fd93ba33b0f61e83da3f55f6e0a84fb4"}, + {file = "pydantic_core-2.1.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:840238c845b0f80777151fef0003088ab91c6f7b3467edaff4932b425c4e3c3f"}, + {file = "pydantic_core-2.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7648e48ba263ca0a8a2dc55a60a219c9133fb101ba52c89a14a29fb3d4322ca3"}, + {file = "pydantic_core-2.1.2-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:8eb4e2b71562375609c66a79f89acd4fe95c5cba23473d04952c8b14b6f908f5"}, + {file = "pydantic_core-2.1.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056afea59651c4e47ec6dadbb77ccae4742c059a3d12bc1c0e393d189d2970d"}, + {file = "pydantic_core-2.1.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46cd323371aa7e4053010ccdb94063a4273aa9e5dbe97f8a1147faa769de8d8d"}, + {file = "pydantic_core-2.1.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aa39499625239da4ec960cf4fc66b023929b24cc77fb8520289cfdb3c1986428"}, + {file = "pydantic_core-2.1.2-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f5de2d4167fd4bc5ad205fb7297e25867b8e335ca08d64ed7a561d2955a2c32d"}, + {file = "pydantic_core-2.1.2-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:9a5fba9168fc27805553760fa8198db46eef83bf52b4e87ebbe1333b823d0e70"}, + {file = "pydantic_core-2.1.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e68a404fad8493989d6f07b7b9e066f1d2524d7cb64db2d4e9a84c920032c67f"}, + {file = "pydantic_core-2.1.2-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:1a5c4475510d1a9cc1458a26cfc21442223e52ce9adb640775c38739315d03c7"}, + {file = "pydantic_core-2.1.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0681472245ef182554208a25d16884c84f1c5a69f14e6169b88932e5da739a1c"}, + {file = "pydantic_core-2.1.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7fd334b40c5e13a97becfcaba314de0dcc6f7fe21ec8f992139bcc64700e9dc"}, + {file = "pydantic_core-2.1.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7345b1741bf66a9d8ed0ec291c3eabd534444e139e1ea6db5742ac9fd3be2530"}, + {file = "pydantic_core-2.1.2-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0855cf8b760fb40f97f0226cb527c8a94a2ab9d8179628beae20d6939aaeacb0"}, + {file = "pydantic_core-2.1.2-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d281a10837d98db997c0247f45d138522c91ce30cf3ae7a6afdb5e709707d360"}, + {file = "pydantic_core-2.1.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:82e09f27edab289187dd924d4d93f2a35f21aa969699b2504aa643da7fbfeff9"}, + {file = "pydantic_core-2.1.2-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:aa54902fa51f7d921ba80923cf1c7ff3dce796a7903300bd8824deb90e357744"}, + {file = "pydantic_core-2.1.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b9a5fc4058d64c9c826684dcdb43891c1b474a4a88dcf8dfc3e1fb5889496f8"}, + {file = "pydantic_core-2.1.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:817681d111cb65f07d46496eafec815f48e1aff37713b73135a0a9eb4d3610ab"}, + {file = "pydantic_core-2.1.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b5d37aedea5963f2097bddbcdb255483191646a52d40d8bb66d61c190fcac91"}, + {file = "pydantic_core-2.1.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f2de65752fff248319bcd3b29da24e205fa505607539fcd4acc4037355175b63"}, + {file = "pydantic_core-2.1.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:a8b9c2cc4c5f8169b943d24be4bd1548fe81c016d704126e3a3124a2fc164885"}, + {file = "pydantic_core-2.1.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f7bcdf70c8b6e70be11c78d3c00b80a24cccfb408128f23e91ec3019bed1ecc1"}, + {file = "pydantic_core-2.1.2.tar.gz", hash = "sha256:d2c790f0d928b672484eac4f5696dd0b78f3d6d148a641ea196eb49c0875e30a"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pylint" +version = "2.17.4" +description = "python code static checker" +optional = false +python-versions = ">=3.7.2" +files = [ + {file = "pylint-2.17.4-py3-none-any.whl", hash = "sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c"}, + {file = "pylint-2.17.4.tar.gz", hash = "sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1"}, +] + +[package.dependencies] +astroid = ">=2.15.4,<=2.17.0-dev0" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +dill = [ + {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, +] +isort = ">=4.2.5,<6" +mccabe = ">=0.6,<0.8" +platformdirs = ">=2.2.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +tomlkit = ">=0.10.1" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +spelling = ["pyenchant (>=3.2,<4.0)"] +testutils = ["gitpython (>3)"] + +[[package]] +name = "pymongo" +version = "4.4.0" +description = "Python driver for MongoDB " +optional = false +python-versions = ">=3.7" +files = [ + {file = "pymongo-4.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:50294bae0f20ec4f8d3f5eefd133956f582942c156d08f6b88f2a1b1efe04c53"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux1_i686.whl", hash = "sha256:88ceab5cd84f7d86f018fa66377d6f90fcf3643d56283f2f4124ccef58390a0e"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:2578f077b9448b7a420b3e9b0efdfb7ecdb2a3c27e00c181610809717c900cd9"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux2014_i686.whl", hash = "sha256:ebe1954aa85e622674ea01828419f129527c95c40a392e0f7761e242d85a772f"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux2014_ppc64le.whl", hash = "sha256:7e0fbf05bb74a3f610f970a178bfb4e048f6b82fc22dda5e14e0ddfc4d66c9b7"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux2014_s390x.whl", hash = "sha256:4b43ae6e1c4b972761065f77f3eff4b914154bc5bd74d632305875c5309eafd1"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:18acb807de39eb9b8ff7122094920f1da79c1781dc96cfef73dd97da51448f7b"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5c56169effa5bf9fae5e9a66efc211b3f252869d99d6c400792eced7f213b9"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c6bd8470c89b2cd6312fa685dbf4c64371a04a7e4a3a55e2007626f8f997103"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02f535bc8f8d75d45ec6cd944804d466a73a46afc368d6c36e232b887edd0475"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff281a66925790a05e3c7e0de1350a0992b66a4e51724317ac35026ac856ae28"}, + {file = "pymongo-4.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6aa18b255af46641d167378f8b8f06becb6eb1670f622aefa34e502362267fa9"}, + {file = "pymongo-4.4.0-cp310-cp310-win32.whl", hash = "sha256:34ea6ffb77f0cf8d01c4c1df60dc68141859ada1507c326380ef81e23b58c9cc"}, + {file = "pymongo-4.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:071c256fbb35c6942970b8b6eb6b89bac302db49a2d6d35e68c35b442a0ce710"}, + {file = "pymongo-4.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d0a8f16a97758ca9af1baa927521b24175dba7e95ce745d5bf64a5c75fe61df8"}, + {file = "pymongo-4.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02060ced24a26e1c73b6f491b728fe99e73f38ba3a1e4b882dc7b873d419ab3e"}, + {file = "pymongo-4.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38ece8d2892de19fa437bc4f60b0d8c5353b185e8cc1c543212a488c93c74834"}, + {file = "pymongo-4.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2128592140426044d59a89f30b7aba1e185faf2553b3d161dcca0aa970ba40c7"}, + {file = "pymongo-4.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8d482e2ae01a5ac17183afe8c808cb6919889bdf22f0d3663105ccf0ea89adf"}, + {file = "pymongo-4.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0ddb34591f5e19296ef5b643e23ec5179a7c1d2b73c17701f50dcfa493e0252"}, + {file = "pymongo-4.4.0-cp311-cp311-win32.whl", hash = "sha256:23bfd793be088470a1c7bca5c907ae3180e6a4cf731e96a194c89413c042cf4c"}, + {file = "pymongo-4.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:48908eaca3dccc2af8b4eae73ee00d2e1e7ffe91ce630c8906981c075161ad8c"}, + {file = "pymongo-4.4.0-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:a4315509a4e155e6bd4b199bd435ff2bb31f558915726e0c50a725ae7b99727f"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:93d8d5ee37cd9634747a1363496fd3949451bdaeb9539278985cb6fd08d929cf"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3e6efcf768209dc4d966fabbbe4dcd2dd2d60ec4d8342668da02664f0c73a9e8"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e5f19bb887d83959ba1c359fba16cdedb0f868ba85ae375c3e4e0fdf5697a524"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:b641683de9bf05b4b52a629bf8ddd5fa0fb061ca54bc2412ce89ce2de2beda36"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:2f74b606c11c570ec2a6c384fc194d96f00eaa829c7c08cbec455f7b02d28774"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:242d1a30162ead28e69df37748021039c4b292bbfd7c5449294a26c8365d342d"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:4b092e2a11f37a41e1767a221ff2719397ae2e033f978de236ce10c4b1916227"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c96a080cae86c1c27758fdd3fbee0298a006b05272de4dff9dea21ca34952c72"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:67aa85bbab597615efd83f521c8da34dd9a19b7456cc919c227378c793073183"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eea8af506b8b33573c76942a5c2390f2cddb4e195b1cdfc373ca919e9b95904"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44893b6788da1d67696ff2f27e42e315d40965a4fa23786dcc26c932c5b02149"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01807a3d0b758cbf65e7b3af84a24d2ee58c9d6c0af8b0af99b581bcaa75a931"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9f3e8fc3433a86ab0b3d960f8fb48fe7135876df04987dd04b3bf35d9b49ae92"}, + {file = "pymongo-4.4.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0669823de06c3a77fddf738f6250688b7fdae2b44edbe3c103b7fbfdfc848392"}, + {file = "pymongo-4.4.0-cp37-cp37m-win32.whl", hash = "sha256:95a5036b7046f617207f38936c188eeb56dbe740cba0fa1215df2e1b9ee38c74"}, + {file = "pymongo-4.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:45838fa9abf3bce35a24fffcd289de23f3d6edc4cc9beac28037df3e1009f892"}, + {file = "pymongo-4.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:40ad38ad6f6dbd8a9dbd05195a15fe7749a0692dc895274a4f806d9831fceb3c"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:028addb304bc525d4a10c5c6e59ef5f140e528ae285c10e1d43f19905631e32f"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:093c5343c5863e87023318050507511fa7458b0376caabcc41abff0e36aaabc8"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:1a1bb096579ffa59143a8d8fc9d4692db3e04305cf5a0e48e0724ae47a836255"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:b100895d6b57d6a7e8de5fd15578aaa46170b56d978baf56182c10e8ba725fbf"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:29956f61ab885c28b190ff817de7ad0c75a470699632b44848b102640fbf9e73"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:f38bbab4d13d180ed00c2f107e36503bd2e2cc4c6d4ae2734c0a85c2edaf2d2e"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:274eb75231aca12d54d421852fc230e8655e4b33b30e9eb7fd34269955e125dd"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6603315f9a15e5ed80143a5a744c695a8503e27a76fb5828f7269225f39ddd"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d368def4564681f681f4fe1ae906239bb4dc7dd403c49d15d3a6fe2688950f13"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf4e83af0bd3cf4c98eaf1ed2d028afd520bdffd6f7570f6cc5a44e9363fbb9a"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eba5abcee347bdaa7f1a3f18fd97758f0b75a6dc5704885e793aeb51e8e5e32"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a2e496753e91bc82dfbe1f3bab21e0907866dab3c810e255ebaf991cd5c5455d"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8edb59aa5c10a3fb8d3a0c2cac8ba58c0d8f4e56f9003378ac1fff503a8d3f42"}, + {file = "pymongo-4.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:801094c80d117b0d476f0afbe16cdfe438cc4069c5b0f2578564cb4b3c83f80f"}, + {file = "pymongo-4.4.0-cp38-cp38-win32.whl", hash = "sha256:8fd68b540fb70954deeb2b6a1fb2f34d6342bcf221e003e6063e3b28e87b2778"}, + {file = "pymongo-4.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:a5551491ace0f05ae0bbe5a496c4daf216d9fc98e182940f471c228235b1626e"}, + {file = "pymongo-4.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4481f2796d53cd0c74d988a23a11266e6cae03be3878f42ed2c221b192d14f8d"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6a2564ed1a07258a73f7adfb0663aa69022f1edc431d11aae4a32a29e7755d3c"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:900c773d4f9d68747bb19ef40c35c701f4a919a6b96efa2d4e1cb50074a9738e"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b213fae58d6ba14ac71a481691981e582ff5813630f3a82aaf92fb79399ba0ec"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:4a0cfab6b6c1826e8dfe4453c08aa70343a693dede7c09dca564a9b1f2393374"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:8d8a8aef8724058d416536902e680f2b06499e58c54220becdfcd3ff8e5dccfd"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:6acedf0778b79b6ea111a28fb23760b5f6b7b1c3e1f1e3595cf87ce709bce344"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:15cf004b6329da48078d7d9d1c79c802df6631b94e5a1ed9a112d713cc0f66e9"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2caac57d2a0160dce877e706e94e8a15b87feb71c257ecb8b5a039f7e98ba99b"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2875f0bdb605e56630f46e12082f26ac2c680a5473f5f154b7131841727948c"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78be52dc21f578a17e2c1cf1a222d4e64e91e0b1dba7e18f5ff7be7c0bf8053f"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7eb221dcb9e27415d2bd6e2d3001d1da0f351e2aa1564f6f3987f2206c066600"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3eed06a24157a891eac5f73ec2400d22cccc95cde78a3f0e2b90c5ab17f1cf1"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:196c2c9ffccdf0ad4efdfae29347c4e2ae52c3415e958736cda84e4062553e96"}, + {file = "pymongo-4.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4b65f4e66efe43dcc5fb3a285f899e798742b8365bafdd832054675d34d640d5"}, + {file = "pymongo-4.4.0-cp39-cp39-win32.whl", hash = "sha256:4a28ad09abccc9f71632398febfea12d3f28cec7e44fe6f2b16665807e62c298"}, + {file = "pymongo-4.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:5e13ba36f18489600db28da13da73e8e190bd48899ad268cb482fe726d31a922"}, + {file = "pymongo-4.4.0.tar.gz", hash = "sha256:a1b5d286fee4b9b5a0312faede02f2ce2f56ac695685af1d25f428abdac9a22c"}, +] + +[package.dependencies] +dnspython = ">=1.16.0,<3.0.0" + +[package.extras] +aws = ["pymongo-auth-aws (<2.0.0)"] +encryption = ["pymongo[aws]", "pymongocrypt (>=1.6.0,<2.0.0)"] +gssapi = ["pykerberos", "winkerberos (>=0.5.0)"] +ocsp = ["certifi", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"] +snappy = ["python-snappy"] +zstd = ["zstandard"] + +[[package]] +name = "pytest" +version = "7.4.0" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-bdd" +version = "6.1.1" +description = "BDD for pytest" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pytest_bdd-6.1.1-py3-none-any.whl", hash = "sha256:57eba5878d77036f356a85fb1d108cb061d8af4fb4d032b1a424fa9abe9e498b"}, + {file = "pytest_bdd-6.1.1.tar.gz", hash = "sha256:138af3592bcce5d4684b0d690777cf199b39ce45d423ca28086047ffe6111010"}, +] + +[package.dependencies] +Mako = "*" +parse = "*" +parse-type = "*" +pytest = ">=6.2.0" +typing-extensions = "*" + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "starlette" +version = "0.27.0" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.7" +files = [ + {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, + {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.11.8" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, + {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, +] + +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "unicorn" +version = "2.0.1.post1" +description = "Unicorn CPU emulator engine" +optional = false +python-versions = "*" +files = [ + {file = "unicorn-2.0.1.post1-py2.py3-none-macosx_10_15_x86_64.whl", hash = "sha256:2d3eb3ec7eabbc4e66b4d853e11b27c426ce03072860c756139ed2191588751c"}, + {file = "unicorn-2.0.1.post1-py2.py3-none-manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b3eba8848f3cfd43ed5f3128f01880ecc4e73e6b36c1e5e951177db0fb9c03b"}, + {file = "unicorn-2.0.1.post1-py2.py3-none-manylinux1_i686.whl", hash = "sha256:4cf11d4883160dd3c4967bb3187dd886cdca4422884b315ace436af6047aae0d"}, + {file = "unicorn-2.0.1.post1-py2.py3-none-manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45be711d2bc8b82d07f2e0bdde128f3e9408c42978a3ed5edf709bce5350fcdf"}, + {file = "unicorn-2.0.1.post1-py2.py3-none-manylinux1_x86_64.whl", hash = "sha256:5ed85a0b7bbfa47dd029272e071c814f21e9dacc2eefd63521f4d638ff1d6db9"}, + {file = "unicorn-2.0.1.post1-py2.py3-none-manylinux2014_aarch64.whl", hash = "sha256:0bd05f9ae6b86b1326e6c3c1d1f3e51264b479cd1961388fd0da0ae674ec0195"}, + {file = "unicorn-2.0.1.post1-py2.py3-none-win32.whl", hash = "sha256:9f0e3bbe207a6d2ddd3dff528bf3b2251c8e11d0fb4bab2338dff01475f6f41b"}, + {file = "unicorn-2.0.1.post1-py2.py3-none-win_amd64.whl", hash = "sha256:7145fe448d17b2377b18e08e0521749cf0f94b9d3fe8b67c032bb4c932a21c29"}, + {file = "unicorn-2.0.1.post1.tar.gz", hash = "sha256:7fc69523eb83b4c8abc7cb4410ca21875e066c34b7afe998f59481e830d28e56"}, +] + +[[package]] +name = "wrapt" +version = "1.15.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +files = [ + {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, + {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, + {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, + {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, + {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, + {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, + {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, + {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, + {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, + {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, + {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, + {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, + {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, + {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, + {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, + {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, + {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, + {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, + {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, + {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, + {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, + {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, + {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, + {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, + {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, + {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, + {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, + {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, + {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, + {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, + {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, + {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, + {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, + {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, + {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, + {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, + {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, + {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "b35eae4b7f6b4ad91a452e883ef337c3c325bd7852b9cbbafe05d394998585c2" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3766ee0 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,26 @@ +[tool.poetry] +name = "messages-api" +version = "0.1.0" +description = "This service enables the sending and receiving of events through the HTTP REST protocol, without depending on a specific implementation of an event broker or queue" +authors = ["Matias Chediek "] +readme = "README.md" +packages = [{include = "messages_api"}] + +[tool.poetry.dependencies] +python = "^3.8" +fastapi = "^0.100.0" +pydantic = "^2.0.2" +pymongo = "^4.4.0" +unicorn = "^2.0.1.post1" +pika = "^1.3.2" + + +[tool.poetry.group.dev.dependencies] +pylint = "^2.17.4" +pytest = "^7.4.0" +pytest-bdd = "^6.1.1" +pytest-cov = "^4.1.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/src/app/.keep b/src/app/.keep new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/__init__.py b/src/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/integration_tests/features/__init__.py b/src/tests/integration_tests/features/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/integration_tests/features/arguments.feature b/src/tests/integration_tests/features/arguments.feature new file mode 100644 index 0000000..1ceec96 --- /dev/null +++ b/src/tests/integration_tests/features/arguments.feature @@ -0,0 +1,7 @@ +Feature: Step arguments + + Scenario: Arguments for given, when, then + Given there are 5 cucumbers + When I eat 3 cucumbers + And I eat 2 cucumbers + Then I should have 0 cucumbers diff --git a/src/tests/integration_tests/features/test_publish_article.py b/src/tests/integration_tests/features/test_publish_article.py new file mode 100644 index 0000000..0a1b774 --- /dev/null +++ b/src/tests/integration_tests/features/test_publish_article.py @@ -0,0 +1,19 @@ +from pytest_bdd import scenarios, given, when, then, parsers + + +scenarios("arguments.feature") + + +@given(parsers.parse("there are {start:d} cucumbers"), target_fixture="cucumbers") +def given_cucumbers(start): + return {"start": start, "eat": 0} + + +@when(parsers.parse("I eat {eat:d} cucumbers")) +def eat_cucumbers(cucumbers, eat): + cucumbers["eat"] += eat + + +@then(parsers.parse("I should have {left:d} cucumbers")) +def should_have_left_cucumbers(cucumbers, left): + assert cucumbers["start"] - cucumbers["eat"] == left diff --git a/src/tests/unit_tests/__init__.py b/src/tests/unit_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/unit_tests/test_sysexit.py b/src/tests/unit_tests/test_sysexit.py new file mode 100644 index 0000000..c460a66 --- /dev/null +++ b/src/tests/unit_tests/test_sysexit.py @@ -0,0 +1,10 @@ +import pytest + + +def f(): + raise SystemExit(1) + + +def test_mytest(): + with pytest.raises(SystemExit): + f() \ No newline at end of file From 0ea8cdb1ac526d22bcfb9c9c8e1210e3838fbba2 Mon Sep 17 00:00:00 2001 From: Matias Chediek Date: Wed, 12 Jul 2023 19:35:06 -0300 Subject: [PATCH 2/6] ClickUp-85ztfjmg3 Add docker compose and improve makefile Docker image RabbitMQ and MongoDB --- .dockerignore | 8 +++++ .env.development.example | 13 ++++++++ Dockerfile | 23 +++++--------- README.md | 3 +- docker-compose.yml | 46 +++++++++++++++++++++++++++ makefile | 69 ++++++++++++++++++++++++++++++++++------ poetry.lock | 45 +++++++++++++++++++++++++- poetry.toml | 2 ++ pyproject.toml | 4 +-- src/app/.keep | 0 src/app/main.py | 15 +++++++++ 11 files changed, 199 insertions(+), 29 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.development.example create mode 100644 docker-compose.yml create mode 100644 poetry.toml delete mode 100644 src/app/.keep create mode 100644 src/app/main.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..55c123e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.* +!.local/* +docker-compose.yml +Dockerfile +Dockerfile.build +README.md +Makefile +./service/tests \ No newline at end of file diff --git a/.env.development.example b/.env.development.example new file mode 100644 index 0000000..2ca1f1e --- /dev/null +++ b/.env.development.example @@ -0,0 +1,13 @@ +MONGO_INITDB_DATABASE=admin +MONGO_INITDB_ROOT_USERNAME=root +MONGO_INITDB_ROOT_PASSWORD=root + +MONGO_INITDB_ADMINUSERNAME=admin +MONGO_INITDB_ADMINPASSWORD=admin +DATABASE_HOST=mapi_mongodb +DATABASE_NAME=mapi + +RABBITMQ_HOST=mapi_rabbitmq + +MONGODB_ENABLE_ADMIN=true +MONGODB_URL=mongodb://admin:admin@mapi_mongodb/mapi?retryWrites=true&w=majority diff --git a/Dockerfile b/Dockerfile index 7b1abff..aa8f97b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,14 @@ -FROM python:3.11-slim-buster +FROM python:3.11-slim SHELL ["/bin/bash", "-o", "pipefail", "-c"] -ARG GH_USER -ARG GH_PAT # Configure env ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ POETRY_VERSION=1.4.2 \ POETRY_HOME="/usr/src/poetry" \ - POETRY_CACHE_DIR="/usr/src/poetry/cache" \ - POETRY_VIRTUALENVS_IN_PROJECT="true" + POETRY_CACHE_DIR="/usr/src/poetry/cache" ENV PATH="$POETRY_HOME/bin:$PATH" @@ -29,19 +26,15 @@ RUN set -ex; \ rm -rf /var/lib/apt/lists/* # Install Poetry -RUN curl -sSL https://install.python-poetry.org | python3 - && \ - poetry config http-basic.fastapi-auth-utils "$GH_USER" "$GH_PAT" +RUN curl -sSL https://install.python-poetry.org | python3 - -WORKDIR /code +WORKDIR /usr/src/app COPY pyproject.toml poetry.lock ./ -# Install dependencies -RUN poetry install +RUN poetry config virtualenvs.create false \ + && poetry install $(test "$YOUR_ENV" == production && echo "--no-dev") --no-interaction --no-ansi -# Set commands -COPY src/app . +COPY . . -ENTRYPOINT ["/usr/src/entrypoint"] - -CMD ["uvicorn", "app.main:app", "--host 0.0.0.0", "--port 8000", "--reload"] \ No newline at end of file +CMD ["uvicorn", "src.app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file diff --git a/README.md b/README.md index b86b8d9..1b299d0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ -# messages-api +# MAPI + This service enables the sending and receiving of events through the HTTP REST protocol, without depending on a specific implementation of an event broker or queue diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..32cce27 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,46 @@ +version: "3.5" +services: + + mapi: &mapi + build: + context: . + dockerfile: Dockerfile + ports: + - "${DOCKER_APP_PORT:-8000}:8000" + depends_on: + - mapi_mongodb + - mapi_rabbitmq + env_file: + - .env + volumes: + - ./:/usr/src/app:delegated + networks: + - mongodb_network + - rabbit_network + + mapi_mongodb: + image: mongo:6.0.1 + container_name: mapi_mongodb + env_file: + - .env + ports: + - ${DOCKER_MONGODB_PORT:-27017}:27017 + volumes: + - ./init-mongodb:/docker-entrypoint-initdb.d + - ./init-mongodb/data:/tmp/data + networks: + - mongodb_network + + mapi_rabbitmq: + image: rabbitmq:3 + container_name: mapi_rabbitmq + networks: + - rabbit_network + +volumes: + mapiapivenv: + init-mongodb: + +networks: + mongodb_network: + rabbit_network: \ No newline at end of file diff --git a/makefile b/makefile index 88d9de4..9ecfaa3 100644 --- a/makefile +++ b/makefile @@ -1,17 +1,66 @@ -PY = python -VENV = .venv -BIN=$(VENV)/bin +# default service +service := mapi -setup: requirements.txt - $(PY) -m venv $(VENV) - $(BIN)/pip install --upgrade -r requirements.txt +# all our targets are phony (no files to check). +.PHONY: help up shell code build clean +help: + @echo "" + @echo "Usage: make [TARGET] [EXTRA_ARGUMENTS]" + @echo "Targets:" + @echo " build build project" + @echo " up start project" + @echo " shell start a shell inside the container" + @echo " unit-tests run unit tests" + @echo " integration-tests run integration tests" + @echo " clean remove the project, including containers, images, volumens, etc" + +# start project +up: + @docker-compose up + +# interactive shell +shell: + @docker-compose run --rm --service-ports $(service) /bin/bash + +# open VS Code +code: + @echo "\033[0;32mWait for the container to start before clicking the Reopen in Container button." + @code . + +create-env: +ifneq (,$(wildcard ./.env)) + @echo "Skipping step, .env file already exists" +else + @cp ./.env.development.example ./.env +endif + +ifneq (,$(wildcard ./public-keys.json)) + @echo "Skipping step, public-keys.json file already exists" +else + @echo '{"realm_name": "pub key without BEGIN PUBLIC KEY and END PUBLIC KEY fragments"}' >> ./public-keys.json + @echo "public-keys.json file created, ensure to complete" +endif + +# install project build: - $(BIN)/pylint ./src/app + @docker-compose build \ + && echo "" \ + && echo "Installation complete." \ + || echo "\033[0;31mInstallation failed." + +build-cache: + @docker-compose build --no-cache \ + && echo "" \ + && echo "Installation complete." \ + || echo "\033[0;31mInstallation failed." +# run unit tests tests: - $(BIN)/pytest --cov=src/app/ --cov-report=html:reports/html_dir --cov-report=xml:reports/coverage.xml --feature features -vv src/tests/ + @docker-compose run $(service) pytest --cov=src/ --cov-report=html:reports/html_dir --cov-report=xml:reports/coverage.xml --feature features -vv src/tests/ +# uninstall project clean: - rm -rf __pycache__ - rm -rf .venv + @docker-compose down --remove-orphans -v --rmi local 2>/dev/null \ + && echo "\033[0;32mProject removed." \ + || echo "\033[0;32mProject already removed." \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 6e8e598..b98001d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -54,6 +54,20 @@ wrapt = [ {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, ] +[[package]] +name = "click" +version = "8.1.4" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.4-py3-none-any.whl", hash = "sha256:2739815aaa5d2c986a88f1e9230c55e17f0caad3d958a5e13ad0797c166db9e3"}, + {file = "click-8.1.4.tar.gz", hash = "sha256:b97d0c74955da062a7d4ef92fadb583806a585b2ea81958a81bd72726cbb8e37"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -207,6 +221,17 @@ typing-extensions = ">=4.5.0" [package.extras] all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + [[package]] name = "idna" version = "3.4" @@ -872,6 +897,24 @@ files = [ {file = "unicorn-2.0.1.post1.tar.gz", hash = "sha256:7fc69523eb83b4c8abc7cb4410ca21875e066c34b7afe998f59481e830d28e56"}, ] +[[package]] +name = "uvicorn" +version = "0.22.0" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.7" +files = [ + {file = "uvicorn-0.22.0-py3-none-any.whl", hash = "sha256:e9434d3bbf05f310e762147f769c9f21235ee118ba2d2bf1155a7196448bd996"}, + {file = "uvicorn-0.22.0.tar.gz", hash = "sha256:79277ae03db57ce7d9aa0567830bbb51d7a612f54d6e1e3e92da3ef24c2c8ed8"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + [[package]] name = "wrapt" version = "1.15.0" @@ -959,4 +1002,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "b35eae4b7f6b4ad91a452e883ef337c3c325bd7852b9cbbafe05d394998585c2" +content-hash = "0cb293aa6e292762b0efa75e4573797cef35a22beec647a98356e7ef0a328044" diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 0000000..efa46ec --- /dev/null +++ b/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 3766ee0..3642510 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,9 +2,8 @@ name = "messages-api" version = "0.1.0" description = "This service enables the sending and receiving of events through the HTTP REST protocol, without depending on a specific implementation of an event broker or queue" -authors = ["Matias Chediek "] +authors = ["Grupo Simpli "] readme = "README.md" -packages = [{include = "messages_api"}] [tool.poetry.dependencies] python = "^3.8" @@ -13,6 +12,7 @@ pydantic = "^2.0.2" pymongo = "^4.4.0" unicorn = "^2.0.1.post1" pika = "^1.3.2" +uvicorn = "^0.22.0" [tool.poetry.group.dev.dependencies] diff --git a/src/app/.keep b/src/app/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/main.py b/src/app/main.py new file mode 100644 index 0000000..30d4a45 --- /dev/null +++ b/src/app/main.py @@ -0,0 +1,15 @@ +from typing import Union + +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +def read_root(): + return {"Hello": "World"} + + +@app.get("/items/{item_id}") +def read_item(item_id: int, q: Union[str, None] = None): + return {"item_id": item_id, "q": q} From 8774593e280de43c7699e457fd3b2bbbe147550d Mon Sep 17 00:00:00 2001 From: Matias Chediek Date: Thu, 13 Jul 2023 14:26:17 -0300 Subject: [PATCH 3/6] ClickUp-85ztfjmg3 Add entrypoint to ensure infrastructure was available --- .dockerignore | 4 +-- .env.development.example | 19 +++++++------ .gitignore | 2 ++ .local/entrypoint | 61 ++++++++++++++++++++++++++++++++++++++++ .local/start_api | 10 +++++++ Dockerfile | 10 ++++++- docker-compose.yml | 16 ++++++++++- makefile | 8 +----- 8 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 .local/entrypoint create mode 100644 .local/start_api diff --git a/.dockerignore b/.dockerignore index 55c123e..4f9a674 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,7 +2,5 @@ !.local/* docker-compose.yml Dockerfile -Dockerfile.build README.md -Makefile -./service/tests \ No newline at end of file +Makefile \ No newline at end of file diff --git a/.env.development.example b/.env.development.example index 2ca1f1e..c6b08bb 100644 --- a/.env.development.example +++ b/.env.development.example @@ -1,13 +1,14 @@ -MONGO_INITDB_DATABASE=admin -MONGO_INITDB_ROOT_USERNAME=root -MONGO_INITDB_ROOT_PASSWORD=root +MONGO_INITDB_DATABASE=mapi +MONGO_INITDB_ROOT_USERNAME=user +MONGO_INITDB_ROOT_PASSWORD=pass -MONGO_INITDB_ADMINUSERNAME=admin -MONGO_INITDB_ADMINPASSWORD=admin DATABASE_HOST=mapi_mongodb -DATABASE_NAME=mapi - -RABBITMQ_HOST=mapi_rabbitmq +DOCKER_MONGODB_PORT=27017 MONGODB_ENABLE_ADMIN=true -MONGODB_URL=mongodb://admin:admin@mapi_mongodb/mapi?retryWrites=true&w=majority +MONGODB_URL=mongodb://$MONGO_INITDB_ROOT_USERNAME:$MONGO_INITDB_ROOT_PASSWORD@$DATABASE_HOST:$DOCKER_MONGODB_PORT + +RABBITMQ_DEFAULT_USER=user +RABBITMQ_DEFAULT_PASS=pass +RABBITMQ_HOST=mapi_rabbitmq +RABBITMQ_PORT=5672 \ No newline at end of file diff --git a/.gitignore b/.gitignore index d2b1ff6..417d298 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ reports/ +init-mongodb/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.local/entrypoint b/.local/entrypoint new file mode 100644 index 0000000..6612b34 --- /dev/null +++ b/.local/entrypoint @@ -0,0 +1,61 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o nounset + +source .venv/bin/activate + +setup_ready() { +python << END +import sys +import os +import pika +from pymongo import MongoClient + +mongodb_url = os.getenv("MONGODB_URL", "Nothing was set") + +try: + print ("Checking MongoDB") + client = MongoClient(host = [mongodb_url], serverSelectionTimeoutMS = 2000) + client.server_info() + print ("Mongo OK") +except Exception as e: + print (e) + sys.exit(-1) + +rabbitmq_user = os.getenv("RABBITMQ_DEFAULT_USER", "Nothing was set") +rabbitmq_pass = os.getenv("RABBITMQ_DEFAULT_PASS", "Nothing was set") +rabbitmq_host = os.getenv("RABBITMQ_HOST", "Nothing was set") +rabbitmq_port = os.getenv("RABBITMQ_PORT", "Nothing was set") + +try: + print ("Checking RabbitMQ") + credentials = pika.PlainCredentials(rabbitmq_user, rabbitmq_pass) + parameters = pika.ConnectionParameters(rabbitmq_host,rabbitmq_port,'/',credentials) + connection = pika.BlockingConnection(parameters) + if connection.is_open: + print ("RabbitMQ OK") + else: + print ("RabbitMQ connection is no open") + sys.exit(-1) + +except Exception as e: + print (e) + sys.exit(-1) +finally: + connection.close() + +sys.exit(0) + +END +} + +until setup_ready; do + >&2 echo 'Waiting for MongoDB and RabbitMQ to become available...' + sleep 2 +done + +>&2 echo 'MongoDB and RabbitMQ are available, running the application' + +exec "$@" \ No newline at end of file diff --git a/.local/start_api b/.local/start_api new file mode 100644 index 0000000..4f1cf91 --- /dev/null +++ b/.local/start_api @@ -0,0 +1,10 @@ +#!/bin/bash + +set -o errexit +set -o pipefail + +if [[ -z "${KUBERNETES_SERVICE_HOST}" ]]; then + uvicorn src.app.main:app --host 0.0.0.0 --port 8000 --reload +else + uvicorn src.app.main:app --proxy-headers --host 0.0.0.0 --port 8000 +fi \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index aa8f97b..f320553 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,7 @@ RUN set -ex; \ git \ vim \ openssh-server \ + iputils-ping \ curl; \ \ apt-get clean; \ @@ -35,6 +36,13 @@ COPY pyproject.toml poetry.lock ./ RUN poetry config virtualenvs.create false \ && poetry install $(test "$YOUR_ENV" == production && echo "--no-dev") --no-interaction --no-ansi +COPY .local/entrypoint .local/start_* /usr/src/ +RUN set -ex; \ + chmod +x /usr/src/entrypoint; \ + chmod +x /usr/src/start_api + COPY . . -CMD ["uvicorn", "src.app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "8000"] \ No newline at end of file +ENTRYPOINT ["/usr/src/entrypoint"] + +CMD ["/usr/src/start_api"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 32cce27..5e84b7e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,15 +30,29 @@ services: - ./init-mongodb/data:/tmp/data networks: - mongodb_network + healthcheck: + test: echo 'db.runCommand("ping").ok' | mongo mongo:27017/mapi --quiet + interval: 10s + timeout: 10s + retries: 5 + start_period: 40s mapi_rabbitmq: image: rabbitmq:3 container_name: mapi_rabbitmq + env_file: + - .env networks: - rabbit_network + healthcheck: + test: rabbitmq-diagnostics -q ping + interval: 30s + timeout: 30s + retries: 3 + ports: + - ${RABBITMQ_PORT:-5672}:5672 volumes: - mapiapivenv: init-mongodb: networks: diff --git a/makefile b/makefile index 9ecfaa3..48dc7bb 100644 --- a/makefile +++ b/makefile @@ -35,12 +35,6 @@ else @cp ./.env.development.example ./.env endif -ifneq (,$(wildcard ./public-keys.json)) - @echo "Skipping step, public-keys.json file already exists" -else - @echo '{"realm_name": "pub key without BEGIN PUBLIC KEY and END PUBLIC KEY fragments"}' >> ./public-keys.json - @echo "public-keys.json file created, ensure to complete" -endif # install project build: @@ -57,7 +51,7 @@ build-cache: # run unit tests tests: - @docker-compose run $(service) pytest --cov=src/ --cov-report=html:reports/html_dir --cov-report=xml:reports/coverage.xml --feature features -vv src/tests/ + @docker-compose run $(service) pytest --cov=src/ --cov-report=html:cover/html_dir --cov-report=xml:cover/coverage.xml --feature features -vv src/tests/ # uninstall project clean: From 40fafbf500aa44950d9258d31ae115b646db5148 Mon Sep 17 00:00:00 2001 From: Matias Chediek Date: Thu, 13 Jul 2023 14:45:34 -0300 Subject: [PATCH 4/6] ClickUp-85ztfjmg3 Add girhub workflow --- .github/workflows/pr-quality.yml | 51 ++++++++++++++++++++++++++++++++ .local/entrypoint | 2 -- makefile | 4 --- 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/pr-quality.yml diff --git a/.github/workflows/pr-quality.yml b/.github/workflows/pr-quality.yml new file mode 100644 index 0000000..502f3b7 --- /dev/null +++ b/.github/workflows/pr-quality.yml @@ -0,0 +1,51 @@ +name: PR Qualiti + +on: + pull_request: + types: + - opened + - synchronize + branches: ["main"] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@main + + - name: Run make script for build and tests + run: | + make create-env + make build + + sonarq_analysis: + needs: build + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@main + + - name: Set up environment + run: | + make create-env + docker compose -f docker-compose.yml up -d + + - name: Run make script for tests and coverage + run: | + make tests + + - name: Upload coverage report + uses: actions/upload-artifact@v2 + with: + name: coverage-report + path: cover + + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@master + with: + sonar-token: ${{ secrets.SONAR_TOKEN }} + coverage-report-path: cover/coverage.xml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.local/entrypoint b/.local/entrypoint index 6612b34..c0f9e55 100644 --- a/.local/entrypoint +++ b/.local/entrypoint @@ -4,8 +4,6 @@ set -o errexit set -o pipefail set -o nounset -source .venv/bin/activate - setup_ready() { python << END import sys diff --git a/makefile b/makefile index 48dc7bb..a642ef2 100644 --- a/makefile +++ b/makefile @@ -29,11 +29,7 @@ code: @code . create-env: -ifneq (,$(wildcard ./.env)) - @echo "Skipping step, .env file already exists" -else @cp ./.env.development.example ./.env -endif # install project From 7d08adbb6b13eb2c956e905915fefedeeab7cdb1 Mon Sep 17 00:00:00 2001 From: Matias Chediek Date: Mon, 17 Jul 2023 12:24:09 -0300 Subject: [PATCH 5/6] ClickUp-85ztfjmg3 Start channel logic and test Start consumer logic and test --- .env.development.example | 2 +- .github/workflows/pr-quality.yml | 51 ----- .local/start_api | 2 +- .vscode/launch.json | 20 ++ .vscode/settings.json | 5 +- Dockerfile | 4 +- compose-dev.yaml | 12 ++ docker-compose.yml | 2 +- makefile | 4 +- poetry.lock | 74 +++++++- pyproject.toml | 2 + src/{tests => app}/__init__.py | 0 src/app/main.py | 156 +++++++++++++++- .../features/admin}/__init__.py | 0 .../features/admin/create_channel.feature | 89 +++++++++ .../features/admin/manage_consumer.feature | 176 ++++++++++++++++++ .../features/admin/test_feature_spects.py | 33 ++++ .../features/arguments.feature | 7 - .../features/test_publish_article.py | 19 -- src/tests/pytest.ini | 9 + src/tests/unit_tests/__init__.py | 0 src/tests/unit_tests/test_sysexit.py | 10 - 22 files changed, 573 insertions(+), 104 deletions(-) delete mode 100644 .github/workflows/pr-quality.yml create mode 100644 .vscode/launch.json create mode 100644 compose-dev.yaml rename src/{tests => app}/__init__.py (100%) rename src/tests/{integration_tests/features => functional/features/admin}/__init__.py (100%) create mode 100644 src/tests/functional/features/admin/create_channel.feature create mode 100644 src/tests/functional/features/admin/manage_consumer.feature create mode 100644 src/tests/functional/features/admin/test_feature_spects.py delete mode 100644 src/tests/integration_tests/features/arguments.feature delete mode 100644 src/tests/integration_tests/features/test_publish_article.py create mode 100644 src/tests/pytest.ini delete mode 100644 src/tests/unit_tests/__init__.py delete mode 100644 src/tests/unit_tests/test_sysexit.py diff --git a/.env.development.example b/.env.development.example index c6b08bb..017b3eb 100644 --- a/.env.development.example +++ b/.env.development.example @@ -6,7 +6,7 @@ DATABASE_HOST=mapi_mongodb DOCKER_MONGODB_PORT=27017 MONGODB_ENABLE_ADMIN=true -MONGODB_URL=mongodb://$MONGO_INITDB_ROOT_USERNAME:$MONGO_INITDB_ROOT_PASSWORD@$DATABASE_HOST:$DOCKER_MONGODB_PORT +MONGODB_URL=mongodb://$MONGO_INITDB_ROOT_USERNAME:$MONGO_INITDB_ROOT_PASSWORD@$DATABASE_HOST:$DOCKER_MONGODB_PORT/$MONGO_INITDB_DATABASE RABBITMQ_DEFAULT_USER=user RABBITMQ_DEFAULT_PASS=pass diff --git a/.github/workflows/pr-quality.yml b/.github/workflows/pr-quality.yml deleted file mode 100644 index 502f3b7..0000000 --- a/.github/workflows/pr-quality.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: PR Qualiti - -on: - pull_request: - types: - - opened - - synchronize - branches: ["main"] - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@main - - - name: Run make script for build and tests - run: | - make create-env - make build - - sonarq_analysis: - needs: build - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@main - - - name: Set up environment - run: | - make create-env - docker compose -f docker-compose.yml up -d - - - name: Run make script for tests and coverage - run: | - make tests - - - name: Upload coverage report - uses: actions/upload-artifact@v2 - with: - name: coverage-report - path: cover - - - name: SonarQube Scan - uses: SonarSource/sonarqube-scan-action@master - with: - sonar-token: ${{ secrets.SONAR_TOKEN }} - coverage-report-path: cover/coverage.xml - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.local/start_api b/.local/start_api index 4f1cf91..1e4275c 100644 --- a/.local/start_api +++ b/.local/start_api @@ -6,5 +6,5 @@ set -o pipefail if [[ -z "${KUBERNETES_SERVICE_HOST}" ]]; then uvicorn src.app.main:app --host 0.0.0.0 --port 8000 --reload else - uvicorn src.app.main:app --proxy-headers --host 0.0.0.0 --port 8000 + uvicorn src.app.main:app --proxy-headers --host 0.0.0.0 --port 8000 --reload fi \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..958a92d --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: FastAPI", + "type": "python", + "request": "launch", + "module": "uvicorn", + "args": [ + "src.app.main:app", + "--reload" + ], + "jinja": true, + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e8701f0..6d42709 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,8 @@ "makefile.extensionOutputFolder": "./.vscode", "python.linting.enabled": true, "python.linting.mypyEnabled": true, - "python.formatting.provider": "black" + "python.formatting.provider": "black", + "python.testing.pytestArgs": ["."], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true } diff --git a/Dockerfile b/Dockerfile index f320553..7e0f815 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ RUN set -ex; \ # Install Poetry RUN curl -sSL https://install.python-poetry.org | python3 - -WORKDIR /usr/src/app +WORKDIR /code COPY pyproject.toml poetry.lock ./ @@ -43,6 +43,6 @@ RUN set -ex; \ COPY . . -ENTRYPOINT ["/usr/src/entrypoint"] +#ENTRYPOINT ["/usr/src/entrypoint"] CMD ["/usr/src/start_api"] \ No newline at end of file diff --git a/compose-dev.yaml b/compose-dev.yaml new file mode 100644 index 0000000..a92f701 --- /dev/null +++ b/compose-dev.yaml @@ -0,0 +1,12 @@ +services: + app: + entrypoint: + - sleep + - infinity + image: docker/dev-environments-default:stable-1 + init: true + volumes: + - type: bind + source: /var/run/docker.sock + target: /var/run/docker.sock + diff --git a/docker-compose.yml b/docker-compose.yml index 5e84b7e..4300fdb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: env_file: - .env volumes: - - ./:/usr/src/app:delegated + - ./:/code:delegated networks: - mongodb_network - rabbit_network diff --git a/makefile b/makefile index a642ef2..5fc8404 100644 --- a/makefile +++ b/makefile @@ -17,7 +17,7 @@ help: # start project up: - @docker-compose up + @docker-compose up -d # interactive shell shell: @@ -47,7 +47,7 @@ build-cache: # run unit tests tests: - @docker-compose run $(service) pytest --cov=src/ --cov-report=html:cover/html_dir --cov-report=xml:cover/coverage.xml --feature features -vv src/tests/ + @docker-compose run $(service) python -m pytest --cov=src/ --cov-report=html:cover/html_dir --cov-report=xml:cover/coverage.xml --feature features -vv src/tests/ # uninstall project clean: diff --git a/poetry.lock b/poetry.lock index b98001d..b7726a2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -54,6 +54,17 @@ wrapt = [ {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, ] +[[package]] +name = "certifi" +version = "2023.5.7" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] + [[package]] name = "click" version = "8.1.4" @@ -232,6 +243,50 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] +[[package]] +name = "httpcore" +version = "0.17.3" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpcore-0.17.3-py3-none-any.whl", hash = "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87"}, + {file = "httpcore-0.17.3.tar.gz", hash = "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888"}, +] + +[package.dependencies] +anyio = ">=3.0,<5.0" +certifi = "*" +h11 = ">=0.13,<0.15" +sniffio = "==1.*" + +[package.extras] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "httpx" +version = "0.24.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.7" +files = [ + {file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"}, + {file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"}, +] + +[package.dependencies] +certifi = "*" +httpcore = ">=0.15.0,<0.18.0" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + [[package]] name = "idna" version = "3.4" @@ -806,6 +861,23 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +[[package]] +name = "pytest-env" +version = "0.8.2" +description = "py.test plugin that allows you to add environment variables." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest_env-0.8.2-py3-none-any.whl", hash = "sha256:5e533273f4d9e6a41c3a3120e0c7944aae5674fa773b329f00a5eb1f23c53a38"}, + {file = "pytest_env-0.8.2.tar.gz", hash = "sha256:baed9b3b6bae77bd75b9238e0ed1ee6903a42806ae9d6aeffb8754cd5584d4ff"}, +] + +[package.dependencies] +pytest = ">=7.3.1" + +[package.extras] +test = ["coverage (>=7.2.7)", "pytest-mock (>=3.10)"] + [[package]] name = "six" version = "1.16.0" @@ -1002,4 +1074,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "0cb293aa6e292762b0efa75e4573797cef35a22beec647a98356e7ef0a328044" +content-hash = "cca048a468a65a4f22d16584ca56f88ce0b83c330c68c1822cda907aebbded31" diff --git a/pyproject.toml b/pyproject.toml index 3642510..a12025b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ pymongo = "^4.4.0" unicorn = "^2.0.1.post1" pika = "^1.3.2" uvicorn = "^0.22.0" +httpx = "^0.24.1" [tool.poetry.group.dev.dependencies] @@ -20,6 +21,7 @@ pylint = "^2.17.4" pytest = "^7.4.0" pytest-bdd = "^6.1.1" pytest-cov = "^4.1.0" +pytest-env = "^0.8.2" [build-system] requires = ["poetry-core"] diff --git a/src/tests/__init__.py b/src/app/__init__.py similarity index 100% rename from src/tests/__init__.py rename to src/app/__init__.py diff --git a/src/app/main.py b/src/app/main.py index 30d4a45..e230b67 100644 --- a/src/app/main.py +++ b/src/app/main.py @@ -1,15 +1,155 @@ -from typing import Union +from fastapi import FastAPI, Response, status, HTTPException +from pydantic import BaseModel +from pymongo import MongoClient, ReturnDocument +from typing import List +from uuid import UUID, uuid4 +import os +import re -from fastapi import FastAPI + +# Core +class Channel(BaseModel): + id: str + name: str + + +class Service(BaseModel): + id: str + name: str + hostName: str + endpoint: str + + +class Consumer(BaseModel): + id: str + name: str + services: List[Service] + + +class ChannelConflictException(Exception): + "Existing channel with some of this attributes" + pass + + +class ConsumerConflictException(Exception): + "Existing channel with some of this attributes" + pass + + +# Infra + +client = MongoClient(os.getenv("MONGODB_URL", "Nothing was set")) +db = client[os.getenv("MONGO_INITDB_DATABASE", "Nothing was set")] +channel_collection = db["channels"] +consumer_collection = db["consumers"] + + +class ChannelRepository: + def create_channel(self, channel: Channel): + existing_channel = channel_collection.find_one({"id": channel.id}) + if existing_channel: + raise ChannelConflictException() + channel_collection.insert_one(channel.model_dump()) + + def find_channel_by_name(self, channel_name: str): + return channel_collection.find_one({"name": channel_name}) + + +class ConsumerRepository: + def update_consumer(self, consumer: Consumer): + return consumer_collection.find_one_and_update( + {"id": consumer.id, "name": consumer.name}, + {"$set": consumer.model_dump()}, + return_document=ReturnDocument.AFTER, + ) + + def create_consumer(self, consumer: Consumer): + existing_consumer = consumer_collection.find_one({"id": consumer.id}) + if existing_consumer: + raise ConsumerConflictException() + + consumer_collection.insert_one(consumer.model_dump()) + + def find_consumer_by_name(self, consumer_name: str): + return consumer_collection.find_one({"name": consumer_name}) + + +# Controller app = FastAPI() +channel_repository = ChannelRepository() +consumer_repository = ConsumerRepository() + + +@app.put("/channels/{channel_id}") +def read_item(channel_id: str, channel: Channel, response: Response): + # Validations + channel_name_regex = r"^[a-z0-9]+(?:-[a-z0-9]+)*$" + if channel.id != channel_id or re.search(channel_name_regex, channel.name) is None: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Ids do not match" + ) + + try: + existing_channel = channel_repository.find_channel_by_name(channel.name) + + if existing_channel is None: + channel_repository.create_channel(channel) + response.status_code = status.HTTP_201_CREATED + return None + + if existing_channel["id"] == channel.id: + response.status_code = status.HTTP_200_OK + return None + + raise ChannelConflictException() + + except ChannelConflictException as e: + raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e)) + except Exception as e: + print(e) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@app.put("/consumers/{consumer_id}") +def read_item(consumer_id: str, consumer: Consumer, response: Response): + # Validations + names_regex = r"^[a-z0-9]+(?:-[a-z0-9]+)*$" + if ( + consumer.id != consumer_id + or re.search(names_regex, consumer.name) is None + or (consumer.services is None or len(consumer.services) == 0) + ): + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Ids do not match" + ) + for services in consumer.services: + if re.search(names_regex, services.name) is None: + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, + detail="Ids do not match", + ) + + try: + existing_consumer = consumer_repository.find_consumer_by_name(consumer.name) + + if existing_consumer is None: + consumer_repository.create_consumer(consumer) + response.status_code = status.HTTP_201_CREATED + return None -@app.get("/") -def read_root(): - return {"Hello": "World"} + if existing_consumer["id"] == consumer.id: + consumer_updater = consumer_repository.update_consumer(consumer) + if consumer_updater: + response.status_code = status.HTTP_200_OK + return None + raise Exception("Non updated consumer") + raise ConsumerConflictException() -@app.get("/items/{item_id}") -def read_item(item_id: int, q: Union[str, None] = None): - return {"item_id": item_id, "q": q} + except ConsumerConflictException as e: + raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e)) + except Exception as e: + print(e) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/src/tests/integration_tests/features/__init__.py b/src/tests/functional/features/admin/__init__.py similarity index 100% rename from src/tests/integration_tests/features/__init__.py rename to src/tests/functional/features/admin/__init__.py diff --git a/src/tests/functional/features/admin/create_channel.feature b/src/tests/functional/features/admin/create_channel.feature new file mode 100644 index 0000000..c3631cb --- /dev/null +++ b/src/tests/functional/features/admin/create_channel.feature @@ -0,0 +1,89 @@ +Feature: Create a new channel + In order to have channels through which messages are sent + As a user with admin permissions + I want to create a new channel + + Scenario: A valid non existing channel + Given The body: + """ + { + "id": "ef8ac118-8d7f-49cc-abec-78e0d05af80a", + "name": "leads-creation" + } + """ + When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + Then the response status code should be "201" + And the response should be empty + + Scenario: An invalid non existing channel + Given The body: + """ + { + "id": "ef8ac118-8d7f-49cc-abec-78e0d05af80a", + "name": "leads_creation" + } + """ + When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + Then the response status code should be "422" + + Scenario: An valid existing channel + Given The body: + """ + { + "id": "ef8ac118-8d7f-49cc-abec-78e0d05af80a", + "name": "leads-creation" + } + """ + When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + Given The body: + """ + { + "id": "ef8ac118-8d7f-49cc-abec-78e0d05af80a", + "name": "leads-creation" + } + """ + When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + Then the response status code should be "200" + And the response should be empty + + Scenario: Trying to rewrite a channel + Given The body: + """ + { + "id": "ef8ac118-8d7f-49cc-abec-78e0d05af80a", + "name": "leads-creation" + } + """ + When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + Given The body: + """ + { + "id": "ef8ac118-8d7f-49cc-abec-78e0d05af80a", + "name": "other-name" + } + """ + When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + Then the response status code should be "409" + + + Scenario: Trying to create a channel with an existing channel name + Given The body: + """ + { + "id": "j2fyo50q-8d7f-49cc-abec-78e0d05af440", + "name": "leads-creation" + } + """ + When I send a PUT request to "/channels/j2fyo50q-8d7f-49cc-abec-78e0d05af440" with the body + Then the response status code should be "409" + + Scenario: Trying to create a channel with diferent Ids + Given The body: + """ + { + "id": "j2fyo50q-8d7f-49cc-abec-78e0d05af440", + "name": "leads-creation" + } + """ + When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + Then the response status code should be "422" diff --git a/src/tests/functional/features/admin/manage_consumer.feature b/src/tests/functional/features/admin/manage_consumer.feature new file mode 100644 index 0000000..f962251 --- /dev/null +++ b/src/tests/functional/features/admin/manage_consumer.feature @@ -0,0 +1,176 @@ +Feature: Create a new consumer + In order to have consumers through which messages are sent + As a user with admin permissions + I want to create a new consumer + + Scenario: A valid non existing consumer + Given The body: + """ + { + "id": "ef8ac118-8d7f-49cc-abec-78e0d05af80a", + "name": "harley-davidson", + "services": [ + { + "id": "ea6a02d4-250f-11ee-be56-0242ac120002", + "name": "leads-catcher", + "hostName": "localhost", + "endpoint": "/lead" + } + ] + } + """ + When I send a PUT request to "/consumers/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + Then the response status code should be "201" + And the response should be empty + + Scenario: Update a valid existing consumer + Given The body: + """ + { + "id": "ef8ac118-8d7f-49cc-abec-78e0d05af80a", + "name": "harley-davidson", + "services": [ + { + "id": "ea6a02d4-250f-11ee-be56-0242ac120002", + "name": "leads-catcher", + "hostName": "localhost", + "endpoint": "/leads" + }, + { + "id": "9832ead4-2510-11ee-be56-0242ac120002", + "name": "customer", + "hostName": "localhost", + "endpoint": "/customers" + } + ] + } + """ + When I send a PUT request to "/consumers/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + Then the response status code should be "200" + And the response should be empty + + Scenario: An invalid consumer, no services + Given The body: + """ + { + "id": "56d09f90-2511-11ee-be56-0242ac120002", + "name": "harley-davidson", + "services": [] + } + """ + When I send a PUT request to "/consumers/56d09f90-2511-11ee-be56-0242ac120002" with the body + Then the response status code should be "422" + + Scenario: An invalid consumer, the ids do not match + Given The body: + """ + { + "id": "56d0a38c-2511-11ee-be56-0242ac120002", + "name": "harley-davidson", + "services": [ + { + "id": "9832ead4-2510-11ee-be56-0242ac120002", + "name": "customer", + "hostName": "localhost", + "endpoint": "/customers" + } + ] + } + """ + When I send a PUT request to "/consumers/56d0a5e4-2511-11ee-be56-0242ac120002" with the body + Then the response status code should be "422" + + Scenario: An invalid consumer, bad naming + Given The body: + """ + { + "id": "56d0a80a-2511-11ee-be56-0242ac120002", + "name": "harley_davidson", + "services": [ + + { + "id": "9832ead4-2510-11ee-be56-0242ac120002", + "name": "customer", + "hostName": "localhost", + "endpoint": "/customers" + } + ] + } + """ + When I send a PUT request to "/consumers/56d0a80a-2511-11ee-be56-0242ac120002" with the body + Then the response status code should be "422" + + + Scenario: An invalid consumer, service bad naming + Given The body: + """ + { + "id": "56d0a80a-2511-11ee-be56-0242ac120002", + "name": "harley-davidson", + "services": [ + { + "id": "9832ead4-2510-11ee-be56-0242ac120002", + "name": "customer_catcher", + "hostName": "localhost", + "endpoint": "/customers" + } + ] + } + """ + When I send a PUT request to "/consumers/56d0a80a-2511-11ee-be56-0242ac120002" with the body + Then the response status code should be "422" + + Scenario: Trying to create a consumer with an existing consumer name + Given The body: + """ + { + "id": "56d0aa76-2511-11ee-be56-0242ac120002", + "name": "harley-davidson", + "services": [ + { + "id": "ea6a02d4-250f-11ee-be56-0242ac120002", + "name": "leads-catcher", + "hostName": "localhost", + "endpoint": "/lead" + } + ] + } + """ + When I send a PUT request to "/consumers/56d0aa76-2511-11ee-be56-0242ac120002" with the body + Then the response status code should be "409" + + + Scenario: Trying to create a consumer with an existing consumer id + Given The body: + """ + { + "id": "56d0b62e-2511-11ee-be56-0242ac120002", + "name": "toyota-peru", + "services": [ + { + "id": "ea6a02d4-250f-11ee-be56-0242ac120002", + "name": "leads-catcher", + "hostName": "localhost", + "endpoint": "/lead" + } + ] + } + """ + When I send a PUT request to "/consumers/56d0b62e-2511-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "id": "56d0b62e-2511-11ee-be56-0242ac120002", + "name": "toyota-ar", + "services": [ + { + "id": "ea6a02d4-250f-11ee-be56-0242ac120002", + "name": "leads-catcher", + "hostName": "localhost", + "endpoint": "/lead" + } + ] + } + """ + When I send a PUT request to "/consumers/56d0b62e-2511-11ee-be56-0242ac120002" with the body + Then the response status code should be "409" \ No newline at end of file diff --git a/src/tests/functional/features/admin/test_feature_spects.py b/src/tests/functional/features/admin/test_feature_spects.py new file mode 100644 index 0000000..7e813d3 --- /dev/null +++ b/src/tests/functional/features/admin/test_feature_spects.py @@ -0,0 +1,33 @@ +import os +from pytest_bdd import scenarios, given, when, then, parsers +from fastapi.testclient import TestClient +from src.app.main import app +import json + +scenarios("create_channel.feature", "manage_consumer.feature") + +client = TestClient(app) +request_body = "" +response = "" + + +@given(parsers.parse("The body:\n{body}")) +def set_body(body): + global request_body + request_body = json.loads(body) + + +@when(parsers.parse('I send a PUT request to "{url}" with the body')) +def send_put_request(url): + global response + response = client.put(url, json=request_body) + + +@then(parsers.parse('the response status code should be "{code}"')) +def check_response_status_code_201(code): + assert response.status_code == int(code) + + +@then("the response should be empty") +def check_response_empty(): + assert response.json() is None diff --git a/src/tests/integration_tests/features/arguments.feature b/src/tests/integration_tests/features/arguments.feature deleted file mode 100644 index 1ceec96..0000000 --- a/src/tests/integration_tests/features/arguments.feature +++ /dev/null @@ -1,7 +0,0 @@ -Feature: Step arguments - - Scenario: Arguments for given, when, then - Given there are 5 cucumbers - When I eat 3 cucumbers - And I eat 2 cucumbers - Then I should have 0 cucumbers diff --git a/src/tests/integration_tests/features/test_publish_article.py b/src/tests/integration_tests/features/test_publish_article.py deleted file mode 100644 index 0a1b774..0000000 --- a/src/tests/integration_tests/features/test_publish_article.py +++ /dev/null @@ -1,19 +0,0 @@ -from pytest_bdd import scenarios, given, when, then, parsers - - -scenarios("arguments.feature") - - -@given(parsers.parse("there are {start:d} cucumbers"), target_fixture="cucumbers") -def given_cucumbers(start): - return {"start": start, "eat": 0} - - -@when(parsers.parse("I eat {eat:d} cucumbers")) -def eat_cucumbers(cucumbers, eat): - cucumbers["eat"] += eat - - -@then(parsers.parse("I should have {left:d} cucumbers")) -def should_have_left_cucumbers(cucumbers, left): - assert cucumbers["start"] - cucumbers["eat"] == left diff --git a/src/tests/pytest.ini b/src/tests/pytest.ini new file mode 100644 index 0000000..c99a3e4 --- /dev/null +++ b/src/tests/pytest.ini @@ -0,0 +1,9 @@ +[pytest] +env = + MONGO_INITDB_DATABASE=test_mapi, + MONGO_INITDB_ROOT_USERNAME=user, + MONGO_INITDB_ROOT_PASSWORD=pass, + DATABASE_HOST=mapi_mongodb, + DOCKER_MONGODB_PORT=27017, + MONGODB_ENABLE_ADMIN=true, + MONGODB_URL=mongodb://user:pass@mapi_mongodb:27017 \ No newline at end of file diff --git a/src/tests/unit_tests/__init__.py b/src/tests/unit_tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/tests/unit_tests/test_sysexit.py b/src/tests/unit_tests/test_sysexit.py deleted file mode 100644 index c460a66..0000000 --- a/src/tests/unit_tests/test_sysexit.py +++ /dev/null @@ -1,10 +0,0 @@ -import pytest - - -def f(): - raise SystemExit(1) - - -def test_mytest(): - with pytest.raises(SystemExit): - f() \ No newline at end of file From 0d9492d4ebbc87878aebe19e72450b04c1b23984 Mon Sep 17 00:00:00 2001 From: Matias Chediek Date: Tue, 18 Jul 2023 21:55:33 -0300 Subject: [PATCH 6/6] ClickUp #85ztfjmg3 Start subscription logic --- src/app/main.py | 75 +++++- .../features/admin/create_channel.feature | 16 +- .../features/admin/manage_consumer.feature | 23 +- .../admin/manage_subscriptions.feature | 235 ++++++++++++++++++ .../features/admin/test_feature_spects.py | 11 +- src/tests/pytest.ini | 12 +- 6 files changed, 342 insertions(+), 30 deletions(-) create mode 100644 src/tests/functional/features/admin/manage_subscriptions.feature diff --git a/src/app/main.py b/src/app/main.py index e230b67..9e2ada1 100644 --- a/src/app/main.py +++ b/src/app/main.py @@ -26,6 +26,12 @@ class Consumer(BaseModel): services: List[Service] +class Subscription(BaseModel): + channelName: str + consumerName: str + serviceName: str + + class ChannelConflictException(Exception): "Existing channel with some of this attributes" pass @@ -42,6 +48,7 @@ class ConsumerConflictException(Exception): db = client[os.getenv("MONGO_INITDB_DATABASE", "Nothing was set")] channel_collection = db["channels"] consumer_collection = db["consumers"] +subscription_collection = db["subscriptions"] class ChannelRepository: @@ -74,12 +81,21 @@ def find_consumer_by_name(self, consumer_name: str): return consumer_collection.find_one({"name": consumer_name}) +class SubscriptionRepository: + def create_subscription(self, subscription: Subscription): + subscription_collection.insert_one(subscription.model_dump()) + + def find_subscription(self, subscription: Subscription): + return subscription_collection.find_one(subscription.model_dump()) + + # Controller app = FastAPI() channel_repository = ChannelRepository() consumer_repository = ConsumerRepository() +subscription_repository = SubscriptionRepository() @app.put("/channels/{channel_id}") @@ -92,6 +108,7 @@ def read_item(channel_id: str, channel: Channel, response: Response): ) try: + # business logic existing_channel = channel_repository.find_channel_by_name(channel.name) if existing_channel is None: @@ -130,8 +147,8 @@ def read_item(consumer_id: str, consumer: Consumer, response: Response): status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Ids do not match", ) - try: + # business logic existing_consumer = consumer_repository.find_consumer_by_name(consumer.name) if existing_consumer is None: @@ -153,3 +170,59 @@ def read_item(consumer_id: str, consumer: Consumer, response: Response): except Exception as e: print(e) raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + + +@app.post("/subscriptions") +def read_item(subscription: Subscription, response: Response): + # Validations + if ( + (subscription.channelName is None or subscription.channelName == "") + or (subscription.consumerName is None or subscription.consumerName == "") + or (subscription.serviceName is None or subscription.serviceName == "") + ): + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Ids do not match" + ) + + try: + # business logic + + existing_channel = channel_repository.find_channel_by_name( + subscription.channelName + ) + + if existing_channel is None: + response.status_code = status.HTTP_404_NOT_FOUND + return None + + existing_consumer = consumer_repository.find_consumer_by_name( + subscription.consumerName + ) + + if existing_consumer is None: + response.status_code = status.HTTP_404_NOT_FOUND + return None + + existing_service = [ + service + for service in existing_consumer["services"] + if service["name"] == subscription.serviceName + ] + + if len(existing_service) == 0: + response.status_code = status.HTTP_404_NOT_FOUND + return None + + existing_subscription = subscription_repository.find_subscription(subscription) + + if existing_subscription: + response.status_code = status.HTTP_200_OK + return None + + subscription_repository.create_subscription(subscription) + response.status_code = status.HTTP_201_CREATED + return None + + except Exception as e: + print(e) + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/src/tests/functional/features/admin/create_channel.feature b/src/tests/functional/features/admin/create_channel.feature index c3631cb..a4657da 100644 --- a/src/tests/functional/features/admin/create_channel.feature +++ b/src/tests/functional/features/admin/create_channel.feature @@ -11,7 +11,7 @@ Feature: Create a new channel "name": "leads-creation" } """ - When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + When I send a "PUT" request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body Then the response status code should be "201" And the response should be empty @@ -23,7 +23,7 @@ Feature: Create a new channel "name": "leads_creation" } """ - When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + When I send a "PUT" request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body Then the response status code should be "422" Scenario: An valid existing channel @@ -34,7 +34,7 @@ Feature: Create a new channel "name": "leads-creation" } """ - When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + When I send a "PUT" request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body Given The body: """ { @@ -42,7 +42,7 @@ Feature: Create a new channel "name": "leads-creation" } """ - When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + When I send a "PUT" request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body Then the response status code should be "200" And the response should be empty @@ -54,7 +54,7 @@ Feature: Create a new channel "name": "leads-creation" } """ - When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + When I send a "PUT" request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body Given The body: """ { @@ -62,7 +62,7 @@ Feature: Create a new channel "name": "other-name" } """ - When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + When I send a "PUT" request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body Then the response status code should be "409" @@ -74,7 +74,7 @@ Feature: Create a new channel "name": "leads-creation" } """ - When I send a PUT request to "/channels/j2fyo50q-8d7f-49cc-abec-78e0d05af440" with the body + When I send a "PUT" request to "/channels/j2fyo50q-8d7f-49cc-abec-78e0d05af440" with the body Then the response status code should be "409" Scenario: Trying to create a channel with diferent Ids @@ -85,5 +85,5 @@ Feature: Create a new channel "name": "leads-creation" } """ - When I send a PUT request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + When I send a "PUT" request to "/channels/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body Then the response status code should be "422" diff --git a/src/tests/functional/features/admin/manage_consumer.feature b/src/tests/functional/features/admin/manage_consumer.feature index f962251..64c8d13 100644 --- a/src/tests/functional/features/admin/manage_consumer.feature +++ b/src/tests/functional/features/admin/manage_consumer.feature @@ -1,9 +1,10 @@ Feature: Create a new consumer - In order to have consumers through which messages are sent + In order to have consumers which will subscribe to channels As a user with admin permissions - I want to create a new consumer + I want to manage consumers Scenario: A valid non existing consumer + Given The body: """ { @@ -19,7 +20,7 @@ Feature: Create a new consumer ] } """ - When I send a PUT request to "/consumers/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + When I send a "PUT" request to "/consumers/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body Then the response status code should be "201" And the response should be empty @@ -45,7 +46,7 @@ Feature: Create a new consumer ] } """ - When I send a PUT request to "/consumers/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body + When I send a "PUT" request to "/consumers/ef8ac118-8d7f-49cc-abec-78e0d05af80a" with the body Then the response status code should be "200" And the response should be empty @@ -58,7 +59,7 @@ Feature: Create a new consumer "services": [] } """ - When I send a PUT request to "/consumers/56d09f90-2511-11ee-be56-0242ac120002" with the body + When I send a "PUT" request to "/consumers/56d09f90-2511-11ee-be56-0242ac120002" with the body Then the response status code should be "422" Scenario: An invalid consumer, the ids do not match @@ -77,7 +78,7 @@ Feature: Create a new consumer ] } """ - When I send a PUT request to "/consumers/56d0a5e4-2511-11ee-be56-0242ac120002" with the body + When I send a "PUT" request to "/consumers/56d0a5e4-2511-11ee-be56-0242ac120002" with the body Then the response status code should be "422" Scenario: An invalid consumer, bad naming @@ -97,7 +98,7 @@ Feature: Create a new consumer ] } """ - When I send a PUT request to "/consumers/56d0a80a-2511-11ee-be56-0242ac120002" with the body + When I send a "PUT" request to "/consumers/56d0a80a-2511-11ee-be56-0242ac120002" with the body Then the response status code should be "422" @@ -117,7 +118,7 @@ Feature: Create a new consumer ] } """ - When I send a PUT request to "/consumers/56d0a80a-2511-11ee-be56-0242ac120002" with the body + When I send a "PUT" request to "/consumers/56d0a80a-2511-11ee-be56-0242ac120002" with the body Then the response status code should be "422" Scenario: Trying to create a consumer with an existing consumer name @@ -136,7 +137,7 @@ Feature: Create a new consumer ] } """ - When I send a PUT request to "/consumers/56d0aa76-2511-11ee-be56-0242ac120002" with the body + When I send a "PUT" request to "/consumers/56d0aa76-2511-11ee-be56-0242ac120002" with the body Then the response status code should be "409" @@ -156,7 +157,7 @@ Feature: Create a new consumer ] } """ - When I send a PUT request to "/consumers/56d0b62e-2511-11ee-be56-0242ac120002" with the body + When I send a "PUT" request to "/consumers/56d0b62e-2511-11ee-be56-0242ac120002" with the body Given The body: """ { @@ -172,5 +173,5 @@ Feature: Create a new consumer ] } """ - When I send a PUT request to "/consumers/56d0b62e-2511-11ee-be56-0242ac120002" with the body + When I send a "PUT" request to "/consumers/56d0b62e-2511-11ee-be56-0242ac120002" with the body Then the response status code should be "409" \ No newline at end of file diff --git a/src/tests/functional/features/admin/manage_subscriptions.feature b/src/tests/functional/features/admin/manage_subscriptions.feature new file mode 100644 index 0000000..d17cfa8 --- /dev/null +++ b/src/tests/functional/features/admin/manage_subscriptions.feature @@ -0,0 +1,235 @@ +Feature: Create a new consumer + In order to have subscribed consumers to channels + As a user with admin permissions + I want to manage a consumer + + Scenario: A valid non existing subscription + + Given The body: + """ + { + "id": "b252d466-259b-11ee-be56-0242ac120002", + "name": "product-creation" + } + """ + When I send a "PUT" request to "/channels/b252d466-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "id": "b252d9de-259b-11ee-be56-0242ac120002", + "name": "hd", + "services": [ + { + "id": "b252dba0-259b-11ee-be56-0242ac120002", + "name": "new-product", + "hostName": "localhost", + "endpoint": "/products" + } + ] + } + """ + When I send a "PUT" request to "/consumers/b252d9de-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "channelName": "product-creation", + "consumerName": "hd", + "serviceName": "new-product" + } + """ + When I send a "POST" request to "/subscriptions" with the body + Then the response status code should be "201" + And the response should be empty + + Scenario: A valid existing subscription + + Given The body: + """ + { + "id": "b252d466-259b-11ee-be56-0242ac120002", + "name": "product-creation" + } + """ + When I send a "PUT" request to "/channels/b252d466-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "id": "b252d9de-259b-11ee-be56-0242ac120002", + "name": "hd", + "services": [ + { + "id": "b252dba0-259b-11ee-be56-0242ac120002", + "name": "new-product", + "hostName": "localhost", + "endpoint": "/products" + } + ] + } + """ + When I send a "PUT" request to "/consumers/b252d9de-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "channelName": "product-creation", + "consumerName": "hd", + "serviceName": "new-product" + } + """ + When I send a "POST" request to "/subscriptions" with the body + Then the response status code should be "200" + And the response should be empty + + Scenario: A subscription without channel + + Given The body: + """ + { + "id": "b252de0c-259b-11ee-be56-0242ac120002", + "name": "hd-2", + "services": [ + { + "id": "b252dba0-259b-11ee-be56-0242ac120002", + "name": "new-product", + "hostName": "localhost", + "endpoint": "/products" + } + ] + } + """ + When I send a "PUT" request to "/consumers/b252de0c-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "consumerName": "hd-2", + "serviceName": "new-product" + } + """ + When I send a "POST" request to "/subscriptions" with the body + Then the response status code should be "422" + + Scenario: A subscription without consumer + + Given The body: + """ + { + "id": "b252e104-259b-11ee-be56-0242ac120002", + "name": "customer-update" + } + """ + When I send a "PUT" request to "/channels/b252e104-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "channelName": "customer-update", + "serviceName": "new-product" + } + """ + When I send a "POST" request to "/subscriptions" with the body + Then the response status code should be "422" + + Scenario: A subscription without service + + Given The body: + """ + { + "id": "b252e230-259b-11ee-be56-0242ac120002", + "name": "customer-delete" + } + """ + When I send a "PUT" request to "/channels/b252e230-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "channelName": "customer-delete", + "consumerName": "hd-2" + } + """ + When I send a "POST" request to "/subscriptions" with the body + Then the response status code should be "422" + + Scenario: A subscription with a non existent channel + + Given The body: + """ + { + "id": "b252de0c-259b-11ee-be56-0242ac120002", + "name": "hd-2", + "services": [ + { + "id": "b252dba0-259b-11ee-be56-0242ac120002", + "name": "new-product", + "hostName": "localhost", + "endpoint": "/products" + } + ] + } + """ + When I send a "PUT" request to "/consumers/b252de0c-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "channelName": "fake", + "consumerName": "hd-2", + "serviceName": "new-product" + } + """ + When I send a "POST" request to "/subscriptions" with the body + Then the response status code should be "404" + + Scenario: A subscription with a non existent consumer + + Given The body: + """ + { + "id": "b252e104-259b-11ee-be56-0242ac120002", + "name": "customer-update" + } + """ + When I send a "PUT" request to "/channels/b252e104-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "channelName": "customer-update", + "consumerName": "fake", + "serviceName": "new-product" + } + """ + When I send a "POST" request to "/subscriptions" with the body + Then the response status code should be "404" + + Scenario: A subscription with a non existent service + + Given The body: + """ + { + "id": "b252e104-259b-11ee-be56-0242ac120002", + "name": "customer-update" + } + """ + When I send a "PUT" request to "/channels/b252e104-259b-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "id": "94fa6950-25a6-11ee-be56-0242ac120002", + "name": "hd-3", + "services": [ + { + "id": "b252dba0-259b-11ee-be56-0242ac120002", + "name": "new-product", + "hostName": "localhost", + "endpoint": "/products" + } + ] + } + """ + When I send a "PUT" request to "/consumers/94fa6950-25a6-11ee-be56-0242ac120002" with the body + Given The body: + """ + { + "channelName": "customer-update", + "consumerName": "hd-3", + "serviceName": "fake" + } + """ + When I send a "POST" request to "/subscriptions" with the body + Then the response status code should be "404" \ No newline at end of file diff --git a/src/tests/functional/features/admin/test_feature_spects.py b/src/tests/functional/features/admin/test_feature_spects.py index 7e813d3..9434bb4 100644 --- a/src/tests/functional/features/admin/test_feature_spects.py +++ b/src/tests/functional/features/admin/test_feature_spects.py @@ -4,7 +4,9 @@ from src.app.main import app import json -scenarios("create_channel.feature", "manage_consumer.feature") +scenarios( + "create_channel.feature", "manage_consumer.feature", "manage_subscriptions.feature" +) client = TestClient(app) request_body = "" @@ -17,10 +19,11 @@ def set_body(body): request_body = json.loads(body) -@when(parsers.parse('I send a PUT request to "{url}" with the body')) -def send_put_request(url): +@when(parsers.parse('I send a "{method}" request to "{url}" with the body')) +def send_put_request(method, url): global response - response = client.put(url, json=request_body) + + response = client.request(method, url, json=request_body) @then(parsers.parse('the response status code should be "{code}"')) diff --git a/src/tests/pytest.ini b/src/tests/pytest.ini index c99a3e4..c8be6d4 100644 --- a/src/tests/pytest.ini +++ b/src/tests/pytest.ini @@ -1,9 +1,9 @@ [pytest] env = - MONGO_INITDB_DATABASE=test_mapi, - MONGO_INITDB_ROOT_USERNAME=user, - MONGO_INITDB_ROOT_PASSWORD=pass, - DATABASE_HOST=mapi_mongodb, - DOCKER_MONGODB_PORT=27017, - MONGODB_ENABLE_ADMIN=true, + MONGO_INITDB_DATABASE=test_mapi + MONGO_INITDB_ROOT_USERNAME=user + MONGO_INITDB_ROOT_PASSWORD=pass + DATABASE_HOST=mapi_mongodb + DOCKER_MONGODB_PORT=27017 + MONGODB_ENABLE_ADMIN=true MONGODB_URL=mongodb://user:pass@mapi_mongodb:27017 \ No newline at end of file