From 2a8c48ac8ed740340b6254a493c30889deafd196 Mon Sep 17 00:00:00 2001 From: Christoph Zwerschke Date: Wed, 7 Aug 2024 14:11:43 +0200 Subject: [PATCH] Support facets with intermediate arrays and improve fixtures (#31) --- .pyproject_generation/pyproject_custom.toml | 2 +- .template/mandatory_files.txt | 1 + Dockerfile | 38 +++-- Dockerfile.debian | 48 ++++++ README.md | 22 +-- lock/requirements-dev.txt | 170 ++++++++++--------- lock/requirements.txt | 164 +++++++++--------- openapi.yaml | 2 +- pyproject.toml | 2 +- src/mass/adapters/outbound/utils.py | 62 ++++--- tests/fixtures/joint.py | 101 ++++++++--- tests/fixtures/test_config.yaml | 2 + tests/fixtures/test_data/FilteringTests.json | 51 ++++++ tests/test_api.py | 2 +- tests/test_consumer.py | 16 +- tests/test_filtering.py | 67 +++++++- tests/test_index_creation.py | 16 +- tests/test_logging.py | 6 +- tests/test_relevance.py | 2 +- tests/test_resources.py | 47 +++-- 20 files changed, 528 insertions(+), 293 deletions(-) create mode 100644 Dockerfile.debian diff --git a/.pyproject_generation/pyproject_custom.toml b/.pyproject_generation/pyproject_custom.toml index 73a74d5..407f365 100644 --- a/.pyproject_generation/pyproject_custom.toml +++ b/.pyproject_generation/pyproject_custom.toml @@ -1,6 +1,6 @@ [project] name = "mass" -version = "3.0.1" +version = "3.0.2" description = "Metadata Artifact Search Service - A service for searching metadata artifacts and filtering results." dependencies = [ "typer>=0.12", diff --git a/.template/mandatory_files.txt b/.template/mandatory_files.txt index 660a15e..fcfb0f8 100644 --- a/.template/mandatory_files.txt +++ b/.template/mandatory_files.txt @@ -23,6 +23,7 @@ lock/requirements-dev.txt lock/requirements.txt Dockerfile +Dockerfile.debian config_schema.json example_config.yaml LICENSE diff --git a/Dockerfile b/Dockerfile index 30993ee..f120a7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,35 +13,39 @@ # See the License for the specific language governing permissions and # limitations under the License. -## creating building container -FROM python:3.12-slim-bookworm AS builder -# update and install dependencies -RUN apt update -RUN apt upgrade -y +# BASE: a base image with updated packages +FROM python:3.12-alpine AS base +#RUN apk update +#RUN apk upgrade --available +RUN apk upgrade --no-cache --available + +# BUILDER: a container to build the service wheel +FROM base as builder RUN pip install build -# copy code COPY . /service WORKDIR /service -# build wheel RUN python -m build -# creating running container -FROM python:3.12-slim-bookworm -# update and install dependencies -RUN apt update -RUN apt upgrade -y -# copy and install requirements and wheel +# DEP-BUILDER: a container to (build and) install dependencies +FROM base AS dep-builder +RUN apk update +RUN apk add build-base gcc g++ libffi-dev zlib-dev +RUN apk upgrade --available WORKDIR /service COPY --from=builder /service/lock/requirements.txt /service RUN pip install --no-deps -r requirements.txt -RUN rm requirements.txt + +# RUNNER: a container to run the service +FROM base as runner +WORKDIR /service +RUN rm -rf /usr/local/lib/python3.12 && mkdir /usr/local/lib/python3.12 +COPY --from=dep-builder /usr/local/lib/python3.12 /usr/local/lib/python3.12 COPY --from=builder /service/dist/ /service RUN pip install --no-deps *.whl RUN rm *.whl -# create new user and execute as that user -RUN useradd --create-home appuser +RUN adduser -D appuser WORKDIR /home/appuser USER appuser -# set environment ENV PYTHONUNBUFFERED=1 + ENTRYPOINT ["mass"] diff --git a/Dockerfile.debian b/Dockerfile.debian new file mode 100644 index 0000000..bdeb83a --- /dev/null +++ b/Dockerfile.debian @@ -0,0 +1,48 @@ +# Copyright 2021 - 2024 Universität Tübingen, DKFZ, EMBL, and Universität zu Köln +# for the German Human Genome-Phenome Archive (GHGA) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +## creating building container +FROM python:3.12-slim-bookworm AS builder +# update and install dependencies +RUN apt update +RUN apt upgrade -y +RUN pip install build +# copy code +COPY . /service +WORKDIR /service +# build wheel +RUN python -m build + +# creating running container +FROM python:3.12-slim-bookworm +# update and install dependencies +RUN apt update +RUN apt upgrade -y +# copy and install requirements and wheel +WORKDIR /service +COPY --from=builder /service/lock/requirements.txt /service +RUN pip install --no-deps -r requirements.txt +RUN rm requirements.txt +COPY --from=builder /service/dist/ /service +RUN pip install --no-deps *.whl +RUN rm *.whl +# create new user and execute as that user +RUN useradd --create-home appuser +WORKDIR /home/appuser +USER appuser +# set environment +ENV PYTHONUNBUFFERED=1 + +ENTRYPOINT ["mass"] diff --git a/README.md b/README.md index 397465e..a8eee6f 100644 --- a/README.md +++ b/README.md @@ -37,13 +37,13 @@ We recommend using the provided Docker container. A pre-build version is available at [docker hub](https://hub.docker.com/repository/docker/ghga/mass): ```bash -docker pull ghga/mass:3.0.1 +docker pull ghga/mass:3.0.2 ``` Or you can build the container yourself from the [`./Dockerfile`](./Dockerfile): ```bash # Execute in the repo's root dir: -docker build -t ghga/mass:3.0.1 . +docker build -t ghga/mass:3.0.2 . ``` For production-ready deployment, we recommend using Kubernetes, however, @@ -51,7 +51,7 @@ for simple use cases, you could execute the service using docker on a single server: ```bash # The entrypoint is preconfigured: -docker run -p 8080:8080 ghga/mass:3.0.1 --help +docker run -p 8080:8080 ghga/mass:3.0.2 --help ``` If you prefer not to use containers, you may install the service from source: @@ -72,7 +72,7 @@ The service requires the following configuration parameters: - **`service_name`** *(string)*: Default: `"mass"`. -- **`service_instance_id`** *(string)*: A string that uniquely identifies this instance across all instances of this service. A globally unique Kafka client ID will be created by concatenating the service_name and the service_instance_id. +- **`service_instance_id`** *(string, required)*: A string that uniquely identifies this instance across all instances of this service. A globally unique Kafka client ID will be created by concatenating the service_name and the service_instance_id. Examples: @@ -105,11 +105,11 @@ The service requires the following configuration parameters: - **`log_traceback`** *(boolean)*: Whether to include exception tracebacks in log messages. Default: `true`. -- **`searchable_classes`** *(object)*: A collection of searchable_classes with facetable and selected fields. Can contain additional properties. +- **`searchable_classes`** *(object, required)*: A collection of searchable_classes with facetable and selected fields. Can contain additional properties. - **Additional properties**: Refer to *[#/$defs/SearchableClass](#%24defs/SearchableClass)*. -- **`resource_change_event_topic`** *(string)*: Name of the event topic used to track resource deletion and upsertion events. +- **`resource_change_event_topic`** *(string, required)*: Name of the event topic used to track resource deletion and upsertion events. Examples: @@ -119,7 +119,7 @@ The service requires the following configuration parameters: ``` -- **`resource_deletion_event_type`** *(string)*: The type to use for events with deletion instructions. +- **`resource_deletion_event_type`** *(string, required)*: The type to use for events with deletion instructions. Examples: @@ -129,7 +129,7 @@ The service requires the following configuration parameters: ``` -- **`resource_upsertion_event_type`** *(string)*: The type to use for events with upsert instructions. +- **`resource_upsertion_event_type`** *(string, required)*: The type to use for events with upsert instructions. Examples: @@ -139,7 +139,7 @@ The service requires the following configuration parameters: ``` -- **`kafka_servers`** *(array)*: A list of connection strings to connect to Kafka bootstrap servers. +- **`kafka_servers`** *(array, required)*: A list of connection strings to connect to Kafka bootstrap servers. - **Items** *(string)* @@ -193,7 +193,7 @@ The service requires the following configuration parameters: ``` -- **`db_connection_str`** *(string, format: password)*: MongoDB connection string. Might include credentials. For more information see: https://naiveskill.com/mongodb-connection-string/. +- **`db_connection_str`** *(string, format: password, required)*: MongoDB connection string. Might include credentials. For more information see: https://naiveskill.com/mongodb-connection-string/. Examples: @@ -203,7 +203,7 @@ The service requires the following configuration parameters: ``` -- **`db_name`** *(string)*: Name of the database located on the MongoDB server. +- **`db_name`** *(string, required)*: Name of the database located on the MongoDB server. Examples: diff --git a/lock/requirements-dev.txt b/lock/requirements-dev.txt index 89f35f1..04c1a9d 100644 --- a/lock/requirements-dev.txt +++ b/lock/requirements-dev.txt @@ -367,9 +367,9 @@ jsonschema-specifications==2023.12.1 \ --hash=sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc \ --hash=sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c # via jsonschema -jsonschema2md==1.1.0 \ - --hash=sha256:2386fc4d119330686db3989ea497ab96a4defb6388386fc0ceff756b5c1a66a7 \ - --hash=sha256:e89edf2de1bc7fc3e842915c7c29b7b70888555a87002eccc06350c0412a1458 +jsonschema2md==1.3.0 \ + --hash=sha256:5ee8f6674c9fec7303daa24c79023805caf2f2fefb99834813bd746227d146ea \ + --hash=sha256:ba089d46a3ac6f43b10caeaf8cd1b3978c7bd15c8e287de78d247666cb8857c0 # via -r lock/requirements-dev-template.in logot==1.3.0 \ --hash=sha256:bb2e8cf8ca949015e1e096e45023095ebd5df06ea4627f5df47d53dcdf62b74e \ @@ -1034,82 +1034,94 @@ virtualenv==20.26.3 \ --hash=sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a \ --hash=sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589 # via pre-commit -watchfiles==0.22.0 \ - --hash=sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b \ - --hash=sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31 \ - --hash=sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1 \ - --hash=sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab \ - --hash=sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249 \ - --hash=sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd \ - --hash=sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1 \ - --hash=sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6 \ - --hash=sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71 \ - --hash=sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13 \ - --hash=sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171 \ - --hash=sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1 \ - --hash=sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1 \ - --hash=sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c \ - --hash=sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971 \ - --hash=sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb \ - --hash=sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f \ - --hash=sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6 \ - --hash=sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27 \ - --hash=sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88 \ - --hash=sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843 \ - --hash=sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a \ - --hash=sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed \ - --hash=sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84 \ - --hash=sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0 \ - --hash=sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d \ - --hash=sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2 \ - --hash=sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797 \ - --hash=sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e \ - --hash=sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35 \ - --hash=sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6 \ - --hash=sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e \ - --hash=sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2 \ - --hash=sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550 \ - --hash=sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e \ - --hash=sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c \ - --hash=sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2 \ - --hash=sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc \ - --hash=sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6 \ - --hash=sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96 \ - --hash=sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93 \ - --hash=sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562 \ - --hash=sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795 \ - --hash=sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385 \ - --hash=sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f \ - --hash=sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848 \ - --hash=sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087 \ - --hash=sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec \ - --hash=sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb \ - --hash=sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232 \ - --hash=sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696 \ - --hash=sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2 \ - --hash=sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e \ - --hash=sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67 \ - --hash=sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e \ - --hash=sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68 \ - --hash=sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb \ - --hash=sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be \ - --hash=sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71 \ - --hash=sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c \ - --hash=sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da \ - --hash=sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39 \ - --hash=sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea \ - --hash=sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a \ - --hash=sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb \ - --hash=sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099 \ - --hash=sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a \ - --hash=sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538 \ - --hash=sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72 \ - --hash=sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1 \ - --hash=sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8 \ - --hash=sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d \ - --hash=sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d \ - --hash=sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c \ - --hash=sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86 +watchfiles==0.23.0 \ + --hash=sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d \ + --hash=sha256:02ff5d7bd066c6a7673b17c8879cd8ee903078d184802a7ee851449c43521bdd \ + --hash=sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0 \ + --hash=sha256:0eff099a4df36afaa0eea7a913aa64dcf2cbd4e7a4f319a73012210af4d23810 \ + --hash=sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef \ + --hash=sha256:11698bb2ea5e991d10f1f4f83a39a02f91e44e4bd05f01b5c1ec04c9342bf63c \ + --hash=sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4 \ + --hash=sha256:1733b9bc2c8098c6bdb0ff7a3d7cb211753fecb7bd99bdd6df995621ee1a574b \ + --hash=sha256:18e2de19801b0eaa4c5292a223effb7cfb43904cb742c5317a0ac686ed604765 \ + --hash=sha256:1cf7f486169986c4b9d34087f08ce56a35126600b6fef3028f19ca16d5889071 \ + --hash=sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b \ + --hash=sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5 \ + --hash=sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1 \ + --hash=sha256:1ebaebb53b34690da0936c256c1cdb0914f24fb0e03da76d185806df9328abed \ + --hash=sha256:20b423b58f5fdde704a226b598a2d78165fe29eb5621358fe57ea63f16f165c4 \ + --hash=sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220 \ + --hash=sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217 \ + --hash=sha256:2537ef60596511df79b91613a5bb499b63f46f01a11a81b0a2b0dedf645d0a9c \ + --hash=sha256:296e0b29ab0276ca59d82d2da22cbbdb39a23eed94cca69aed274595fb3dfe42 \ + --hash=sha256:2aec5c29915caf08771d2507da3ac08e8de24a50f746eb1ed295584ba1820330 \ + --hash=sha256:2dddc2487d33e92f8b6222b5fb74ae2cfde5e8e6c44e0248d24ec23befdc5366 \ + --hash=sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788 \ + --hash=sha256:3af1b05361e1cc497bf1be654a664750ae61f5739e4bb094a2be86ec8c6db9b6 \ + --hash=sha256:40cb8fa00028908211eb9f8d47744dca21a4be6766672e1ff3280bee320436f1 \ + --hash=sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e \ + --hash=sha256:486bda18be5d25ab5d932699ceed918f68eb91f45d018b0343e3502e52866e5e \ + --hash=sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0 \ + --hash=sha256:4ccd3011cc7ee2f789af9ebe04745436371d36afe610028921cab9f24bb2987b \ + --hash=sha256:4ea756e425ab2dfc8ef2a0cb87af8aa7ef7dfc6fc46c6f89bcf382121d4fff75 \ + --hash=sha256:524fcb8d59b0dbee2c9b32207084b67b2420f6431ed02c18bd191e6c575f5c48 \ + --hash=sha256:532e1f2c491274d1333a814e4c5c2e8b92345d41b12dc806cf07aaff786beb66 \ + --hash=sha256:556347b0abb4224c5ec688fc58214162e92a500323f50182f994f3ad33385dcb \ + --hash=sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8 \ + --hash=sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b \ + --hash=sha256:6a9265cf87a5b70147bfb2fec14770ed5b11a5bb83353f0eee1c25a81af5abfe \ + --hash=sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1 \ + --hash=sha256:6bb91fa4d0b392f0f7e27c40981e46dda9eb0fbc84162c7fb478fe115944f491 \ + --hash=sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8 \ + --hash=sha256:7ca6b71dcc50d320c88fb2d88ecd63924934a8abc1673683a242a7ca7d39e781 \ + --hash=sha256:7cf12ac34c444362f3261fb3ff548f0037ddd4c5bb85f66c4be30d2936beb3c5 \ + --hash=sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f \ + --hash=sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e \ + --hash=sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320 \ + --hash=sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8 \ + --hash=sha256:8ada449e22198c31fb013ae7e9add887e8d2bd2335401abd3cbc55f8c5083647 \ + --hash=sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c \ + --hash=sha256:8f48c917ffd36ff9a5212614c2d0d585fa8b064ca7e66206fb5c095015bc8207 \ + --hash=sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b \ + --hash=sha256:9837edf328b2805346f91209b7e660f65fb0e9ca18b7459d075d58db082bf981 \ + --hash=sha256:9d183e3888ada88185ab17064079c0db8c17e32023f5c278d7bf8014713b1b5b \ + --hash=sha256:9f02a259fcbbb5fcfe7a0805b1097ead5ba7a043e318eef1db59f93067f0b49b \ + --hash=sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1 \ + --hash=sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0 \ + --hash=sha256:a0b2c25040a3c0ce0e66c7779cc045fdfbbb8d59e5aabfe033000b42fe44b53e \ + --hash=sha256:a753993635eccf1ecb185dedcc69d220dab41804272f45e4aef0a67e790c3eb3 \ + --hash=sha256:a8323daae27ea290ba3350c70c836c0d2b0fb47897fa3b0ca6a5375b952b90d3 \ + --hash=sha256:a8f195338a5a7b50a058522b39517c50238358d9ad8284fd92943643144c0c03 \ + --hash=sha256:a96ac14e184aa86dc43b8a22bb53854760a58b2966c2b41580de938e9bf26ed0 \ + --hash=sha256:aafea64a3ae698695975251f4254df2225e2624185a69534e7fe70581066bc1b \ + --hash=sha256:aba037c1310dd108411d27b3d5815998ef0e83573e47d4219f45753c710f969f \ + --hash=sha256:b1f67312efa3902a8e8496bfa9824d3bec096ff83c4669ea555c6bdd213aa516 \ + --hash=sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3 \ + --hash=sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1 \ + --hash=sha256:b98732ec893975455708d6fc9a6daab527fc8bbe65be354a3861f8c450a632a4 \ + --hash=sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9 \ + --hash=sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581 \ + --hash=sha256:bee8ce357a05c20db04f46c22be2d1a2c6a8ed365b325d08af94358e0688eeb4 \ + --hash=sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf \ + --hash=sha256:c846884b2e690ba62a51048a097acb6b5cd263d8bd91062cd6137e2880578472 \ + --hash=sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a \ + --hash=sha256:d2d42254b189a346249424fb9bb39182a19289a2409051ee432fb2926bad966a \ + --hash=sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb \ + --hash=sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77 \ + --hash=sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f \ + --hash=sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab \ + --hash=sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304 \ + --hash=sha256:e75695cc952e825fa3e0684a7f4a302f9128721f13eedd8dbd3af2ba450932b8 \ + --hash=sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75 \ + --hash=sha256:ecf2be4b9eece4f3da8ba5f244b9e51932ebc441c0867bd6af46a3d97eb068d6 \ + --hash=sha256:ee1f5fcbf5bc33acc0be9dd31130bcba35d6d2302e4eceafafd7d9018c7755ab \ + --hash=sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1 \ + --hash=sha256:efadd40fca3a04063d40c4448c9303ce24dd6151dc162cfae4a2a060232ebdcb \ + --hash=sha256:f18de0f82c62c4197bea5ecf4389288ac755896aac734bd2cc44004c56e4ac47 \ + --hash=sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff \ + --hash=sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b \ + --hash=sha256:fb02d41c33be667e6135e6686f1bb76104c88a312a18faa0ef0262b5bf7f1a0f \ + --hash=sha256:fd257f98cff9c6cb39eee1a83c7c3183970d8a8d23e8cf4f47d9a21329285cee # via uvicorn websockets==12.0 \ --hash=sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b \ diff --git a/lock/requirements.txt b/lock/requirements.txt index b5ec895..190b3de 100644 --- a/lock/requirements.txt +++ b/lock/requirements.txt @@ -733,82 +733,94 @@ uvloop==0.19.0 \ # via # -c lock/requirements-dev.txt # uvicorn -watchfiles==0.22.0 \ - --hash=sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b \ - --hash=sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31 \ - --hash=sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1 \ - --hash=sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab \ - --hash=sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249 \ - --hash=sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd \ - --hash=sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1 \ - --hash=sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6 \ - --hash=sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71 \ - --hash=sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13 \ - --hash=sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171 \ - --hash=sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1 \ - --hash=sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1 \ - --hash=sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c \ - --hash=sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971 \ - --hash=sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb \ - --hash=sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f \ - --hash=sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6 \ - --hash=sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27 \ - --hash=sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88 \ - --hash=sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843 \ - --hash=sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a \ - --hash=sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed \ - --hash=sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84 \ - --hash=sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0 \ - --hash=sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d \ - --hash=sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2 \ - --hash=sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797 \ - --hash=sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e \ - --hash=sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35 \ - --hash=sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6 \ - --hash=sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e \ - --hash=sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2 \ - --hash=sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550 \ - --hash=sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e \ - --hash=sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c \ - --hash=sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2 \ - --hash=sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc \ - --hash=sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6 \ - --hash=sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96 \ - --hash=sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93 \ - --hash=sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562 \ - --hash=sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795 \ - --hash=sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385 \ - --hash=sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f \ - --hash=sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848 \ - --hash=sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087 \ - --hash=sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec \ - --hash=sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb \ - --hash=sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232 \ - --hash=sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696 \ - --hash=sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2 \ - --hash=sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e \ - --hash=sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67 \ - --hash=sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e \ - --hash=sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68 \ - --hash=sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb \ - --hash=sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be \ - --hash=sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71 \ - --hash=sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c \ - --hash=sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da \ - --hash=sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39 \ - --hash=sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea \ - --hash=sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a \ - --hash=sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb \ - --hash=sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099 \ - --hash=sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a \ - --hash=sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538 \ - --hash=sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72 \ - --hash=sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1 \ - --hash=sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8 \ - --hash=sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d \ - --hash=sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d \ - --hash=sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c \ - --hash=sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86 +watchfiles==0.23.0 \ + --hash=sha256:02b7ba9d4557149410747353e7325010d48edcfe9d609a85cb450f17fd50dc3d \ + --hash=sha256:02ff5d7bd066c6a7673b17c8879cd8ee903078d184802a7ee851449c43521bdd \ + --hash=sha256:0e01bcb8d767c58865207a6c2f2792ad763a0fe1119fb0a430f444f5b02a5ea0 \ + --hash=sha256:0eff099a4df36afaa0eea7a913aa64dcf2cbd4e7a4f319a73012210af4d23810 \ + --hash=sha256:109a61763e7318d9f821b878589e71229f97366fa6a5c7720687d367f3ab9eef \ + --hash=sha256:11698bb2ea5e991d10f1f4f83a39a02f91e44e4bd05f01b5c1ec04c9342bf63c \ + --hash=sha256:130a896d53b48a1cecccfa903f37a1d87dbb74295305f865a3e816452f6e49e4 \ + --hash=sha256:1733b9bc2c8098c6bdb0ff7a3d7cb211753fecb7bd99bdd6df995621ee1a574b \ + --hash=sha256:18e2de19801b0eaa4c5292a223effb7cfb43904cb742c5317a0ac686ed604765 \ + --hash=sha256:1cf7f486169986c4b9d34087f08ce56a35126600b6fef3028f19ca16d5889071 \ + --hash=sha256:1d636c8aeb28cdd04a4aa89030c4b48f8b2954d8483e5f989774fa441c0ed57b \ + --hash=sha256:1db691bad0243aed27c8354b12d60e8e266b75216ae99d33e927ff5238d270b5 \ + --hash=sha256:1e5f3ca0ff47940ce0a389457b35d6df601c317c1e1a9615981c474452f98de1 \ + --hash=sha256:1ebaebb53b34690da0936c256c1cdb0914f24fb0e03da76d185806df9328abed \ + --hash=sha256:20b423b58f5fdde704a226b598a2d78165fe29eb5621358fe57ea63f16f165c4 \ + --hash=sha256:2368c5371c17fdcb5a2ea71c5c9d49f9b128821bfee69503cc38eae00feb3220 \ + --hash=sha256:24655e8c1c9c114005c3868a3d432c8aa595a786b8493500071e6a52f3d09217 \ + --hash=sha256:2537ef60596511df79b91613a5bb499b63f46f01a11a81b0a2b0dedf645d0a9c \ + --hash=sha256:296e0b29ab0276ca59d82d2da22cbbdb39a23eed94cca69aed274595fb3dfe42 \ + --hash=sha256:2aec5c29915caf08771d2507da3ac08e8de24a50f746eb1ed295584ba1820330 \ + --hash=sha256:2dddc2487d33e92f8b6222b5fb74ae2cfde5e8e6c44e0248d24ec23befdc5366 \ + --hash=sha256:37fd826dac84c6441615aa3f04077adcc5cac7194a021c9f0d69af20fb9fa788 \ + --hash=sha256:3af1b05361e1cc497bf1be654a664750ae61f5739e4bb094a2be86ec8c6db9b6 \ + --hash=sha256:40cb8fa00028908211eb9f8d47744dca21a4be6766672e1ff3280bee320436f1 \ + --hash=sha256:46f1d8069a95885ca529645cdbb05aea5837d799965676e1b2b1f95a4206313e \ + --hash=sha256:486bda18be5d25ab5d932699ceed918f68eb91f45d018b0343e3502e52866e5e \ + --hash=sha256:48a1b05c0afb2cd2f48c1ed2ae5487b116e34b93b13074ed3c22ad5c743109f0 \ + --hash=sha256:4ccd3011cc7ee2f789af9ebe04745436371d36afe610028921cab9f24bb2987b \ + --hash=sha256:4ea756e425ab2dfc8ef2a0cb87af8aa7ef7dfc6fc46c6f89bcf382121d4fff75 \ + --hash=sha256:524fcb8d59b0dbee2c9b32207084b67b2420f6431ed02c18bd191e6c575f5c48 \ + --hash=sha256:532e1f2c491274d1333a814e4c5c2e8b92345d41b12dc806cf07aaff786beb66 \ + --hash=sha256:556347b0abb4224c5ec688fc58214162e92a500323f50182f994f3ad33385dcb \ + --hash=sha256:62d2b18cb1edaba311fbbfe83fb5e53a858ba37cacb01e69bc20553bb70911b8 \ + --hash=sha256:6991e3a78f642368b8b1b669327eb6751439f9f7eaaa625fae67dd6070ecfa0b \ + --hash=sha256:6a9265cf87a5b70147bfb2fec14770ed5b11a5bb83353f0eee1c25a81af5abfe \ + --hash=sha256:6b1a950ab299a4a78fd6369a97b8763732bfb154fdb433356ec55a5bce9515c1 \ + --hash=sha256:6bb91fa4d0b392f0f7e27c40981e46dda9eb0fbc84162c7fb478fe115944f491 \ + --hash=sha256:6c21a5467f35c61eafb4e394303720893066897fca937bade5b4f5877d350ff8 \ + --hash=sha256:7ca6b71dcc50d320c88fb2d88ecd63924934a8abc1673683a242a7ca7d39e781 \ + --hash=sha256:7cf12ac34c444362f3261fb3ff548f0037ddd4c5bb85f66c4be30d2936beb3c5 \ + --hash=sha256:7f7252f52a09f8fa5435dc82b6af79483118ce6bd51eb74e6269f05ee22a7b9f \ + --hash=sha256:85042ab91814fca99cec4678fc063fb46df4cbb57b4835a1cc2cb7a51e10250e \ + --hash=sha256:857af85d445b9ba9178db95658c219dbd77b71b8264e66836a6eba4fbf49c320 \ + --hash=sha256:87f889f6e58849ddb7c5d2cb19e2e074917ed1c6e3ceca50405775166492cca8 \ + --hash=sha256:8ada449e22198c31fb013ae7e9add887e8d2bd2335401abd3cbc55f8c5083647 \ + --hash=sha256:8e56fbcdd27fce061854ddec99e015dd779cae186eb36b14471fc9ae713b118c \ + --hash=sha256:8f48c917ffd36ff9a5212614c2d0d585fa8b064ca7e66206fb5c095015bc8207 \ + --hash=sha256:9338ade39ff24f8086bb005d16c29f8e9f19e55b18dcb04dfa26fcbc09da497b \ + --hash=sha256:9837edf328b2805346f91209b7e660f65fb0e9ca18b7459d075d58db082bf981 \ + --hash=sha256:9d183e3888ada88185ab17064079c0db8c17e32023f5c278d7bf8014713b1b5b \ + --hash=sha256:9f02a259fcbbb5fcfe7a0805b1097ead5ba7a043e318eef1db59f93067f0b49b \ + --hash=sha256:9f8e6bb5ac007d4a4027b25f09827ed78cbbd5b9700fd6c54429278dacce05d1 \ + --hash=sha256:9ff785af8bacdf0be863ec0c428e3288b817e82f3d0c1d652cd9c6d509020dd0 \ + --hash=sha256:a0b2c25040a3c0ce0e66c7779cc045fdfbbb8d59e5aabfe033000b42fe44b53e \ + --hash=sha256:a753993635eccf1ecb185dedcc69d220dab41804272f45e4aef0a67e790c3eb3 \ + --hash=sha256:a8323daae27ea290ba3350c70c836c0d2b0fb47897fa3b0ca6a5375b952b90d3 \ + --hash=sha256:a8f195338a5a7b50a058522b39517c50238358d9ad8284fd92943643144c0c03 \ + --hash=sha256:a96ac14e184aa86dc43b8a22bb53854760a58b2966c2b41580de938e9bf26ed0 \ + --hash=sha256:aafea64a3ae698695975251f4254df2225e2624185a69534e7fe70581066bc1b \ + --hash=sha256:aba037c1310dd108411d27b3d5815998ef0e83573e47d4219f45753c710f969f \ + --hash=sha256:b1f67312efa3902a8e8496bfa9824d3bec096ff83c4669ea555c6bdd213aa516 \ + --hash=sha256:b4ac73b02ca1824ec0a7351588241fd3953748d3774694aa7ddb5e8e46aef3e3 \ + --hash=sha256:b8d3c5cd327dd6ce0edfc94374fb5883d254fe78a5e9d9dfc237a1897dc73cd1 \ + --hash=sha256:b98732ec893975455708d6fc9a6daab527fc8bbe65be354a3861f8c450a632a4 \ + --hash=sha256:ba31c32f6b4dceeb2be04f717811565159617e28d61a60bb616b6442027fd4b9 \ + --hash=sha256:bd3e2d64500a6cad28bcd710ee6269fbeb2e5320525acd0cfab5f269ade68581 \ + --hash=sha256:bee8ce357a05c20db04f46c22be2d1a2c6a8ed365b325d08af94358e0688eeb4 \ + --hash=sha256:c5e7803a65eb2d563c73230e9d693c6539e3c975ccfe62526cadde69f3fda0cf \ + --hash=sha256:c846884b2e690ba62a51048a097acb6b5cd263d8bd91062cd6137e2880578472 \ + --hash=sha256:d1aa4cc85202956d1a65c88d18c7b687b8319dbe6b1aec8969784ef7a10e7d1a \ + --hash=sha256:d2d42254b189a346249424fb9bb39182a19289a2409051ee432fb2926bad966a \ + --hash=sha256:dccc858372a56080332ea89b78cfb18efb945da858fabeb67f5a44fa0bcb4ebb \ + --hash=sha256:dd41d5c72417b87c00b1b635738f3c283e737d75c5fa5c3e1c60cd03eac3af77 \ + --hash=sha256:e087e8fdf1270d000913c12e6eca44edd02aad3559b3e6b8ef00f0ce76e0636f \ + --hash=sha256:e397b64f7aaf26915bf2ad0f1190f75c855d11eb111cc00f12f97430153c2eab \ + --hash=sha256:e495ed2a7943503766c5d1ff05ae9212dc2ce1c0e30a80d4f0d84889298fa304 \ + --hash=sha256:e75695cc952e825fa3e0684a7f4a302f9128721f13eedd8dbd3af2ba450932b8 \ + --hash=sha256:eb99c954291b2fad0eff98b490aa641e128fbc4a03b11c8a0086de8b7077fb75 \ + --hash=sha256:ecf2be4b9eece4f3da8ba5f244b9e51932ebc441c0867bd6af46a3d97eb068d6 \ + --hash=sha256:ee1f5fcbf5bc33acc0be9dd31130bcba35d6d2302e4eceafafd7d9018c7755ab \ + --hash=sha256:ee7db6e36e7a2c15923072e41ea24d9a0cf39658cb0637ecc9307b09d28827e1 \ + --hash=sha256:efadd40fca3a04063d40c4448c9303ce24dd6151dc162cfae4a2a060232ebdcb \ + --hash=sha256:f18de0f82c62c4197bea5ecf4389288ac755896aac734bd2cc44004c56e4ac47 \ + --hash=sha256:f449afbb971df5c6faeb0a27bca0427d7b600dd8f4a068492faec18023f0dcff \ + --hash=sha256:f46c6f0aec8d02a52d97a583782d9af38c19a29900747eb048af358a9c1d8e5b \ + --hash=sha256:fb02d41c33be667e6135e6686f1bb76104c88a312a18faa0ef0262b5bf7f1a0f \ + --hash=sha256:fd257f98cff9c6cb39eee1a83c7c3183970d8a8d23e8cf4f47d9a21329285cee # via # -c lock/requirements-dev.txt # uvicorn diff --git a/openapi.yaml b/openapi.yaml index e755577..0384cde 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -182,7 +182,7 @@ info: name: Apache 2.0 summary: A service for searching metadata artifacts and filtering results. title: Metadata Artifact Search Service - version: 3.0.1 + version: 3.0.2 openapi: 3.1.0 paths: /health: diff --git a/pyproject.toml b/pyproject.toml index 91bcdc1..ed23c39 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ classifiers = [ "Intended Audience :: Developers", ] name = "mass" -version = "3.0.1" +version = "3.0.2" description = "Metadata Artifact Search Service - A service for searching metadata artifacts and filtering results." dependencies = [ "typer>=0.12", diff --git a/src/mass/adapters/outbound/utils.py b/src/mass/adapters/outbound/utils.py index 8c24b29..e14a284 100644 --- a/src/mass/adapters/outbound/utils.py +++ b/src/mass/adapters/outbound/utils.py @@ -41,18 +41,6 @@ def pipeline_match_text_search(*, query: str) -> JsonObject: return {"$match": text_search} -def args_for_getfield(*, root_object_name: str, field_name: str) -> tuple[str, str]: - """Fieldpath names can't have '.', so specify any nested fields with $getField""" - prefix = f"${root_object_name}" - specified_field = field_name - if "." in field_name: - pieces = field_name.split(".") - specified_field = pieces[-1] - prefix += "." + ".".join(pieces[:-1]) - - return prefix, specified_field - - def pipeline_match_filters_stage(*, filters: list[models.Filter]) -> JsonObject: """Build segment of pipeline to apply search filters""" filter_values = defaultdict(list) @@ -95,35 +83,45 @@ def pipeline_facet_sort_and_paginate( segment: dict[str, list[JsonObject]] = {} for facet in facet_fields: - prefix, specified_field = args_for_getfield( - root_object_name="content", field_name=facet.key - ) name = facet.name if not name: name = name_from_key(facet.key) - segment[name] = [ - { - "$unwind": { - "path": prefix, - "preserveNullAndEmptyArrays": True, - } - }, + pipeline: list[JsonObject] = [ { "$unwind": { - "path": f"{prefix}.{specified_field}", + "path": "$content", "preserveNullAndEmptyArrays": True, } }, - { - "$group": { - "_id": {"$getField": {"field": specified_field, "input": prefix}}, - "count": {"$sum": 1}, - } - }, - {"$addFields": {"value": "$_id"}}, # rename "_id" to "value" on each option - {"$unset": "_id"}, - {"$sort": {"value": 1}}, ] + path = "$content" + for field in facet.key.split("."): + path += f".{field}" + pipeline.append( + { + "$unwind": { + "path": path, + "preserveNullAndEmptyArrays": True, + } + }, + ) + path, field = path.rsplit(".", 1) + pipeline.extend( + ( + { + "$group": { + "_id": {"$getField": {"field": field, "input": path}}, + "count": {"$sum": 1}, + } + }, + { + "$addFields": {"value": "$_id"} + }, # rename "_id" to "value" on each option + {"$unset": "_id"}, + {"$sort": {"value": 1}}, + ) + ) + segment[name] = pipeline # this is the total number of hits, but pagination can mean only a few are returned segment["count"] = [{"$count": "total"}] diff --git a/tests/fixtures/joint.py b/tests/fixtures/joint.py index d406160..c2dc209 100644 --- a/tests/fixtures/joint.py +++ b/tests/fixtures/joint.py @@ -24,9 +24,10 @@ import pytest_asyncio from ghga_service_commons.api.testing import AsyncTestClient +from hexkit.custom_types import Ascii, JsonObject from hexkit.providers.akafka import KafkaEventSubscriber from hexkit.providers.akafka.testutils import KafkaFixture -from hexkit.providers.mongodb.testutils import MongoDbFixture +from hexkit.providers.mongodb.testutils import MongoClient, MongoDbFixture from mass.config import Config from mass.core import models @@ -42,35 +43,48 @@ class State: """The current state of the database and the event topics""" + # the database is considered dirty if it is not (yet) populated properly database_dirty: bool + # the events are considered dirty if some have been published already events_dirty: bool resources: dict[str, list[models.Resource]] -state = State(database_dirty=True, events_dirty=True, resources={}) +state = State(database_dirty=True, events_dirty=False, resources={}) @dataclass class JointFixture: """A fixture embedding all other fixtures.""" + # attributes that can be used freely in tests + config: Config - query_handler: QueryHandlerPort - mongodb: MongoDbFixture - kafka: KafkaFixture - rest_client: AsyncTestClient - event_subscriber: KafkaEventSubscriber resources: dict[str, list[models.Resource]] + rest_client: AsyncTestClient + + # attributes that should not be accessed by tests directly + + _mongodb: MongoDbFixture + _kafka: KafkaFixture + _query_handler: QueryHandlerPort + _event_subscriber: KafkaEventSubscriber - def empty_database(self) -> None: + # convenience methods that can be accessed by tests directly + + def purge_database(self) -> None: """Empty the database.""" - self.mongodb.empty_collections() + self._mongodb.empty_collections() state.database_dirty = True + async def purge_events(self) -> None: + """Purge all events.""" + await self._kafka.clear_topics() + async def load_test_data(self) -> None: """Populate a collection for each file in test_data.""" filename_pattern = re.compile(r"/(\w+)\.json") - self.query_handler._dao_collection._indexes_created = False # type: ignore + self._query_handler._dao_collection._indexes_created = False # type: ignore for filename in glob.glob("tests/fixtures/test_data/*.json"): match_obj = re.search(filename_pattern, filename) if match_obj: @@ -80,17 +94,17 @@ async def load_test_data(self) -> None: resources = get_resources_from_file(filename) state.resources[collection_name] = resources for resource in resources: - await self.query_handler.load_resource( + await self._query_handler.load_resource( resource=resource, class_name=collection_name ) async def reset_state(self) -> None: """Reset the state of the database and event topics if needed.""" if state.events_dirty: - await self.kafka.clear_topics() + await self.purge_events() state.events_dirty = False if state.database_dirty: - self.mongodb.empty_collections() + self.purge_database() await self.load_test_data() state.database_dirty = False @@ -103,18 +117,63 @@ async def call_search_endpoint(self, params: QueryParams) -> models.QueryResults response.raise_for_status() return models.QueryResults(**result) + @property + def mongodb_client(self) -> MongoClient: + """Get a MongoDB client and mark the database state as dirty.""" + state.database_dirty = True + return self._mongodb.client + + def recreate_mongodb_indexes(self) -> None: + """Set flag to recreate MongoDB indexes and mark the database state as dirty.""" + self._query_handler._dao_collection._indexes_created = False # type: ignore + state.database_dirty = True + + async def handle_query( + self, + class_name: str, + query: str, + filters: list[models.Filter], + skip: int = 0, + limit: int | None = None, + sorting_parameters: list[models.SortingParameter] | None = None, + ) -> models.QueryResults: + """Handle a query.""" + return await self._query_handler.handle_query( + class_name=class_name, + query=query, + filters=filters, + skip=skip, + limit=limit, + sorting_parameters=sorting_parameters, + ) + async def delete_resource(self, resource_id: str, class_name: str) -> None: - """Delete a resource and mark the database as dirty.""" - await self.query_handler.delete_resource( + """Delete a resource and mark the database state as dirty.""" + await self._query_handler.delete_resource( resource_id=resource_id, class_name=class_name ) state.database_dirty = True async def load_resource(self, resource: models.Resource, class_name: str) -> None: - """Load a resource and mark the database as dirty.""" - await self.query_handler.load_resource(resource=resource, class_name=class_name) + """Load a resource and mark the database state as dirty.""" + await self._query_handler.load_resource( + resource=resource, class_name=class_name + ) state.database_dirty = True + async def consume_event(self) -> None: + """Run the event subscriber in order to to consume an event.""" + await self._event_subscriber.run(forever=False) + + async def publish_event( + self, payload: JsonObject, type_: Ascii, topic: Ascii, key: Ascii = "test" + ) -> None: + """Publish a test event and mark the events state as dirty.""" + await self._kafka.publish_event( + payload=payload, type_=type_, topic=topic, key=key + ) + state.events_dirty = True + @pytest_asyncio.fixture() async def joint_fixture( @@ -134,10 +193,10 @@ async def joint_fixture( ): joint_fixture = JointFixture( config=config, - query_handler=query_handler, - event_subscriber=event_subscriber, - kafka=kafka, - mongodb=mongodb, + _query_handler=query_handler, + _event_subscriber=event_subscriber, + _kafka=kafka, + _mongodb=mongodb, rest_client=rest_client, resources=state.resources, ) diff --git a/tests/fixtures/test_config.yaml b/tests/fixtures/test_config.yaml index 700fc91..987edd7 100644 --- a/tests/fixtures/test_config.yaml +++ b/tests/fixtures/test_config.yaml @@ -61,6 +61,8 @@ searchable_classes: name: Food - key: friends.name name: Friend + - key: special.features.fur.color + name: Fur color selected_fields: - key: name resource_change_event_topic: searchable_resources diff --git a/tests/fixtures/test_data/FilteringTests.json b/tests/fixtures/test_data/FilteringTests.json index 480d979..3ccd750 100644 --- a/tests/fixtures/test_data/FilteringTests.json +++ b/tests/fixtures/test_data/FilteringTests.json @@ -14,6 +14,15 @@ ], "id_": "1", "name": "Jack", + "special": { + "features": [ + { + "fur": { + "color": "brown" + } + } + ] + }, "species": "monkey" }, { @@ -34,6 +43,15 @@ ], "id_": "2", "name": "Bruiser", + "special": { + "features": [ + { + "fur": { + "color": "light brown" + } + } + ] + }, "species": "dog" }, { @@ -54,6 +72,18 @@ ], "id_": "3", "name": "Lady", + "special": { + "features": [ + { + "fur": { + "color": [ + "cream", + "brown" + ] + } + } + ] + }, "species": "dog" }, { @@ -80,6 +110,18 @@ ], "id_": "4", "name": "Garfield", + "special": { + "features": [ + { + "fur": { + "color": [ + "orange", + "black" + ] + } + } + ] + }, "species": "cat" }, { @@ -103,6 +145,15 @@ ], "id_": "5", "name": "Flipper", + "special": { + "features": [ + { + "fur": { + "color": "gray" + } + } + ] + }, "species": "dolphin" } ] diff --git a/tests/test_api.py b/tests/test_api.py index 530eb48..5b7dc44 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -76,7 +76,7 @@ async def test_malformed_document( joint_fixture: JointFixture, caplog: pytest.LogCaptureFixture ): """Test behavior from API perspective upon querying when bad doc exists""" - joint_fixture.empty_database() + joint_fixture.purge_database() # define and load a new resource without all the required facets resource = models.Resource( diff --git a/tests/test_consumer.py b/tests/test_consumer.py index 2ebed8c..3a11edc 100644 --- a/tests/test_consumer.py +++ b/tests/test_consumer.py @@ -38,7 +38,7 @@ async def test_resource_upsert( ): """Try upserting with no pre-existing resource with matching ID (i.e. insert)""" # get all the documents in the collection - results_all = await joint_fixture.query_handler.handle_query( + results_all = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[] ) assert results_all.count > 0 @@ -61,7 +61,7 @@ async def test_resource_upsert( ).model_dump() # publish the event - await joint_fixture.kafka.publish_event( + await joint_fixture.publish_event( payload=payload, type_=joint_fixture.config.resource_upsertion_event_type, topic=joint_fixture.config.resource_change_event_topic, @@ -69,10 +69,10 @@ async def test_resource_upsert( ) # consume the event - await joint_fixture.event_subscriber.run(forever=False) + await joint_fixture.consume_event() # verify that the resource was added - updated_resources = await joint_fixture.query_handler.handle_query( + updated_resources = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[] ) if is_insert: @@ -93,7 +93,7 @@ async def test_resource_upsert( async def test_resource_delete(joint_fixture: JointFixture): """Test resource deletion via event consumption""" # get all the documents in the collection - targeted_initial_results = await joint_fixture.query_handler.handle_query( + targeted_initial_results = await joint_fixture.handle_query( class_name=CLASS_NAME, query='"1HotelAlpha-id"', filters=[], @@ -105,7 +105,7 @@ async def test_resource_delete(joint_fixture: JointFixture): accession="1HotelAlpha-id", class_name=CLASS_NAME ) - await joint_fixture.kafka.publish_event( + await joint_fixture.publish_event( payload=resource_info.model_dump(), type_=joint_fixture.config.resource_deletion_event_type, topic=joint_fixture.config.resource_change_event_topic, @@ -113,10 +113,10 @@ async def test_resource_delete(joint_fixture: JointFixture): ) # consume the event - await joint_fixture.event_subscriber.run(forever=False) + await joint_fixture.consume_event() # get all the documents in the collection - results_post_delete = await joint_fixture.query_handler.handle_query( + results_post_delete = await joint_fixture.handle_query( class_name=CLASS_NAME, query='"1HotelAlpha-id"', filters=[] ) diff --git a/tests/test_filtering.py b/tests/test_filtering.py index 399fa29..e4616a0 100644 --- a/tests/test_filtering.py +++ b/tests/test_filtering.py @@ -31,7 +31,7 @@ async def test_facets(joint_fixture: JointFixture): results = await joint_fixture.call_search_endpoint(params) facets = results.facets - assert len(facets) == 3 + assert len(facets) == 4 facet = facets[0] assert facet.key == "species" @@ -77,11 +77,25 @@ async def test_facets(joint_fixture: JointFixture): } assert list(options) == sorted(options) + facet = facets[3] + assert facet.key == "special.features.fur.color" + assert facet.name == "Fur color" + options = {option.value: option.count for option in facet.options} + assert options == { + "black": 1, + "brown": 2, + "cream": 1, + "gray": 1, + "light brown": 1, + "orange": 1, + } + assert list(options) == sorted(options) + @pytest.mark.parametrize( "species,names", [("mouse", []), ("cat", ["Garfield"]), ("dog", ["Bruiser", "Lady"])], - ids=[0, 1, 2], + ids=range(1, 4), ) async def test_single_valued_with_with_single_filter( species: str, names: list[str], joint_fixture: JointFixture @@ -101,7 +115,7 @@ async def test_single_valued_with_with_single_filter( # Check that the facet only contains the filtered values facets = results.facets - assert len(facets) == 3 + assert len(facets) == 4 facet = facets[0] assert facet.key == "species" assert facet.name == "Species" @@ -118,7 +132,7 @@ async def test_single_valued_with_with_single_filter( @pytest.mark.parametrize( "food,names", [("broccoli", []), ("bananas", ["Jack"]), ("fish", ["Garfield", "Flipper"])], - ids=[0, 1, 2], + ids=range(1, 4), ) async def test_multi_valued_with_with_single_filter( food: str, names: list[str], joint_fixture: JointFixture @@ -138,7 +152,7 @@ async def test_multi_valued_with_with_single_filter( # Check that the facet only contains the filtered values facets = results.facets - assert len(facets) == 3 + assert len(facets) == 4 facet = facets[1] assert facet.key == "eats" assert facet.name == "Food" @@ -188,7 +202,7 @@ async def test_multiple_filters(joint_fixture: JointFixture): ("Jon", ["Garfield", "Flipper"]), ("Jack", ["Jack", "Bruiser", "Garfield"]), ], - ids=[0, 1, 2, 3], + ids=range(1, 5), ) async def test_filter_for_common_friend( friend: str, names: list[str], joint_fixture: JointFixture @@ -208,7 +222,7 @@ async def test_filter_for_common_friend( # Check that the facet contains the friend in question facets = results.facets - assert len(facets) == 3 + assert len(facets) == 4 facet = facets[2] assert facet.key == "friends.name" assert facet.name == "Friend" @@ -217,7 +231,7 @@ async def test_filter_for_common_friend( async def test_filter_for_couple_of_friends(joint_fixture: JointFixture): - """Test that we can search for animals who have one of the specified friends""" + """Test that we can search for animals with one of the specified friends""" params: QueryParams = { "class_name": CLASS_NAME, "filter_by": ["friends.name"] * 3, @@ -232,7 +246,7 @@ async def test_filter_for_couple_of_friends(joint_fixture: JointFixture): # Check that the facet contains the selected friends and all their co-friends facets = results.facets - assert len(facets) == 3 + assert len(facets) == 4 facet = facets[2] assert facet.key == "friends.name" assert facet.name == "Friend" @@ -240,3 +254,38 @@ async def test_filter_for_couple_of_friends(joint_fixture: JointFixture): co_friends = "Buddy Hector Jack Jog Jon Peter Sandy Tramp Trusty".split() assert option_values == co_friends assert all(option.count == 1 for option in facet.options) + + +@pytest.mark.parametrize( + "color,names", + [ + ("green", []), + ("orange", ["Garfield"]), + ("brown", ["Jack", "Lady"]), + ], + ids=range(1, 4), +) +async def test_filter_for_fur_color( + color: str, names: list[str], joint_fixture: JointFixture +): + """Test that we can search for animals with a given fur color""" + params: QueryParams = { + "class_name": CLASS_NAME, + "filter_by": "special.features.fur.color", + "value": color, + } + + results = await joint_fixture.call_search_endpoint(params) + + # Check that the expected names are returned + returned_names = [resource.content["name"] for resource in results.hits] + assert returned_names == names + + # Check that the facet contains the color in question + facets = results.facets + assert len(facets) == 4 + facet = facets[3] + assert facet.key == "special.features.fur.color" + assert facet.name == "Fur color" + if names: + assert any(option.value == color for option in facet.options) diff --git a/tests/test_index_creation.py b/tests/test_index_creation.py index 010b9c0..62118b7 100644 --- a/tests/test_index_creation.py +++ b/tests/test_index_creation.py @@ -35,17 +35,17 @@ async def test_index_creation(joint_fixture: JointFixture, create_index_manually: bool): """Test the index creation function.""" # indexes have been created in fixture setup, so delete them again - joint_fixture.empty_database() + joint_fixture.purge_database() # verify collection does not exist - database = joint_fixture.mongodb.client[joint_fixture.config.db_name] + database = joint_fixture.mongodb_client[joint_fixture.config.db_name] assert CLASS_NAME not in database.list_collection_names() - # reset the flag so it actually runs the indexing function - joint_fixture.query_handler._dao_collection._indexes_created = False # type: ignore + # let the query handler know that it needs to run the indexing function + joint_fixture.recreate_mongodb_indexes() # make sure we do not get an error when trying to query non-existent collection - results_without_coll = await joint_fixture.query_handler.handle_query( + results_without_coll = await joint_fixture.handle_query( class_name=CLASS_NAME, query=QUERY_STRING, filters=[], @@ -54,7 +54,7 @@ async def test_index_creation(joint_fixture: JointFixture, create_index_manually assert results_without_coll == models.QueryResults() # create collection without index - joint_fixture.mongodb.client[joint_fixture.config.db_name].create_collection( + joint_fixture.mongodb_client[joint_fixture.config.db_name].create_collection( CLASS_NAME ) @@ -69,7 +69,7 @@ async def test_index_creation(joint_fixture: JointFixture, create_index_manually ) # Verify querying empty collection with query string gives empty results model - results_without_coll = await joint_fixture.query_handler.handle_query( + results_without_coll = await joint_fixture.handle_query( class_name=CLASS_NAME, query=QUERY_STRING, filters=[], @@ -90,7 +90,7 @@ async def test_index_creation(joint_fixture: JointFixture, create_index_manually assert any(index["name"] == f"$**_{TEXT}" for index in collection.list_indexes()) # verify that supplying a query string doesn't result in an error - results_with_coll = await joint_fixture.query_handler.handle_query( + results_with_coll = await joint_fixture.handle_query( class_name=CLASS_NAME, query=QUERY_STRING, filters=[], diff --git a/tests/test_logging.py b/tests/test_logging.py index 4e34fa8..541a885 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -110,7 +110,7 @@ async def test_event_sub_logging( Constants are defined above in an effort to keep code redundancy down. """ # get all the documents in the collection - all_results = await joint_fixture.query_handler.handle_query( + all_results = await joint_fixture.handle_query( class_name="NestedData", query="", filters=[], @@ -124,7 +124,7 @@ async def test_event_sub_logging( else joint_fixture.config.resource_deletion_event_type ) - await joint_fixture.kafka.publish_event( + await joint_fixture.publish_event( payload=resource.model_dump(), type_=event_to_use, topic=joint_fixture.config.resource_change_event_topic, @@ -135,7 +135,7 @@ async def test_event_sub_logging( caplog.clear() # consume the event - await joint_fixture.event_subscriber.run(forever=False) + await joint_fixture.consume_event() # examine logs and try to be specific by filtering by logger name logs_of_interest = [ diff --git a/tests/test_relevance.py b/tests/test_relevance.py index 217ee6d..888fb9e 100644 --- a/tests/test_relevance.py +++ b/tests/test_relevance.py @@ -83,7 +83,7 @@ def sorted_reference_results( if not sorts: sorts = [RELEVANCE_SORT, ID_ASC] - results = joint_fixture.mongodb.client[joint_fixture.config.db_name][ + results = joint_fixture.mongodb_client[joint_fixture.config.db_name][ CLASS_NAME ].find({"$text": {"$search": query}}, {"score": {"$meta": "textScore"}}) results = [x for x in results] # type: ignore diff --git a/tests/test_resources.py b/tests/test_resources.py index a63a130..8a25a18 100644 --- a/tests/test_resources.py +++ b/tests/test_resources.py @@ -18,6 +18,7 @@ import pytest from mass.core import models +from mass.ports.inbound.query_handler import QueryHandlerPort from tests.fixtures.config import get_config from tests.fixtures.joint import JointFixture @@ -30,7 +31,7 @@ async def test_basic_query(joint_fixture: JointFixture): """Make sure we can pull back the documents as expected""" # pull back all 3 test documents - results = await joint_fixture.query_handler.handle_query( + results = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[] ) @@ -39,7 +40,7 @@ async def test_basic_query(joint_fixture: JointFixture): async def test_text_search(joint_fixture: JointFixture): """Test basic text search""" - results_text = await joint_fixture.query_handler.handle_query( + results_text = await joint_fixture.handle_query( class_name=CLASS_NAME, query="poolside", filters=[] ) @@ -49,7 +50,7 @@ async def test_text_search(joint_fixture: JointFixture): async def test_filters_work(joint_fixture: JointFixture): """Test a query with filters selected but no query string""" - results_filtered = await joint_fixture.query_handler.handle_query( + results_filtered = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[models.Filter(key="city", value="Amsterdam")], @@ -58,7 +59,7 @@ async def test_filters_work(joint_fixture: JointFixture): assert results_filtered.count == 1 assert results_filtered.hits[0].id_ == "3zoo-id" - results_multi_filter = await joint_fixture.query_handler.handle_query( + results_multi_filter = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[ @@ -73,7 +74,7 @@ async def test_filters_work(joint_fixture: JointFixture): async def test_facets_returned(joint_fixture: JointFixture): """Verify that facet fields are returned correctly""" - results_faceted = await joint_fixture.query_handler.handle_query( + results_faceted = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[models.Filter(key="category", value="hotel")], @@ -114,7 +115,7 @@ async def test_facets_returned(joint_fixture: JointFixture): async def test_limit_parameter(joint_fixture: JointFixture): """Test that the limit parameter works""" - results_limited = await joint_fixture.query_handler.handle_query( + results_limited = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[], limit=2 ) assert len(results_limited.hits) == 2 @@ -122,7 +123,7 @@ async def test_limit_parameter(joint_fixture: JointFixture): async def test_skip_parameter(joint_fixture: JointFixture): """Test that the skip parameter works""" - results_skip = await joint_fixture.query_handler.handle_query( + results_skip = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[], skip=1 ) assert len(results_skip.hits) == 2 @@ -131,7 +132,7 @@ async def test_skip_parameter(joint_fixture: JointFixture): async def test_all_parameters(joint_fixture: JointFixture): """Sanity check - make sure it all works together""" - results_all = await joint_fixture.query_handler.handle_query( + results_all = await joint_fixture.handle_query( class_name=CLASS_NAME, query="hotel", filters=[models.Filter(key="category", value="hotel")], @@ -146,7 +147,7 @@ async def test_all_parameters(joint_fixture: JointFixture): async def test_resource_load(joint_fixture: JointFixture): """Test the load function in the query handler""" # get all the documents in the collection - results_all = await joint_fixture.query_handler.handle_query( + results_all = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[] ) @@ -162,12 +163,12 @@ async def test_resource_load(joint_fixture: JointFixture): await joint_fixture.load_resource(resource=resource, class_name=CLASS_NAME) # make sure the new resource is added to the collection - results_after_load = await joint_fixture.query_handler.handle_query( + results_after_load = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[] ) assert results_after_load.count - results_all.count == 1 - target_search = await joint_fixture.query_handler.handle_query( + target_search = await joint_fixture.handle_query( class_name=CLASS_NAME, query="added-resource", filters=[], @@ -199,7 +200,7 @@ async def test_loading_non_configured_resource(joint_fixture: JointFixture): }, ) - with pytest.raises(joint_fixture.query_handler.ClassNotConfiguredError): + with pytest.raises(QueryHandlerPort.ClassNotConfiguredError): await joint_fixture.load_resource(resource=resource, class_name="ThisWillBreak") @@ -217,16 +218,14 @@ async def test_error_from_malformed_resource(joint_fixture: JointFixture): await joint_fixture.load_resource(resource=resource, class_name=CLASS_NAME) - with pytest.raises(joint_fixture.query_handler.ValidationError): - await joint_fixture.query_handler.handle_query( - class_name=CLASS_NAME, query="", filters=[] - ) + with pytest.raises(QueryHandlerPort.ValidationError): + await joint_fixture.handle_query(class_name=CLASS_NAME, query="", filters=[]) async def test_absent_resource(joint_fixture: JointFixture): """Make sure we get an error when looking for a resource type that doesn't exist""" - with pytest.raises(joint_fixture.query_handler.ClassNotConfiguredError): - await joint_fixture.query_handler.handle_query( + with pytest.raises(QueryHandlerPort.ClassNotConfiguredError): + await joint_fixture.handle_query( class_name="does_not_exist", query="", filters=[] ) @@ -236,7 +235,7 @@ async def test_resource_deletion(joint_fixture: JointFixture): Verify that the targeted resource is deleted and nothing else. """ - all_resources = await joint_fixture.query_handler.handle_query( + all_resources = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[] ) @@ -246,7 +245,7 @@ async def test_resource_deletion(joint_fixture: JointFixture): ) # see if deletion occurred, and make sure only one item was deleted - results_after_deletion = await joint_fixture.query_handler.handle_query( + results_after_deletion = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[] ) assert all_resources.count - results_after_deletion.count == 1 @@ -258,20 +257,20 @@ async def test_resource_deletion(joint_fixture: JointFixture): async def test_resource_deletion_failure(joint_fixture: JointFixture): """Test for correct error when failing to delete a resource""" - all_resources = await joint_fixture.query_handler.handle_query( + all_resources = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[] ) assert all_resources.count > 0 # try to delete a resource that doesn't exist - with pytest.raises(joint_fixture.query_handler.ResourceNotFoundError): + with pytest.raises(QueryHandlerPort.ResourceNotFoundError): await joint_fixture.delete_resource( resource_id="not-here", class_name=CLASS_NAME ) # verify that nothing was actually deleted - all_resources_again = await joint_fixture.query_handler.handle_query( + all_resources_again = await joint_fixture.handle_query( class_name=CLASS_NAME, query="", filters=[] ) @@ -280,7 +279,7 @@ async def test_resource_deletion_failure(joint_fixture: JointFixture): async def test_resource_deletion_not_configured(joint_fixture: JointFixture): """Test for correct error when trying to delete a non-configured resource""" - with pytest.raises(joint_fixture.query_handler.ClassNotConfiguredError): + with pytest.raises(QueryHandlerPort.ClassNotConfiguredError): await joint_fixture.delete_resource( resource_id="1HotelAlpha-id", class_name="Not-Configured" )