diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..b172375
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,5 @@
+[paths]
+source =
+ src
+ /workspace/src
+ **/lib/python*/site-packages
diff --git a/.deprecated_files b/.deprecated_files
index 888a8e8..ca3684e 100644
--- a/.deprecated_files
+++ b/.deprecated_files
@@ -9,8 +9,15 @@
.github/workflows/check_mandatory_and_static_files.yaml
.github/workflows/dev_cd.yaml
+.github/workflows/unit_and_int_tests.yaml
scripts/check_mandatory_and_static_files.py
scripts/update_static_files.py
docs
+
+setup.py
+setup.cfg
+
+.pylintrc
+.flake8
diff --git a/.devcontainer/dev_install b/.devcontainer/dev_install
index 31cb590..b5ee189 100755
--- a/.devcontainer/dev_install
+++ b/.devcontainer/dev_install
@@ -6,11 +6,11 @@ cd /workspace
# upgrade pip
python -m pip install --upgrade pip
-# install with all extras in editable mode
-pip install -e .[all]
-
# install or upgrade dependencies for development and testing
-pip install -r requirements-dev.txt
+pip install --no-deps -r requirements-dev.txt
+
+# install the package itself in edit mode:
+pip install --no-deps -e .
# install pre-commit hooks to git
pre-commit install
diff --git a/.flake8 b/.flake8
deleted file mode 100644
index bfd9ba4..0000000
--- a/.flake8
+++ /dev/null
@@ -1,21 +0,0 @@
-; Copyright 2021 - 2023 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.
-
-[flake8]
-ignore = E, W
- # ignore all style checks from pycodestyle
- # as they are already checked by black
-exclude = .git,__pycache__,build,dist
-max-complexity = 10
diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml
index 786220f..bdcd34b 100644
--- a/.github/workflows/cd.yaml
+++ b/.github/workflows/cd.yaml
@@ -2,7 +2,8 @@ name: CD
on:
release:
- types: [published]
+ types:
+ [published]
# trigger only on new release
jobs:
@@ -44,7 +45,7 @@ jobs:
name: Verify package version vs tag version
# package version must be same with tag version
run: |
- PKG_VER="$(python setup.py --version)"
+ PKG_VER="$(grep -oP 'version = "\K[^"]+' pyproject.toml)"
echo "Package version is $PKG_VER" >&2
echo "Tag version is ${{ steps.get_version_tag.outputs.version }}" >&2
if [ "$PKG_VER" != "${{ steps.get_version_tag.outputs.version }}" ]; then
diff --git a/.github/workflows/check_config_docs.yaml b/.github/workflows/check_config_docs.yaml
index c44b4b7..f863f80 100644
--- a/.github/workflows/check_config_docs.yaml
+++ b/.github/workflows/check_config_docs.yaml
@@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v3
- id: common
- uses: ghga-de/gh-action-common@v2
+ uses: ghga-de/gh-action-common@v3
- name: Check config docs
run: |
diff --git a/.github/workflows/check_openapi_spec.yaml b/.github/workflows/check_openapi_spec.yaml
index 84966b8..5fad821 100644
--- a/.github/workflows/check_openapi_spec.yaml
+++ b/.github/workflows/check_openapi_spec.yaml
@@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v3
- id: common
- uses: ghga-de/gh-action-common@v2
+ uses: ghga-de/gh-action-common@v3
- name: Check if openapi.yaml is up to date
run: |
diff --git a/.github/workflows/check_readme.yaml b/.github/workflows/check_readme.yaml
index a052d1e..9f400d2 100644
--- a/.github/workflows/check_readme.yaml
+++ b/.github/workflows/check_readme.yaml
@@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v3
- id: common
- uses: ghga-de/gh-action-common@v2
+ uses: ghga-de/gh-action-common@v3
- name: Check readme
run: |
diff --git a/.github/workflows/static_code_analysis.yaml b/.github/workflows/static_code_analysis.yaml
index 31feccf..401f428 100644
--- a/.github/workflows/static_code_analysis.yaml
+++ b/.github/workflows/static_code_analysis.yaml
@@ -10,29 +10,19 @@ jobs:
- uses: actions/checkout@v3
- id: common
- uses: ghga-de/gh-action-common@v2
+ uses: ghga-de/gh-action-common@v3
- uses: pre-commit/action@v3.0.0
env:
SKIP: no-commit-to-branch
+ - name: ruff
+ uses: chartboost/ruff-action@v1
- name: black
run: |
black --check .
- - name: isort
- run: |
- isort --check --profile black .
- name: mypy
run: |
mypy .
- - name: pylint
- run: |
- pylint "${{ steps.common.outputs.MAIN_SRC_DIR }}"
- - name: flake8
- run: |
- flake8 --config .flake8
- - name: bandit
- run: |
- bandit -r "${{ steps.common.outputs.MAIN_SRC_DIR }}"
- name: Check license header and file
run: |
./scripts/license_checker.py
diff --git a/.github/workflows/unit_and_int_tests.yaml b/.github/workflows/tests.yaml
similarity index 65%
rename from .github/workflows/unit_and_int_tests.yaml
rename to .github/workflows/tests.yaml
index d6e5ed3..05f474c 100644
--- a/.github/workflows/unit_and_int_tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -1,24 +1,26 @@
-name: Unit and Integration Tests
+name: Tests
on: push
jobs:
- unit_and_int_tests:
+ tests:
runs-on: ubuntu-latest
- name: Unit and Integration Tests
+ name: Tests
steps:
- uses: actions/checkout@v3
- id: common
- uses: ghga-de/gh-action-common@v2
+ uses: ghga-de/gh-action-common@v3
- - name: Run pytest
+ - id: pytest
run: |
export ${{ steps.common.outputs.CONFIG_YAML_ENV_VAR_NAME }}="${{ steps.common.outputs.CONFIG_YAML }}"
+
pytest \
- --cov="${{ steps.common.outputs.MAIN_SRC_DIR }}" \
- --cov-report=xml
+ --cov="${{ steps.common.outputs.PACKAGE_NAME }}" \
+ --cov-report=xml \
+ tests
- id: coveralls
name: Upload coverage to coveralls
diff --git a/.gitignore b/.gitignore
index 28b8a9d..a71cfd2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,6 +50,7 @@ coverage.xml
*.py,cover
.hypothesis/
.pytest_cache/
+.ruff_cache/
prof/
# Translations
diff --git a/.mandatory_files b/.mandatory_files
index d7cf949..c0b51fb 100644
--- a/.mandatory_files
+++ b/.mandatory_files
@@ -19,10 +19,12 @@ Dockerfile
config_schema.json
example_config.yaml
LICENSE
+pyproject.toml
README.md
-setup.py
-setup.cfg
+requirements-dev.in
requirements-dev.txt
+requirements.txt
.description.md
.design.md
+.pre-commit-config.yaml
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 31f8a5b..594a10d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -7,6 +7,20 @@ default_language_version:
minimum_pre_commit_version: 3.0.0
repos:
+ - repo: local
+ hooks:
+ - id: update-hook-revs
+ name: "ensure hooks are up to date"
+ language: python
+ additional_dependencies:
+ - "packaging"
+ - "typer"
+ fail_fast: true
+ always_run: true
+ entry: ./scripts/update_hook_revs.py
+ files: '\.pre-commit-config.yaml'
+ args: [--check]
+ pass_filenames: false
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
@@ -36,33 +50,17 @@ repos:
- id: debug-statements
- id: debug-statements
- id: debug-statements
+ - repo: https://github.com/astral-sh/ruff-pre-commit
+ rev: v0.0.292
+ hooks:
+ - id: ruff
+ args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/psf/black
- rev: 23.1.0
+ rev: 23.9.1
hooks:
- id: black
- - repo: https://github.com/pycqa/isort
- rev: 5.12.0
- hooks:
- - id: isort
- args: [--profile, black]
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v1.0.0
+ rev: v1.5.1
hooks:
- id: mypy
args: [--no-warn-unused-ignores]
- - repo: https://github.com/PyCQA/pylint
- rev: v2.16.4
- hooks:
- - id: pylint
- args: [--disable=E0401]
- exclude: tests|.devcontainer
- - repo: https://github.com/PyCQA/flake8
- rev: 6.0.0
- hooks:
- - id: flake8
- args: [--config, .flake8]
- - repo: https://github.com/PyCQA/bandit
- rev: 1.7.4
- hooks:
- - id: bandit
- exclude: tests|.devcontainer|scripts
diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index cfb50db..0000000
--- a/.pylintrc
+++ /dev/null
@@ -1,546 +0,0 @@
-[MASTER]
-
-# A comma-separated list of package or module names from where C extensions may
-# be loaded. Extensions are loading into the active Python interpreter and may
-# run arbitrary code.
-extension-pkg-allow-list=
-
-# A comma-separated list of package or module names from where C extensions may
-# be loaded. Extensions are loading into the active Python interpreter and may
-# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
-# for backward compatibility.)
-extension-pkg-whitelist=pydantic
-
-# Specify a score threshold to be exceeded before program exits with error.
-fail-under=10.0
-
-# Files or directories to be skipped. They should be base names, not paths.
-ignore=CVS
-
-
-# Files or directories matching the regex patterns are skipped. The regex
-# matches against base names, not paths.
-ignore-patterns=
-
-# Python code to execute, usually for sys.path manipulation such as
-# pygtk.require().
-#init-hook=
-
-# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
-# number of processors available to use.
-jobs=1
-
-# Control the amount of potential inferred values when inferring a single
-# object. This can help the performance when dealing with large functions or
-# complex, nested conditions.
-limit-inference-results=100
-
-# List of plugins (as comma separated values of python module names) to load,
-# usually to register additional checkers.
-load-plugins=
-
-# Pickle collected data for later comparisons.
-persistent=yes
-
-# When enabled, pylint would attempt to guess common misconfiguration and emit
-# user-friendly hints instead of false-positive error messages.
-suggestion-mode=yes
-
-# Allow loading of arbitrary C extensions. Extensions are imported into the
-# active Python interpreter and may run arbitrary code.
-unsafe-load-any-extension=no
-
-
-[MESSAGES CONTROL]
-
-# Only show warnings with the listed confidence levels. Leave empty to show
-# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
-confidence=
-
-# Disable the message, report, category or checker with the given id(s). You
-# can either give multiple identifiers separated by comma (,) or put this
-# option multiple times (only on the command line, not in the configuration
-# file where it should appear only once). You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
-# you want to run only the similarities checker, you can use "--disable=all
-# --enable=similarities". If you want to run only the classes checker, but have
-# no Warning level messages displayed, use "--disable=all --enable=classes
-# --disable=W".
-disable=
- duplicate-code, # is behaving strangely sometimes and cannot
- # be disabled on an individual basis:
- # https://github.com/PyCQA/pylint/issues/214
-
- too-few-public-methods, # says that classes should always have methods
- # but that is not true anymore (e.g. dataclasses)
-
- unnecessary-ellipsis, # often used for interfaces/protocols
-
-# Enable the message, report, category or checker with the given id(s). You can
-# either give multiple identifier separated by comma (,) or put this option
-# multiple time (only on the command line, not in the configuration file where
-# it should appear only once). See also the "--disable" option for examples.
-enable=c-extension-no-member
-
-
-[REPORTS]
-
-# Python expression which should return a score less than or equal to 10. You
-# have access to the variables 'error', 'warning', 'refactor', and 'convention'
-# which contain the number of messages in each category, as well as 'statement'
-# which is the total number of statements analyzed. This score is used by the
-# global evaluation report (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
-
-# Template used to display messages. This is a python new-style format string
-# used to format the message information. See doc for all details.
-#msg-template=
-
-# Set the output format. Available formats are text, parseable, colorized, json
-# and msvs (visual studio). You can also give a reporter class, e.g.
-# mypackage.mymodule.MyReporterClass.
-output-format=text
-
-# Tells whether to display a full report or only the messages.
-reports=no
-
-# Activate the evaluation score.
-score=yes
-
-
-[REFACTORING]
-
-# Maximum number of nested blocks for function / method body
-max-nested-blocks=5
-
-# Complete name of functions that never returns. When checking for
-# inconsistent-return-statements if a never returning function is called then
-# it will be considered as an explicit return statement and no message will be
-# printed.
-never-returning-functions=sys.exit,argparse.parse_error
-
-
-[STRING]
-
-# This flag controls whether inconsistent-quotes generates a warning when the
-# character used as a quote delimiter is used inconsistently within a module.
-check-quote-consistency=no
-
-# This flag controls whether the implicit-str-concat should generate a warning
-# on implicit string concatenation in sequences defined over several lines.
-check-str-concat-over-line-jumps=no
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=FIXME,
- XXX,
- TODO
-
-# Regular expression of note tags to take in consideration.
-#notes-rgx=
-
-
-[SPELLING]
-
-# Limits count of emitted suggestions for spelling mistakes.
-max-spelling-suggestions=4
-
-# Spelling dictionary name. Available dictionaries: none. To make it work,
-# install the 'python-enchant' package.
-spelling-dict=
-
-# List of comma separated words that should be considered directives if they
-# appear and the beginning of a comment and should not be checked.
-spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
-
-# List of comma separated words that should not be checked.
-spelling-ignore-words=
-
-# A path to a file that contains the private dictionary; one word per line.
-spelling-private-dict-file=
-
-# Tells whether to store unknown words to the private dictionary (see the
-# --spelling-private-dict-file option) instead of raising a message.
-spelling-store-unknown-words=no
-
-
-[BASIC]
-
-# Naming style matching correct argument names.
-argument-naming-style=snake_case
-
-# Regular expression matching correct argument names. Overrides argument-
-# naming-style.
-#argument-rgx=
-
-# Naming style matching correct attribute names.
-attr-naming-style=snake_case
-
-# Regular expression matching correct attribute names. Overrides attr-naming-
-# style.
-#attr-rgx=
-
-# Bad variable names which should always be refused, separated by a comma.
-bad-names=foo,
- bar,
- baz,
- toto,
- tutu,
- tata
-
-# Bad variable names regexes, separated by a comma. If names match any regex,
-# they will always be refused
-bad-names-rgxs=
-
-# Naming style matching correct class attribute names.
-class-attribute-naming-style=any
-
-# Regular expression matching correct class attribute names. Overrides class-
-# attribute-naming-style.
-#class-attribute-rgx=
-
-# Naming style matching correct class constant names.
-class-const-naming-style=UPPER_CASE
-
-# Regular expression matching correct class constant names. Overrides class-
-# const-naming-style.
-#class-const-rgx=
-
-# Naming style matching correct class names.
-class-naming-style=PascalCase
-
-# Regular expression matching correct class names. Overrides class-naming-
-# style.
-#class-rgx=
-
-# Naming style matching correct constant names.
-const-naming-style=UPPER_CASE
-
-# Regular expression matching correct constant names. Overrides const-naming-
-# style.
-#const-rgx=
-
-# Minimum line length for functions/classes that require docstrings, shorter
-# ones are exempt.
-docstring-min-length=-1
-
-# Naming style matching correct function names.
-function-naming-style=snake_case
-
-# Regular expression matching correct function names. Overrides function-
-# naming-style.
-#function-rgx=
-
-# Good variable names which should always be accepted, separated by a comma.
-good-names=i,
- j,
- k,
- ex,
- ok,
- Run,
- _,
- __,
- id,
- db,
-
-# Good variable names regexes, separated by a comma. If names match any regex,
-# they will always be accepted
-good-names-rgxs=
-
-# Include a hint for the correct naming format with invalid-name.
-include-naming-hint=no
-
-# Naming style matching correct inline iteration names.
-inlinevar-naming-style=any
-
-# Regular expression matching correct inline iteration names. Overrides
-# inlinevar-naming-style.
-#inlinevar-rgx=
-
-# Naming style matching correct method names.
-method-naming-style=snake_case
-
-# Regular expression matching correct method names. Overrides method-naming-
-# style.
-#method-rgx=
-
-# Naming style matching correct module names.
-module-naming-style=snake_case
-
-# Regular expression matching correct module names. Overrides module-naming-
-# style.
-#module-rgx=
-
-# Colon-delimited sets of names that determine each other's naming style when
-# the name regexes allow several styles.
-name-group=
-
-# Regular expression which should only match function or class names that do
-# not require a docstring.
-no-docstring-rgx=^_
-
-# List of decorators that produce properties, such as abc.abstractproperty. Add
-# to this list to register other decorators that produce valid properties.
-# These decorators are taken in consideration only for invalid-name.
-property-classes=abc.abstractproperty
-
-# Naming style matching correct variable names.
-variable-naming-style=snake_case
-
-# Regular expression matching correct variable names. Overrides variable-
-# naming-style.
-#variable-rgx=
-
-
-[LOGGING]
-
-# The type of string formatting that logging methods do. `old` means using %
-# formatting, `new` is for `{}` formatting.
-logging-format-style=old
-
-# Logging modules to check that the string format arguments are in logging
-# function parameter format.
-logging-modules=logging
-
-
-[VARIABLES]
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid defining new builtins when possible.
-additional-builtins=
-
-# Tells whether unused global variables should be treated as a violation.
-allow-global-unused-variables=yes
-
-# List of names allowed to shadow builtins
-allowed-redefined-builtins=
-
-# List of strings which can identify a callback function by name. A callback
-# name must start or end with one of those strings.
-callbacks=cb_,
- _cb
-
-# A regular expression matching the name of dummy variables (i.e. expected to
-# not be used).
-dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
-
-# Argument names that match this expression will be ignored. Default to name
-# with leading underscore.
-ignored-argument-names=_.*|^ignored_|^unused_
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# List of qualified module names which can have objects that can redefine
-# builtins.
-redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
-
-
-[TYPECHECK]
-
-# List of decorators that produce context managers, such as
-# contextlib.contextmanager. Add to this list to register other decorators that
-# produce valid context managers.
-contextmanager-decorators=contextlib.contextmanager
-
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E1101 when accessed. Python regular
-# expressions are accepted.
-generated-members=
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# Tells whether to warn about missing members when the owner of the attribute
-# is inferred to be None.
-ignore-none=yes
-
-# This flag controls whether pylint should warn about no-member and similar
-# checks whenever an opaque object is returned when inferring. The inference
-# can return multiple potential results while evaluating a Python object, but
-# some branches might not be evaluated, which results in partial inference. In
-# that case, it might be useful to still emit no-member and other checks for
-# the rest of the inferred objects.
-ignore-on-opaque-inference=yes
-
-# List of class names for which member attributes should not be checked (useful
-# for classes with dynamically set attributes). This supports the use of
-# qualified names.
-ignored-classes=optparse.Values,thread._local,_thread._local
-
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis). It
-# supports qualified module names, as well as Unix pattern matching.
-ignored-modules=
-
-# Show a hint with possible names when a member name was not found. The aspect
-# of finding the hint is based on edit distance.
-missing-member-hint=yes
-
-# The minimum edit distance a name should have in order to be considered a
-# similar match for a missing member name.
-missing-member-hint-distance=1
-
-# The total number of similar names that should be taken in consideration when
-# showing a hint for a missing member.
-missing-member-max-choices=1
-
-# List of decorators that change the signature of a decorated function.
-signature-mutators=
-
-
-[FORMAT]
-
-# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
-expected-line-ending-format=
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=^\s*(# )??$
-
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
-
-# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
-# tab).
-indent-string=' '
-
-# Maximum number of characters on a single line.
-max-line-length=100
-
-# Maximum number of lines in a module.
-max-module-lines=1000
-
-# Allow the body of a class to be on the same line as the declaration if body
-# contains single statement.
-single-line-class-stmt=no
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=no
-
-
-[SIMILARITIES]
-
-# Ignore comments when computing similarities.
-ignore-comments=yes
-
-# Ignore docstrings when computing similarities.
-ignore-docstrings=yes
-
-# Ignore imports when computing similarities.
-ignore-imports=no
-
-# Minimum lines number of a similarity.
-min-similarity-lines=4
-
-
-[DESIGN]
-
-# Maximum number of arguments for function / method.
-max-args=5
-
-# Maximum number of attributes for a class (see R0902).
-max-attributes=7
-
-# Maximum number of boolean expressions in an if statement (see R0916).
-max-bool-expr=5
-
-# Maximum number of branch for function / method body.
-max-branches=12
-
-# Maximum number of locals for function / method body.
-max-locals=15
-
-# Maximum number of parents for a class (see R0901).
-max-parents=7
-
-# Maximum number of public methods for a class (see R0904).
-max-public-methods=20
-
-# Maximum number of return / yield for function / method body.
-max-returns=6
-
-# Maximum number of statements in function / method body.
-max-statements=50
-
-# Minimum number of public methods for a class (see R0903).
-min-public-methods=2
-
-
-[IMPORTS]
-
-# List of modules that can be imported at any level, not just the top level
-# one.
-allow-any-import-level=
-
-# Allow wildcard imports from modules that define __all__.
-allow-wildcard-with-all=no
-
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
-# Deprecated modules which should not be used, separated by a comma.
-deprecated-modules=optparse,tkinter.tix
-
-# Output a graph (.gv or any supported image format) of external dependencies
-# to the given file (report RP0402 must not be disabled).
-ext-import-graph=
-
-# Output a graph (.gv or any supported image format) of all (i.e. internal and
-# external) dependencies to the given file (report RP0402 must not be
-# disabled).
-import-graph=
-
-# Output a graph (.gv or any supported image format) of internal dependencies
-# to the given file (report RP0402 must not be disabled).
-int-import-graph=
-
-# Force import order to recognize a module as part of the standard
-# compatibility libraries.
-known-standard-library=
-
-# Force import order to recognize a module as part of a third party library.
-known-third-party=enchant
-
-# Couples of modules and preferred modules, separated by a comma.
-preferred-modules=
-
-
-[CLASSES]
-
-# Warn about protected attribute access inside special methods
-check-protected-access-in-special-methods=no
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,
- __new__,
- setUp,
- __post_init__
-
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,
- _fields,
- _replace,
- _source,
- _make
-
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
-
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=cls
-
-
-[EXCEPTIONS]
-
-# Exceptions that will emit a warning when being caught. Defaults to
-# "builtins.BaseException, builtins.Exception".
-overgeneral-exceptions=builtins.BaseException,
- builtins.Exception
diff --git a/.readme_template.md b/.readme_template.md
index 878dbdc..dd8b184 100644
--- a/.readme_template.md
+++ b/.readme_template.md
@@ -97,7 +97,7 @@ Moreover, inside the devcontainer, a convenience commands `dev_install` is avail
It installs the service with all development dependencies, installs pre-commit.
The installation is performed automatically when you build the devcontainer. However,
-if you update dependencies in the [`./setup.cfg`](./setup.cfg) or the
+if you update dependencies in the [`./pyproject.toml`](./pyproject.toml) or the
[`./requirements-dev.txt`](./requirements-dev.txt), please run it again.
## License
diff --git a/.ruff.toml b/.ruff.toml
new file mode 100644
index 0000000..27ee0ea
--- /dev/null
+++ b/.ruff.toml
@@ -0,0 +1,62 @@
+exclude = [
+ ".git",
+ ".devcontainer",
+ "__pycache__",
+ "build",
+ "dist",
+]
+
+ignore = [
+ "E", # pycodestyle errors
+ "W", # pycodestyle warnings - pycodestyle covered by black
+ "PLW", # pylint warnings
+ "RUF001", # ambiguous unicode character strings
+ "RUF010", # explicit conversion to string or repr: !s or !r
+ "RUF012", # mutable class variables need typing.ClassVar annotation
+ "N818", # Errors need to have Error suffix
+ "B008", # function call in arg defaults,
+ "PLR2004", # magic numbers should be constants
+ "D205", # blank-line-after-summary
+ "D400", # first doc line ends in period
+ "D401", # non-imperative-mood
+ "D107", # missing docstring in __init__
+]
+
+line-length = 88
+
+select = [
+ "C90", # McCabe Complexity
+ "F", # pyflakes codes
+ "I", # isort
+ "S", # flake8-bandit
+ "B", # flake8-bugbear
+ "N", # pep8-naming
+ "UP", # pyupgrade
+ "PL", # pylint
+ "RUF", # ruff
+ "SIM", # flake8-simplify
+ "D", # pydocstyle
+]
+
+fixable = [
+ "UP", # e.g. List -> list
+ "I", # sort imports
+ "D", # pydocstyle
+]
+
+src = ["src", "tests", "examples", "scripts"]
+
+target-version = "py39"
+
+[mccabe]
+max-complexity = 10
+
+[per-file-ignores]
+"scripts/*" = ["PL", "S", "SIM", "D"]
+"tests/*" = ["S", "SIM", "PLR", "B011"]
+".devcontainer/*" = ["S", "SIM", "D"]
+"examples/*" = ["S", "D"]
+"__init__.py" = ["D"]
+
+[pydocstyle]
+convention = "pep257"
diff --git a/.static_files b/.static_files
index 3ebfa18..e3c8f76 100644
--- a/.static_files
+++ b/.static_files
@@ -23,31 +23,31 @@ scripts/update_config_docs.py
scripts/update_template_files.py
scripts/update_openapi_docs.py
scripts/update_readme.py
+scripts/update_lock.py
+scripts/update_hook_revs.py
+scripts/list_outdated_dependencies.py
scripts/README.md
.github/workflows/check_config_docs.yaml
.github/workflows/check_template_files.yaml
.github/workflows/static_code_analysis.yaml
-.github/workflows/unit_and_int_tests.yaml
+.github/workflows/tests.yaml
.github/workflows/check_openapi_spec.yaml
.github/workflows/check_readme.yaml
.github/workflows/cd.yaml
example_data/README.md
+.coveragerc
.editorconfig
-.flake8
.gitattributes
.gitignore
-.pre-commit-config.yaml
-.pylintrc
.mypy.ini
+.ruff.toml
pytest.ini
LICENSE
-requirements.txt
-requirements-dev-common.txt
-setup.py
+requirements-dev-common.in
.readme_template.md
readme_generation.md
diff --git a/README.md b/README.md
index 83878e5..7c17dfa 100644
--- a/README.md
+++ b/README.md
@@ -32,13 +32,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:0.3.2
+docker pull ghga/mass:0.3.3
```
Or you can build the container yourself from the [`./Dockerfile`](./Dockerfile):
```bash
# Execute in the repo's root dir:
-docker build -t ghga/mass:0.3.2 .
+docker build -t ghga/mass:0.3.3 .
```
For production-ready deployment, we recommend using Kubernetes, however,
@@ -46,7 +46,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:0.3.2 --help
+docker run -p 8080:8080 ghga/mass:0.3.3 --help
```
If you prefer not to use containers, you may install the service from source:
@@ -64,7 +64,7 @@ mass --help
The service requires the following configuration parameters:
- **`searchable_classes`** *(object)*: A collection of searchable_classes with facetable properties. Can contain additional properties.
- - **Additional Properties**: Refer to *#/definitions/SearchableClass*.
+ - **Additional Properties**: Refer to *[#/definitions/SearchableClass](#definitions/SearchableClass)*.
- **`resource_change_event_topic`** *(string)*: Name of the event topic used to track resource deletion and upsertion events.
@@ -72,7 +72,7 @@ The service requires the following configuration parameters:
- **`resource_upsertion_event_type`** *(string)*: The type to use for events with upsert instructions.
-- **`service_name`** *(string)*: Default: `mass`.
+- **`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.
@@ -80,25 +80,25 @@ The service requires the following configuration parameters:
- **Items** *(string)*
-- **`db_connection_str`** *(string)*: MongoDB connection string. Might include credentials. For more information see: https://naiveskill.com/mongodb-connection-string/.
+- **`db_connection_str`** *(string, format: password)*: MongoDB connection string. Might include credentials. For more information see: https://naiveskill.com/mongodb-connection-string/.
- **`db_name`** *(string)*: Name of the database located on the MongoDB server.
-- **`host`** *(string)*: IP of the host. Default: `127.0.0.1`.
+- **`host`** *(string)*: IP of the host. Default: `"127.0.0.1"`.
- **`port`** *(integer)*: Port to expose the server on the specified host. Default: `8080`.
-- **`log_level`** *(string)*: Controls the verbosity of the log. Must be one of: `['critical', 'error', 'warning', 'info', 'debug', 'trace']`. Default: `info`.
+- **`log_level`** *(string)*: Controls the verbosity of the log. Must be one of: `["critical", "error", "warning", "info", "debug", "trace"]`. Default: `"info"`.
-- **`auto_reload`** *(boolean)*: A development feature. Set to `True` to automatically reload the server upon code changes. Default: `False`.
+- **`auto_reload`** *(boolean)*: A development feature. Set to `True` to automatically reload the server upon code changes. Default: `false`.
- **`workers`** *(integer)*: Number of workers processes to run. Default: `1`.
-- **`api_root_path`** *(string)*: Root path at which the API is reachable. This is relative to the specified host and port. Default: `/`.
+- **`api_root_path`** *(string)*: Root path at which the API is reachable. This is relative to the specified host and port. Default: `"/"`.
-- **`openapi_url`** *(string)*: Path to get the openapi specification in JSON format. This is relative to the specified host and port. Default: `/openapi.json`.
+- **`openapi_url`** *(string)*: Path to get the openapi specification in JSON format. This is relative to the specified host and port. Default: `"/openapi.json"`.
-- **`docs_url`** *(string)*: Path to host the swagger documentation. This is relative to the specified host and port. Default: `/docs`.
+- **`docs_url`** *(string)*: Path to host the swagger documentation. This is relative to the specified host and port. Default: `"/docs"`.
- **`cors_allowed_origins`** *(array)*: A list of origins that should be permitted to make cross-origin requests. By default, cross-origin requests are not allowed. You can use ['*'] to allow any origin.
@@ -117,19 +117,19 @@ The service requires the following configuration parameters:
## Definitions
-- **`FacetLabel`** *(object)*: Contains the key and corresponding user-friendly name for a facet.
+- **`FacetLabel`** *(object)*: Contains the key and corresponding user-friendly name for a facet.
- - **`key`** *(string)*: The raw facet key, such as study.type.
+ - **`key`** *(string, required)*: The raw facet key, such as study.type.
- - **`name`** *(string)*: The user-friendly name for the facet. Default: ``.
+ - **`name`** *(string)*: The user-friendly name for the facet. Default: `""`.
-- **`SearchableClass`** *(object)*: Represents a searchable artifact or resource type.
+- **`SearchableClass`** *(object)*: Represents a searchable artifact or resource type.
- - **`description`** *(string)*: A brief description of the resource type.
+ - **`description`** *(string, required)*: A brief description of the resource type.
- - **`facetable_properties`** *(array)*: A list of of the facetable properties for the resource type.
+ - **`facetable_properties`** *(array, required)*: A list of of the facetable properties for the resource type.
- - **Items**: Refer to *#/definitions/FacetLabel*.
+ - **Items**: Refer to *[#/definitions/FacetLabel](#definitions/FacetLabel)*.
### Usage:
@@ -205,7 +205,7 @@ Moreover, inside the devcontainer, a convenience commands `dev_install` is avail
It installs the service with all development dependencies, installs pre-commit.
The installation is performed automatically when you build the devcontainer. However,
-if you update dependencies in the [`./setup.cfg`](./setup.cfg) or the
+if you update dependencies in the [`./pyproject.toml`](./pyproject.toml) or the
[`./requirements-dev.txt`](./requirements-dev.txt), please run it again.
## License
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..1b954f2
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,42 @@
+[build-system]
+requires = ["setuptools>=67.7.2"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "mass"
+version = "0.3.3"
+description = "Metadata Artifact Search Service - A service for searching metadata artifacts and filtering results."
+readme = "README.md"
+authors = [
+ { name = "German Human Genome Phenome Archive (GHGA)", email = "contact@ghga.de" },
+]
+requires-python = ">=3.9"
+classifiers = [
+ "Development Status :: 1 - Planning",
+ "Operating System :: POSIX :: Linux",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "License :: OSI Approved :: Apache Software License",
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
+ "Topic :: Software Development :: Libraries",
+ "Intended Audience :: Developers",
+]
+dependencies = [
+ "typer>=0.7.0",
+ "ghga-service-commons[api]>=0.4.3",
+ "ghga-event-schemas>=0.13.4",
+ "hexkit[mongodb,akafka]>=0.10.2",
+]
+
+[project.license]
+text = "Apache 2.0"
+
+[project.urls]
+Repository = "https://github.com/ghga-de/mass"
+
+[project.scripts]
+mass = "mass.__main__:cli"
+
+[tool.setuptools.packages.find]
+where = ["src"]
diff --git a/readme_generation.md b/readme_generation.md
index 432153c..84cd618 100644
--- a/readme_generation.md
+++ b/readme_generation.md
@@ -24,11 +24,11 @@ outlined in the following.
- name: The full name of the package is derived from the remote origin Git repository.
- title: A title case representation of the name.
- shortname: An abbreviation of the full name. This is derived from the name mentioned
- in the [`./setup.cfg`](`./setup.cfg).
+ in the [`./pyproject.toml`](./pyproject.toml).
- summary: A short 1-2 sentence summary derived from the description in the
- [`./setup.cfg`](`./setup.cfg).
+ [`./pyproject.toml`](./pyproject.toml).
- version: The package version derived from the version specified in the
- [`./setup.cfg`](`./setup.cfg).
+ [`./pyproject.toml`](./pyproject.toml).
- description: A markdown-formatted description of the features and use cases of this
service or package. Obtained from the [`./.description.md`](./.description.md).
- design_description: A markdown-formatted description of the overall architecture and
diff --git a/requirements-dev-common.in b/requirements-dev-common.in
new file mode 100644
index 0000000..1c677cd
--- /dev/null
+++ b/requirements-dev-common.in
@@ -0,0 +1,34 @@
+# common requirements for development and testing of services
+
+pytest>=7.2.0
+pytest-asyncio>=0.20.3
+pytest-cov>=4.0.0
+pytest-profiling>=1.7.0
+snakeviz>=2.2.0
+
+pre-commit>=3.1.1
+
+mypy>=1.0.0
+mypy-extensions>=1.0.0
+
+ruff>=0.0.290
+
+black>=23.1.0
+
+click>=8.1.0
+typer>=0.7.0
+
+httpx>=0.23.3
+pytest-httpx>=0.21.3
+
+urllib3>=1.26.15
+requests>=2.28.2
+
+stringcase>=1.2.0
+jsonschema2md>=1.0.0
+setuptools>=67.7.2
+
+# required since switch to pyproject.toml and pip-tools
+pip-tools>=7.3.0
+tomli>=2.0.1
+tomli_w>=1.0.0
diff --git a/requirements-dev-common.txt b/requirements-dev-common.txt
deleted file mode 100644
index b253cb8..0000000
--- a/requirements-dev-common.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-# common requirements for development and testing of services
-
-pytest==7.2.0
-pytest-asyncio==0.20.3
-pytest-cov==4.0.0
-pytest-profiling==1.7.0
-snakeviz==2.2.0
-
-pre-commit==3.1.1
-
-mypy==1.0.0
-mypy-extensions==1.0.0
-
-pylint==2.16.4
-flake8==6.0.0
-bandit==1.7.4
-
-black==23.1.0
-
-isort==5.12.0
-
-click==8.1.3
-typer==0.7.0
-
-httpx==0.23.3
-pytest-httpx==0.21.3
-
-
-# work around until this issue is solved:
-# https://github.com/docker/docker-py/issues/3113
-urllib3==1.26.15
-requests==2.28.2
-
-stringcase==1.2.0
-jsonschema2md==0.4.0
-setuptools==67.7.2
diff --git a/requirements-dev.in b/requirements-dev.in
new file mode 100644
index 0000000..450e50d
--- /dev/null
+++ b/requirements-dev.in
@@ -0,0 +1,6 @@
+# requirements for development and testing this service
+
+-r requirements-dev-common.in
+
+# additional requirements can be listed here
+testcontainers[mongo]>=3.4.1
diff --git a/requirements-dev.txt b/requirements-dev.txt
index ff57ecc..7746962 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,6 +1,1196 @@
-# requirements for development and testing this service
+#
+# This file is autogenerated by pip-compile with Python 3.9
+# by the following command:
+#
+# pip-compile --generate-hashes --output-file=/workspace/requirements-dev.txt /tmp/tmpi5lkji3b/pyproject.toml /workspace/requirements-dev.in
+#
+aiokafka==0.8.0 \
+ --hash=sha256:021e9f0027ca63c6c04daccfdd0e985f7a56d51bd0d43f482f674a58fada52f5 \
+ --hash=sha256:0e9d61912678ecae6b3d407107c1a935f21d55af4585b70d8f5dcc39ecb949ce \
+ --hash=sha256:12457b59a06cff7369cace8d4460049b2ef2ab7f7cdc9c4924d577b9d4b48bf9 \
+ --hash=sha256:1c3fd832a06fdd68e82f100fe678a800dd6dbf5e8db6af9521be333f965c325b \
+ --hash=sha256:23f1fbdf54790a3751216e33e62228c8e1eb7feebcb19ef532cd3e4f13ae51ce \
+ --hash=sha256:2567465ee6de4d248fc416f2eef7d33bbe246a79073410ae2368b5bdaeb758c1 \
+ --hash=sha256:2760f095a8ffb5b9b97ad28a43a6a93f38d67cf3bc95b42e6b27462b614c8561 \
+ --hash=sha256:4439a03820dc64a8c3aa5fe17809541e6a001f6f6196aad6b6b88e7ded2b5396 \
+ --hash=sha256:49b30479f68ba9a484a0e3362fb9c48797d7320066db9fcd53e755451f389acb \
+ --hash=sha256:5202073bb8d2350b72805d45ff0125c800ed101506a4ba7be2f03ad1ba8ad1e6 \
+ --hash=sha256:539d8584652e354e7f7bbaf8843e936d91bfc28e224a53a82e1bcb64ac7f6dda \
+ --hash=sha256:57aa55b48004da9bf5a5d37d3412c2d373b0bf32118bdc5c78cc5635998674cc \
+ --hash=sha256:5a8038698a47333cdb0cd198cb4b3ccd2fbbc86ba9a4b9afc3eebe6544db0c2f \
+ --hash=sha256:65e1d27a1c1cd38c66e0b22928af74b192f7598da9acd5bb939c6acea5bb5036 \
+ --hash=sha256:6f50a940411ae6cd0d7bcaf2d821539e3a59b6de012f77de18a573565c9f638f \
+ --hash=sha256:7e292841beda7cfdcd6939aab6cc2a623acd3d655b166f7ff97c658f14ced8c5 \
+ --hash=sha256:881209100355a92696c6501ba1c2b32127bb1f7f2f318b400b3973ab0b52efed \
+ --hash=sha256:8857cbd76e97186e54b98ebb3ea7778fb3618826bb9e4d01375bfab0a1d93d69 \
+ --hash=sha256:9364eb81340b3a70a1222a4701c73a49ea0026a79bf138b4aec342f012d6f039 \
+ --hash=sha256:95682f661f295fac2f5f3f0132aea7c44a1b6c92726161daa509af67ac506885 \
+ --hash=sha256:9bf6d0da5804ae8888c357034d1a6750baa610999181e930678da0e87cec610d \
+ --hash=sha256:b36066df820e30f56386deb56d72efba287ba65419848888ea4b42f9e2741cff \
+ --hash=sha256:b86e3c1d649824103427c021593d75f44e01db1ffbc880b154e04098b534355f \
+ --hash=sha256:d459ab92a360cac240cf10b9ce88e64c1e41d942c7f63b1df6c2aafe27f946d0 \
+ --hash=sha256:dea214c2588237cf0d404624ccd99f466a2e853ca22d7153bb680b2d3f25cdde \
+ --hash=sha256:dfb6dfef6c18726a783d102a6c1b0dfb6d43785a46ff34e967ddfa8f774532dd \
+ --hash=sha256:e005b53597fe9bc6681a2a3b50728d235cf2fb8801e52790678c691c85383565 \
+ --hash=sha256:e07f07290a150552273c02bc5109d0a40bc0f32abc0ae5aeaa1e54fb86369251 \
+ --hash=sha256:ec896d114be157a886e3227bbe3f00658dc4d6f17b203bc44075650817703f0b \
+ --hash=sha256:f0a216a27f05b050d5a5308fb3444014fa6bca5f0cd63468eaa169c5f19ea1dd \
+ --hash=sha256:f3f96301337fa7f7242f46651619b8e9e8fa8f23902dc11416fe764436d662d3
+ # via hexkit
+anyio==4.0.0 \
+ --hash=sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f \
+ --hash=sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a
+ # via
+ # httpcore
+ # starlette
+ # watchfiles
+async-timeout==4.0.3 \
+ --hash=sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f \
+ --hash=sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028
+ # via aiokafka
+attrs==23.1.0 \
+ --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
+ --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
+ # via
+ # jsonschema
+ # referencing
+black==23.9.1 \
+ --hash=sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f \
+ --hash=sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7 \
+ --hash=sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100 \
+ --hash=sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573 \
+ --hash=sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d \
+ --hash=sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f \
+ --hash=sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9 \
+ --hash=sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300 \
+ --hash=sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948 \
+ --hash=sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325 \
+ --hash=sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9 \
+ --hash=sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71 \
+ --hash=sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186 \
+ --hash=sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f \
+ --hash=sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe \
+ --hash=sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855 \
+ --hash=sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80 \
+ --hash=sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393 \
+ --hash=sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c \
+ --hash=sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204 \
+ --hash=sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377 \
+ --hash=sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301
+ # via -r /workspace/requirements-dev-common.in
+build==1.0.3 \
+ --hash=sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b \
+ --hash=sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f
+ # via pip-tools
+certifi==2023.7.22 \
+ --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \
+ --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9
+ # via
+ # httpcore
+ # httpx
+ # requests
+cfgv==3.4.0 \
+ --hash=sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9 \
+ --hash=sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560
+ # via pre-commit
+charset-normalizer==3.3.0 \
+ --hash=sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843 \
+ --hash=sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786 \
+ --hash=sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e \
+ --hash=sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8 \
+ --hash=sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4 \
+ --hash=sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa \
+ --hash=sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d \
+ --hash=sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82 \
+ --hash=sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7 \
+ --hash=sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895 \
+ --hash=sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d \
+ --hash=sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a \
+ --hash=sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382 \
+ --hash=sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678 \
+ --hash=sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b \
+ --hash=sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e \
+ --hash=sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741 \
+ --hash=sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4 \
+ --hash=sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596 \
+ --hash=sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9 \
+ --hash=sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69 \
+ --hash=sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c \
+ --hash=sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77 \
+ --hash=sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13 \
+ --hash=sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459 \
+ --hash=sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e \
+ --hash=sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7 \
+ --hash=sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908 \
+ --hash=sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a \
+ --hash=sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f \
+ --hash=sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8 \
+ --hash=sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482 \
+ --hash=sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d \
+ --hash=sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d \
+ --hash=sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545 \
+ --hash=sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34 \
+ --hash=sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86 \
+ --hash=sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6 \
+ --hash=sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe \
+ --hash=sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e \
+ --hash=sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc \
+ --hash=sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7 \
+ --hash=sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd \
+ --hash=sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c \
+ --hash=sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557 \
+ --hash=sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a \
+ --hash=sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89 \
+ --hash=sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078 \
+ --hash=sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e \
+ --hash=sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4 \
+ --hash=sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403 \
+ --hash=sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0 \
+ --hash=sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89 \
+ --hash=sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115 \
+ --hash=sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9 \
+ --hash=sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05 \
+ --hash=sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a \
+ --hash=sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec \
+ --hash=sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56 \
+ --hash=sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38 \
+ --hash=sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479 \
+ --hash=sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c \
+ --hash=sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e \
+ --hash=sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd \
+ --hash=sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186 \
+ --hash=sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455 \
+ --hash=sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c \
+ --hash=sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65 \
+ --hash=sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78 \
+ --hash=sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287 \
+ --hash=sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df \
+ --hash=sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43 \
+ --hash=sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1 \
+ --hash=sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7 \
+ --hash=sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989 \
+ --hash=sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a \
+ --hash=sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63 \
+ --hash=sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884 \
+ --hash=sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649 \
+ --hash=sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810 \
+ --hash=sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828 \
+ --hash=sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4 \
+ --hash=sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2 \
+ --hash=sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd \
+ --hash=sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5 \
+ --hash=sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe \
+ --hash=sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293 \
+ --hash=sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e \
+ --hash=sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e \
+ --hash=sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8
+ # via requests
+click==8.1.7 \
+ --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
+ --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
+ # via
+ # -r /workspace/requirements-dev-common.in
+ # black
+ # pip-tools
+ # typer
+ # uvicorn
+coverage[toml]==7.3.2 \
+ --hash=sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1 \
+ --hash=sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63 \
+ --hash=sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9 \
+ --hash=sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312 \
+ --hash=sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3 \
+ --hash=sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb \
+ --hash=sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25 \
+ --hash=sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92 \
+ --hash=sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda \
+ --hash=sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148 \
+ --hash=sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6 \
+ --hash=sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216 \
+ --hash=sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a \
+ --hash=sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640 \
+ --hash=sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836 \
+ --hash=sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c \
+ --hash=sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f \
+ --hash=sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2 \
+ --hash=sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901 \
+ --hash=sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed \
+ --hash=sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a \
+ --hash=sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074 \
+ --hash=sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc \
+ --hash=sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84 \
+ --hash=sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083 \
+ --hash=sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f \
+ --hash=sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c \
+ --hash=sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c \
+ --hash=sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637 \
+ --hash=sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2 \
+ --hash=sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82 \
+ --hash=sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f \
+ --hash=sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce \
+ --hash=sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef \
+ --hash=sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f \
+ --hash=sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611 \
+ --hash=sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c \
+ --hash=sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76 \
+ --hash=sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9 \
+ --hash=sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce \
+ --hash=sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9 \
+ --hash=sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf \
+ --hash=sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf \
+ --hash=sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9 \
+ --hash=sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6 \
+ --hash=sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2 \
+ --hash=sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a \
+ --hash=sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a \
+ --hash=sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf \
+ --hash=sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738 \
+ --hash=sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a \
+ --hash=sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4
+ # via pytest-cov
+dependency-injector==4.41.0 \
+ --hash=sha256:02620454ee8101f77a317f3229935ce687480883d72a40858ff4b0c87c935cce \
+ --hash=sha256:059fbb48333148143e8667a5323d162628dfe27c386bd0ed3deeecfc390338bf \
+ --hash=sha256:05e15ea0f2b14c1127e8b0d1597fef13f98845679f63bf670ba12dbfc12a16ef \
+ --hash=sha256:12e91ac0333e7e589421943ff6c6bf9cf0d9ac9703301cec37ccff3723406332 \
+ --hash=sha256:1662e2ef60ac6e681b9e11b5d8b7c17a0f733688916cf695f9540f8f50a61b1e \
+ --hash=sha256:168334cba3f1cbf55299ef38f0f2e31879115cc767b780c859f7814a52d80abb \
+ --hash=sha256:16de2797dcfcc2263b8672bf0751166f7c7b369ca2ff9246ceb67b65f8e1d802 \
+ --hash=sha256:1baee908f21190bdc46a65ce4c417a5175e9397ca62354928694fce218f84487 \
+ --hash=sha256:22b11dbf696e184f0b3d5ac4e5418aeac3c379ba4ea758c04a83869b7e5d1cbf \
+ --hash=sha256:300838e9d4f3fbf539892a5a4072851728e23b37a1f467afcf393edd994d88f0 \
+ --hash=sha256:3055b3fc47a0d6e5f27defb4166c0d37543a4967c279549b154afaf506ce6efc \
+ --hash=sha256:33a724e0a737baadb4378f5dc1b079867cc3a88552fcca719b3dba84716828b2 \
+ --hash=sha256:3535d06416251715b45f8412482b58ec1c6196a4a3baa207f947f0b03a7c4b44 \
+ --hash=sha256:3588bd887b051d16b8bcabaae1127eb14059a0719a8fe34c8a75ba59321b352c \
+ --hash=sha256:3744c327d18408e74781bd6d8b7738745ee80ef89f2c8daecf9ebd098cb84972 \
+ --hash=sha256:37d5954026e3831663518d78bdf4be9c2dbfea691edcb73c813aa3093aa4363a \
+ --hash=sha256:40936d9384363331910abd59dd244158ec3572abf9d37322f15095315ac99893 \
+ --hash=sha256:409441122f40e1b4b8582845fdd76deb9dc5c9d6eb74a057b85736ef9e9c671f \
+ --hash=sha256:48b6886a87b4ceb9b9f78550f77b2a5c7d2ce33bc83efd886556ad468cc9c85a \
+ --hash=sha256:4a31d9d60be4b585585081109480cfb2ef564d3b851cb32a139bf8408411a93a \
+ --hash=sha256:4a44ca3ce5867513a70b31855b218be3d251f5068ce1c480cc3a4ad24ffd3280 \
+ --hash=sha256:51217cb384b468d7cc355544cec20774859f00812f9a1a71ed7fa701c957b2a7 \
+ --hash=sha256:5168dc59808317dc4cdd235aa5d7d556d33e5600156acaf224cead236b48a3e8 \
+ --hash=sha256:54032d62610cf2f4421c9d92cef52957215aaa0bca403cda580c58eb3f726eda \
+ --hash=sha256:56d37b9d2f50a18f059d9abdbea7669a7518bd42b81603c21a27910a2b3f1657 \
+ --hash=sha256:586a0821720b15932addbefb00f7370fbcd5831d6ebbd6494d774b44ff96d23a \
+ --hash=sha256:5fa3ed8f0700e47a0e7363f949b4525ffa8277aa1c5b10ca5b41fce4dea61bb9 \
+ --hash=sha256:63bfba21f8bff654a80e9b9d06dd6c43a442990b73bf89cd471314c11c541ec2 \
+ --hash=sha256:67b369592c57549ccdcad0d5fef1ddb9d39af7fed8083d76e789ab0111fc6389 \
+ --hash=sha256:6b29abac56ce347d2eb58a560723e1663ee2125cf5cc38866ed92b84319927ec \
+ --hash=sha256:6b98945edae88e777091bf0848f869fb94bd76dfa4066d7c870a5caa933391d0 \
+ --hash=sha256:6ee9810841c6e0599356cb884d16453bfca6ab739d0e4f0248724ed8f9ee0d79 \
+ --hash=sha256:740a8e8106a04d3f44b52b25b80570fdac96a8a3934423de7c9202c5623e7936 \
+ --hash=sha256:75280dfa23f7c88e1bf56c3920d58a43516816de6f6ab2a6650bb8a0f27d5c2c \
+ --hash=sha256:75e7a733b372db3144a34020c4233f6b94db2c6342d6d16bc5245b1b941ee2bd \
+ --hash=sha256:76b94c8310929e54136f3cb3de3adc86d1a657b3984299f40bf1cd2ba0bae548 \
+ --hash=sha256:786f7aac592e191c9caafc47732161d807bad65c62f260cd84cd73c7e2d67d6d \
+ --hash=sha256:7a92680bea1c260e5c0d2d6cd60b0c913cba76a456a147db5ac047ecfcfcc758 \
+ --hash=sha256:7dcba8665cafec825b7095d5dd80afb5cf14404450eca3fe8b66e1edbf4dbc10 \
+ --hash=sha256:7fa4970f12a3fc95d8796938b11c41276ad1ff4c447b0e589212eab3fc527a90 \
+ --hash=sha256:87be84084a1b922c4ba15e2e5aa900ee24b78a5467997cb7aec0a1d6cdb4a00b \
+ --hash=sha256:89c67edffe7007cf33cee79ecbca38f48efcc2add5c280717af434db6c789377 \
+ --hash=sha256:8b51efeaebacaf79ef68edfc65e9687699ccffb3538c4a3ab30d0d77e2db7189 \
+ --hash=sha256:8b8cf1c6c56f5c18bdbd9f5e93b52ca29cb4d99606d4056e91f0c761eef496dc \
+ --hash=sha256:8d670a844268dcd758195e58e9a5b39fc74bb8648aba99a13135a4a10ec9cfac \
+ --hash=sha256:8f0090ff14038f17a026ca408a3a0b0e7affb6aa7498b2b59d670f40ac970fbe \
+ --hash=sha256:939dfc657104bc3e66b67afd3fb2ebb0850c9a1e73d0d26066f2bbdd8735ff9c \
+ --hash=sha256:953bfac819d32dc72b963767589e0ed372e5e9e78b03fb6b89419d0500d34bbe \
+ --hash=sha256:99ed73b1521bf249e2823a08a730c9f9413a58f4b4290da022e0ad4fb333ba3d \
+ --hash=sha256:9e3b9d41e0eff4c8e16fea1e33de66ff0030fe51137ca530f3c52ce110447914 \
+ --hash=sha256:a2381a251b04244125148298212550750e6e1403e9b2850cc62e0e829d050ad3 \
+ --hash=sha256:a2dee5d4abdd21f1a30a51d46645c095be9dcc404c7c6e9f81d0a01415a49e64 \
+ --hash=sha256:a4f113e5d4c3070973ad76e5bda7317e500abae6083d78689f0b6e37cf403abf \
+ --hash=sha256:a8686fa330c83251c75c8238697686f7a0e0f6d40658538089165dc72df9bcff \
+ --hash=sha256:ac79f3c05747f9724bd56c06985e78331fc6c85eb50f3e3f1a35e0c60f9977e9 \
+ --hash=sha256:b0c9c966ff66c77364a2d43d08de9968aff7e3903938fe912ba49796b2133344 \
+ --hash=sha256:b2440b32474d4e747209528ca3ae48f42563b2fbe3d74dbfe949c11dfbfef7c4 \
+ --hash=sha256:b365a8548e9a49049fa6acb24d3cd939f619eeb8e300ca3e156e44402dcc07ec \
+ --hash=sha256:b37f36ecb0c1227f697e1d4a029644e3eda8dd0f0716aa63ad04d96dbb15bbbb \
+ --hash=sha256:b3890a12423ae3a9eade035093beba487f8d092ee6c6cb8706f4e7080a56e819 \
+ --hash=sha256:b8b61a15bc46a3aa7b29bd8a7384b650aa3a7ef943491e93c49a0540a0b3dda4 \
+ --hash=sha256:bc852da612c7e347f2fcf921df2eca2718697a49f648a28a63db3ab504fd9510 \
+ --hash=sha256:c71d30b6708438050675f338edb9a25bea6c258478dbe5ec8405286756a2d347 \
+ --hash=sha256:d03f5fa0fa98a18bd0dfce846db80e2798607f0b861f1f99c97f441f7669d7a2 \
+ --hash=sha256:d09c08c944a25dabfb454238c1a889acd85102b93ae497de523bf9ab7947b28a \
+ --hash=sha256:d283aee588a72072439e6721cb64aa6cba5bc18c576ef0ab28285a6ec7a9d655 \
+ --hash=sha256:d557e40673de984f78dab13ebd68d27fbb2f16d7c4e3b663ea2fa2f9fae6765b \
+ --hash=sha256:e3229d83e99e255451605d5276604386e06ad948e3d60f31ddd796781c77f76f \
+ --hash=sha256:f2842e15bae664a9f69932e922b02afa055c91efec959cb1896f6c499bf68180 \
+ --hash=sha256:f89a507e389b7e4d4892dd9a6f5f4da25849e24f73275478634ac594d621ab3f
+ # via hexkit
+deprecation==2.1.0 \
+ --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \
+ --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a
+ # via testcontainers
+distlib==0.3.7 \
+ --hash=sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057 \
+ --hash=sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8
+ # via virtualenv
+dnspython==2.3.0 \
+ --hash=sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9 \
+ --hash=sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46
+ # via
+ # email-validator
+ # ghga-event-schemas
+ # pymongo
+docker==6.1.3 \
+ --hash=sha256:aa6d17830045ba5ef0168d5eaa34d37beeb113948c413affe1d5991fc11f9a20 \
+ --hash=sha256:aecd2277b8bf8e506e484f6ab7aec39abe0038e29fa4a6d3ba86c3fe01844ed9
+ # via testcontainers
+email-validator==2.0.0.post2 \
+ --hash=sha256:1ff6e86044200c56ae23595695c54e9614f4a9551e0e393614f764860b3d7900 \
+ --hash=sha256:2466ba57cda361fb7309fd3d5a225723c788ca4bbad32a0ebd5373b99730285c
+ # via pydantic
+exceptiongroup==1.1.3 \
+ --hash=sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9 \
+ --hash=sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3
+ # via
+ # anyio
+ # pytest
+fastapi==0.96.1 \
+ --hash=sha256:22d773ce95f14f04f8f37a0c8998fc163e67af83b65510d2879de6cbaaa10215 \
+ --hash=sha256:5c1d243030e63089ccfc0aec69c2da6d619943917727e8e82ee502358d5119bf
+ # via ghga-service-commons
+filelock==3.12.4 \
+ --hash=sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4 \
+ --hash=sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd
+ # via virtualenv
+ghga-event-schemas==0.13.5 \
+ --hash=sha256:1ce129c7c969fe2b263483d55c500a3d7f75b6c97e0acb6f49ddd84eca1e1974 \
+ --hash=sha256:c5fb0abab7d06512b2470bb84247473528051d109200ba69a87b6701e3384d6c
+ # via mass (pyproject.toml)
+ghga-service-commons[api]==0.7.1 \
+ --hash=sha256:70a6bc39d3ab8309524dfbcd4c29e5f37de118c41107fb42054834e5dcf8decc \
+ --hash=sha256:888a789cbea86bcd7433e0b848c137928eea87bd9194de3504a3c25a36ac983c
+ # via mass (pyproject.toml)
+gprof2dot==2022.7.29 \
+ --hash=sha256:45b4d298bd36608fccf9511c3fd88a773f7a1abc04d6cd39445b11ba43133ec5 \
+ --hash=sha256:f165b3851d3c52ee4915eb1bd6cca571e5759823c2cd0f71a79bda93c2dc85d6
+ # via pytest-profiling
+h11==0.14.0 \
+ --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \
+ --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761
+ # via
+ # httpcore
+ # uvicorn
+hexkit[akafka,mongodb]==0.10.2 \
+ --hash=sha256:1495f2bc6ae7423874bc20367dd28555cd15a74ccf5cb4997e0fb8307757987e \
+ --hash=sha256:436ea50e706ab616803eb85a9a9f5e7bec727379b750b966650d6e64d4ea5ef0
+ # via mass (pyproject.toml)
+httpcore==0.16.3 \
+ --hash=sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb \
+ --hash=sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0
+ # via httpx
+httptools==0.6.0 \
+ --hash=sha256:03bfd2ae8a2d532952ac54445a2fb2504c804135ed28b53fefaf03d3a93eb1fd \
+ --hash=sha256:0781fedc610293a2716bc7fa142d4c85e6776bc59d617a807ff91246a95dea35 \
+ --hash=sha256:0d0b0571806a5168013b8c3d180d9f9d6997365a4212cb18ea20df18b938aa0b \
+ --hash=sha256:0fb4a608c631f7dcbdf986f40af7a030521a10ba6bc3d36b28c1dc9e9035a3c0 \
+ --hash=sha256:22c01fcd53648162730a71c42842f73b50f989daae36534c818b3f5050b54589 \
+ --hash=sha256:23b09537086a5a611fad5696fc8963d67c7e7f98cb329d38ee114d588b0b74cd \
+ --hash=sha256:259920bbae18740a40236807915def554132ad70af5067e562f4660b62c59b90 \
+ --hash=sha256:26326e0a8fe56829f3af483200d914a7cd16d8d398d14e36888b56de30bec81a \
+ --hash=sha256:274bf20eeb41b0956e34f6a81f84d26ed57c84dd9253f13dcb7174b27ccd8aaf \
+ --hash=sha256:33eb1d4e609c835966e969a31b1dedf5ba16b38cab356c2ce4f3e33ffa94cad3 \
+ --hash=sha256:35a541579bed0270d1ac10245a3e71e5beeb1903b5fbbc8d8b4d4e728d48ff1d \
+ --hash=sha256:38f3cafedd6aa20ae05f81f2e616ea6f92116c8a0f8dcb79dc798df3356836e2 \
+ --hash=sha256:3f96d2a351b5625a9fd9133c95744e8ca06f7a4f8f0b8231e4bbaae2c485046a \
+ --hash=sha256:463c3bc5ef64b9cf091be9ac0e0556199503f6e80456b790a917774a616aff6e \
+ --hash=sha256:47043a6e0ea753f006a9d0dd076a8f8c99bc0ecae86a0888448eb3076c43d717 \
+ --hash=sha256:4e748fc0d5c4a629988ef50ac1aef99dfb5e8996583a73a717fc2cac4ab89932 \
+ --hash=sha256:5dcc14c090ab57b35908d4a4585ec5c0715439df07be2913405991dbb37e049d \
+ --hash=sha256:65d802e7b2538a9756df5acc062300c160907b02e15ed15ba035b02bce43e89c \
+ --hash=sha256:6bdc6675ec6cb79d27e0575750ac6e2b47032742e24eed011b8db73f2da9ed40 \
+ --hash=sha256:6e22896b42b95b3237eccc42278cd72c0df6f23247d886b7ded3163452481e38 \
+ --hash=sha256:721e503245d591527cddd0f6fd771d156c509e831caa7a57929b55ac91ee2b51 \
+ --hash=sha256:72205730bf1be875003692ca54a4a7c35fac77b4746008966061d9d41a61b0f5 \
+ --hash=sha256:72ec7c70bd9f95ef1083d14a755f321d181f046ca685b6358676737a5fecd26a \
+ --hash=sha256:73e9d66a5a28b2d5d9fbd9e197a31edd02be310186db423b28e6052472dc8201 \
+ --hash=sha256:818325afee467d483bfab1647a72054246d29f9053fd17cc4b86cda09cc60339 \
+ --hash=sha256:82c723ed5982f8ead00f8e7605c53e55ffe47c47465d878305ebe0082b6a1755 \
+ --hash=sha256:82f228b88b0e8c6099a9c4757ce9fdbb8b45548074f8d0b1f0fc071e35655d1c \
+ --hash=sha256:93f89975465133619aea8b1952bc6fa0e6bad22a447c6d982fc338fbb4c89649 \
+ --hash=sha256:9fc6e409ad38cbd68b177cd5158fc4042c796b82ca88d99ec78f07bed6c6b796 \
+ --hash=sha256:b0a816bb425c116a160fbc6f34cece097fd22ece15059d68932af686520966bd \
+ --hash=sha256:b703d15dbe082cc23266bf5d9448e764c7cb3fcfe7cb358d79d3fd8248673ef9 \
+ --hash=sha256:cf8169e839a0d740f3d3c9c4fa630ac1a5aaf81641a34575ca6773ed7ce041a1 \
+ --hash=sha256:dea66d94e5a3f68c5e9d86e0894653b87d952e624845e0b0e3ad1c733c6cc75d \
+ --hash=sha256:e41ccac9e77cd045f3e4ee0fc62cbf3d54d7d4b375431eb855561f26ee7a9ec4 \
+ --hash=sha256:f959e4770b3fc8ee4dbc3578fd910fab9003e093f20ac8c621452c4d62e517cb
+ # via uvicorn
+httpx==0.23.3 \
+ --hash=sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9 \
+ --hash=sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6
+ # via
+ # -r /workspace/requirements-dev-common.in
+ # ghga-service-commons
+ # pytest-httpx
+identify==2.5.30 \
+ --hash=sha256:afe67f26ae29bab007ec21b03d4114f41316ab9dd15aa8736a167481e108da54 \
+ --hash=sha256:f302a4256a15c849b91cfcdcec052a8ce914634b2f77ae87dad29cd749f2d88d
+ # via pre-commit
+idna==3.4 \
+ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
+ --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
+ # via
+ # anyio
+ # email-validator
+ # requests
+ # rfc3986
+importlib-metadata==6.8.0 \
+ --hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb \
+ --hash=sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743
+ # via build
+iniconfig==2.0.0 \
+ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \
+ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
+ # via pytest
+jsonschema==4.19.1 \
+ --hash=sha256:cd5f1f9ed9444e554b38ba003af06c0a8c2868131e56bfbef0550fb450c0330e \
+ --hash=sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf
+ # via
+ # ghga-event-schemas
+ # hexkit
+jsonschema-specifications==2023.7.1 \
+ --hash=sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1 \
+ --hash=sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb
+ # via jsonschema
+jsonschema2md==1.0.0 \
+ --hash=sha256:01e18693c8dcdd643aa79ce757c5896877c590493422d23fd0bd76562fbc424b \
+ --hash=sha256:1d1333cb6d55a152ce10051b05651bc71122c0ec5e572b3433046825661569d4
+ # via -r /workspace/requirements-dev-common.in
+kafka-python==2.0.2 \
+ --hash=sha256:04dfe7fea2b63726cd6f3e79a2d86e709d608d74406638c5da33a01d45a9d7e3 \
+ --hash=sha256:2d92418c7cb1c298fa6c7f0fb3519b520d0d7526ac6cb7ae2a4fc65a51a94b6e
+ # via aiokafka
+motor==3.1.1 \
+ --hash=sha256:01d93d7c512810dcd85f4d634a7244ba42ff6be7340c869791fe793561e734da \
+ --hash=sha256:a4bdadf8a08ebb186ba16e557ba432aa867f689a42b80f2e9f8b24bbb1604742
+ # via hexkit
+mypy==1.5.1 \
+ --hash=sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315 \
+ --hash=sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0 \
+ --hash=sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373 \
+ --hash=sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a \
+ --hash=sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161 \
+ --hash=sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275 \
+ --hash=sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693 \
+ --hash=sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb \
+ --hash=sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65 \
+ --hash=sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4 \
+ --hash=sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb \
+ --hash=sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243 \
+ --hash=sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14 \
+ --hash=sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4 \
+ --hash=sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1 \
+ --hash=sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a \
+ --hash=sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160 \
+ --hash=sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25 \
+ --hash=sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12 \
+ --hash=sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d \
+ --hash=sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92 \
+ --hash=sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770 \
+ --hash=sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2 \
+ --hash=sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70 \
+ --hash=sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb \
+ --hash=sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5 \
+ --hash=sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f
+ # via -r /workspace/requirements-dev-common.in
+mypy-extensions==1.0.0 \
+ --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
+ --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782
+ # via
+ # -r /workspace/requirements-dev-common.in
+ # black
+ # mypy
+nodeenv==1.8.0 \
+ --hash=sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2 \
+ --hash=sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec
+ # via pre-commit
+packaging==23.2 \
+ --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \
+ --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7
+ # via
+ # aiokafka
+ # black
+ # build
+ # deprecation
+ # docker
+ # pytest
+pathspec==0.11.2 \
+ --hash=sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20 \
+ --hash=sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3
+ # via black
+pip-tools==7.3.0 \
+ --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \
+ --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d
+ # via -r /workspace/requirements-dev-common.in
+platformdirs==3.11.0 \
+ --hash=sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3 \
+ --hash=sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e
+ # via
+ # black
+ # virtualenv
+pluggy==1.3.0 \
+ --hash=sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12 \
+ --hash=sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7
+ # via pytest
+pre-commit==3.4.0 \
+ --hash=sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522 \
+ --hash=sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945
+ # via -r /workspace/requirements-dev-common.in
+pydantic[email]==1.10.6 \
+ --hash=sha256:012c99a9c0d18cfde7469aa1ebff922e24b0c706d03ead96940f5465f2c9cf62 \
+ --hash=sha256:0abd9c60eee6201b853b6c4be104edfba4f8f6c5f3623f8e1dba90634d63eb35 \
+ --hash=sha256:12e837fd320dd30bd625be1b101e3b62edc096a49835392dcf418f1a5ac2b832 \
+ --hash=sha256:163e79386c3547c49366e959d01e37fc30252285a70619ffc1b10ede4758250a \
+ --hash=sha256:189318051c3d57821f7233ecc94708767dd67687a614a4e8f92b4a020d4ffd06 \
+ --hash=sha256:1c84583b9df62522829cbc46e2b22e0ec11445625b5acd70c5681ce09c9b11c4 \
+ --hash=sha256:3091d2eaeda25391405e36c2fc2ed102b48bac4b384d42b2267310abae350ca6 \
+ --hash=sha256:32937835e525d92c98a1512218db4eed9ddc8f4ee2a78382d77f54341972c0e7 \
+ --hash=sha256:3a2be0a0f32c83265fd71a45027201e1278beaa82ea88ea5b345eea6afa9ac7f \
+ --hash=sha256:3ac1cd4deed871dfe0c5f63721e29debf03e2deefa41b3ed5eb5f5df287c7b70 \
+ --hash=sha256:3ce13a558b484c9ae48a6a7c184b1ba0e5588c5525482681db418268e5f86186 \
+ --hash=sha256:415a3f719ce518e95a92effc7ee30118a25c3d032455d13e121e3840985f2efd \
+ --hash=sha256:43cdeca8d30de9a897440e3fb8866f827c4c31f6c73838e3a01a14b03b067b1d \
+ --hash=sha256:476f6674303ae7965730a382a8e8d7fae18b8004b7b69a56c3d8fa93968aa21c \
+ --hash=sha256:4c19eb5163167489cb1e0161ae9220dadd4fc609a42649e7e84a8fa8fff7a80f \
+ --hash=sha256:4ca83739c1263a044ec8b79df4eefc34bbac87191f0a513d00dd47d46e307a65 \
+ --hash=sha256:528dcf7ec49fb5a84bf6fe346c1cc3c55b0e7603c2123881996ca3ad79db5bfc \
+ --hash=sha256:53de12b4608290992a943801d7756f18a37b7aee284b9ffa794ee8ea8153f8e2 \
+ --hash=sha256:587d92831d0115874d766b1f5fddcdde0c5b6c60f8c6111a394078ec227fca6d \
+ --hash=sha256:60184e80aac3b56933c71c48d6181e630b0fbc61ae455a63322a66a23c14731a \
+ --hash=sha256:6195ca908045054dd2d57eb9c39a5fe86409968b8040de8c2240186da0769da7 \
+ --hash=sha256:61f1f08adfaa9cc02e0cbc94f478140385cbd52d5b3c5a657c2fceb15de8d1fb \
+ --hash=sha256:72cb30894a34d3a7ab6d959b45a70abac8a2a93b6480fc5a7bfbd9c935bdc4fb \
+ --hash=sha256:751f008cd2afe812a781fd6aa2fb66c620ca2e1a13b6a2152b1ad51553cb4b77 \
+ --hash=sha256:89f15277d720aa57e173954d237628a8d304896364b9de745dcb722f584812c7 \
+ --hash=sha256:8c32b6bba301490d9bb2bf5f631907803135e8085b6aa3e5fe5a770d46dd0160 \
+ --hash=sha256:acc6783751ac9c9bc4680379edd6d286468a1dc8d7d9906cd6f1186ed682b2b0 \
+ --hash=sha256:b1eb6610330a1dfba9ce142ada792f26bbef1255b75f538196a39e9e90388bf4 \
+ --hash=sha256:b243b564cea2576725e77aeeda54e3e0229a168bc587d536cd69941e6797543d \
+ --hash=sha256:b41822064585fea56d0116aa431fbd5137ce69dfe837b599e310034171996084 \
+ --hash=sha256:bbd5c531b22928e63d0cb1868dee76123456e1de2f1cb45879e9e7a3f3f1779b \
+ --hash=sha256:cf95adb0d1671fc38d8c43dd921ad5814a735e7d9b4d9e437c088002863854fd \
+ --hash=sha256:e277bd18339177daa62a294256869bbe84df1fb592be2716ec62627bb8d7c81d \
+ --hash=sha256:ea4e2a7cb409951988e79a469f609bba998a576e6d7b9791ae5d1e0619e1c0f2 \
+ --hash=sha256:f9289065611c48147c1dd1fd344e9d57ab45f1d99b0fb26c51f1cf72cd9bcd31 \
+ --hash=sha256:fd9b9e98068fa1068edfc9eabde70a7132017bdd4f362f8b4fd0abed79c33083
+ # via
+ # fastapi
+ # ghga-event-schemas
+ # ghga-service-commons
+ # hexkit
+pymongo==4.5.0 \
+ --hash=sha256:076afa0a4a96ca9f77fec0e4a0d241200b3b3a1766f8d7be9a905ecf59a7416b \
+ --hash=sha256:08819da7864f9b8d4a95729b2bea5fffed08b63d3b9c15b4fea47de655766cf5 \
+ --hash=sha256:0a1f26bc1f5ce774d99725773901820dfdfd24e875028da4a0252a5b48dcab5c \
+ --hash=sha256:0f4b125b46fe377984fbaecf2af40ed48b05a4b7676a2ff98999f2016d66b3ec \
+ --hash=sha256:1240edc1a448d4ada4bf1a0e55550b6292420915292408e59159fd8bbdaf8f63 \
+ --hash=sha256:152259f0f1a60f560323aacf463a3642a65a25557683f49cfa08c8f1ecb2395a \
+ --hash=sha256:168172ef7856e20ec024fe2a746bfa895c88b32720138e6438fd765ebd2b62dd \
+ --hash=sha256:1b1d7d9aabd8629a31d63cd106d56cca0e6420f38e50563278b520f385c0d86e \
+ --hash=sha256:1d40ad09d9f5e719bc6f729cc6b17f31c0b055029719406bd31dde2f72fca7e7 \
+ --hash=sha256:21b953da14549ff62ea4ae20889c71564328958cbdf880c64a92a48dda4c9c53 \
+ --hash=sha256:23cc6d7eb009c688d70da186b8f362d61d5dd1a2c14a45b890bd1e91e9c451f2 \
+ --hash=sha256:2988ef5e6b360b3ff1c6d55c53515499de5f48df31afd9f785d788cdacfbe2d3 \
+ --hash=sha256:2a0aade2b11dc0c326ccd429ee4134d2d47459ff68d449c6d7e01e74651bd255 \
+ --hash=sha256:2b0176f9233a5927084c79ff80b51bd70bfd57e4f3d564f50f80238e797f0c8a \
+ --hash=sha256:2d4fa1b01fa7e5b7bb8d312e3542e211b320eb7a4e3d8dc884327039d93cb9e0 \
+ --hash=sha256:3236cf89d69679eaeb9119c840f5c7eb388a2110b57af6bb6baf01a1da387c18 \
+ --hash=sha256:33faa786cc907de63f745f587e9879429b46033d7d97a7b84b37f4f8f47b9b32 \
+ --hash=sha256:37df8f6006286a5896d1cbc3efb8471ced42e3568d38e6cb00857277047b0d63 \
+ --hash=sha256:3a7166d57dc74d679caa7743b8ecf7dc3a1235a9fd178654dddb2b2a627ae229 \
+ --hash=sha256:3d79ae3bb1ff041c0db56f138c88ce1dfb0209f3546d8d6e7c3f74944ecd2439 \
+ --hash=sha256:3e33064f1984db412b34d51496f4ea785a9cff621c67de58e09fb28da6468a52 \
+ --hash=sha256:3fa3648e4f1e63ddfe53563ee111079ea3ab35c3b09cd25bc22dadc8269a495f \
+ --hash=sha256:40d5f6e853ece9bfc01e9129b228df446f49316a4252bb1fbfae5c3c9dedebad \
+ --hash=sha256:41771b22dd2822540f79a877c391283d4e6368125999a5ec8beee1ce566f3f82 \
+ --hash=sha256:435228d3c16a375274ac8ab9c4f9aef40c5e57ddb8296e20ecec9e2461da1017 \
+ --hash=sha256:44ee985194c426ddf781fa784f31ffa29cb59657b2dba09250a4245431847d73 \
+ --hash=sha256:465fd5b040206f8bce7016b01d7e7f79d2fcd7c2b8e41791be9632a9df1b4999 \
+ --hash=sha256:496c9cbcb4951183d4503a9d7d2c1e3694aab1304262f831d5e1917e60386036 \
+ --hash=sha256:49dce6957598975d8b8d506329d2a3a6c4aee911fa4bbcf5e52ffc6897122950 \
+ --hash=sha256:4c42748ccc451dfcd9cef6c5447a7ab727351fd9747ad431db5ebb18a9b78a4d \
+ --hash=sha256:505f8519c4c782a61d94a17b0da50be639ec462128fbd10ab0a34889218fdee3 \
+ --hash=sha256:53f2dda54d76a98b43a410498bd12f6034b2a14b6844ca08513733b2b20b7ad8 \
+ --hash=sha256:56320c401f544d762fc35766936178fbceb1d9261cd7b24fbfbc8fb6f67aa8a5 \
+ --hash=sha256:58a63a26a1e3dc481dd3a18d6d9f8bd1d576cd1ffe0d479ba7dd38b0aeb20066 \
+ --hash=sha256:5caee7bd08c3d36ec54617832b44985bd70c4cbd77c5b313de6f7fce0bb34f93 \
+ --hash=sha256:631492573a1bef2f74f9ac0f9d84e0ce422c251644cd81207530af4aa2ee1980 \
+ --hash=sha256:63d8019eee119df308a075b8a7bdb06d4720bf791e2b73d5ab0e7473c115d79c \
+ --hash=sha256:6422b6763b016f2ef2beedded0e546d6aa6ba87910f9244d86e0ac7690f75c96 \
+ --hash=sha256:681f252e43b3ef054ca9161635f81b730f4d8cadd28b3f2b2004f5a72f853982 \
+ --hash=sha256:6d64878d1659d2a5bdfd0f0a4d79bafe68653c573681495e424ab40d7b6d6d41 \
+ --hash=sha256:74c0da07c04d0781490b2915e7514b1adb265ef22af039a947988c331ee7455b \
+ --hash=sha256:7591a3beea6a9a4fa3080d27d193b41f631130e3ffa76b88c9ccea123f26dc59 \
+ --hash=sha256:76a262c41c1a7cbb84a3b11976578a7eb8e788c4b7bfbd15c005fb6ca88e6e50 \
+ --hash=sha256:77cfff95c1fafd09e940b3fdcb7b65f11442662fad611d0e69b4dd5d17a81c60 \
+ --hash=sha256:8027c9063579083746147cf401a7072a9fb6829678076cd3deff28bb0e0f50c8 \
+ --hash=sha256:80a167081c75cf66b32f30e2f1eaee9365af935a86dbd76788169911bed9b5d5 \
+ --hash=sha256:840eaf30ccac122df260b6005f9dfae4ac287c498ee91e3e90c56781614ca238 \
+ --hash=sha256:8543253adfaa0b802bfa88386db1009c6ebb7d5684d093ee4edc725007553d21 \
+ --hash=sha256:89b3f2da57a27913d15d2a07d58482f33d0a5b28abd20b8e643ab4d625e36257 \
+ --hash=sha256:8e559116e4128630ad3b7e788e2e5da81cbc2344dee246af44471fa650486a70 \
+ --hash=sha256:9aff6279e405dc953eeb540ab061e72c03cf38119613fce183a8e94f31be608f \
+ --hash=sha256:9c04b9560872fa9a91251030c488e0a73bce9321a70f991f830c72b3f8115d0d \
+ --hash=sha256:9d2346b00af524757576cc2406414562cced1d4349c92166a0ee377a2a483a80 \
+ --hash=sha256:a253b765b7cbc4209f1d8ee16c7287c4268d3243070bf72d7eec5aa9dfe2a2c2 \
+ --hash=sha256:a8127437ebc196a6f5e8fddd746bd0903a400dc6b5ae35df672dd1ccc7170a2a \
+ --hash=sha256:b25f7bea162b3dbec6d33c522097ef81df7c19a9300722fa6853f5b495aecb77 \
+ --hash=sha256:b33c17d9e694b66d7e96977e9e56df19d662031483efe121a24772a44ccbbc7e \
+ --hash=sha256:b4fe46b58010115514b842c669a0ed9b6a342017b15905653a5b1724ab80917f \
+ --hash=sha256:b520aafc6cb148bac09ccf532f52cbd31d83acf4d3e5070d84efe3c019a1adbf \
+ --hash=sha256:b5bbb87fa0511bd313d9a2c90294c88db837667c2bda2ea3fa7a35b59fd93b1f \
+ --hash=sha256:b6d2a56fc2354bb6378f3634402eec788a8f3facf0b3e7d468db5f2b5a78d763 \
+ --hash=sha256:bbd705d5f3c3d1ff2d169e418bb789ff07ab3c70d567cc6ba6b72b04b9143481 \
+ --hash=sha256:bc5d8c3647b8ae28e4312f1492b8f29deebd31479cd3abaa989090fb1d66db83 \
+ --hash=sha256:c3c3525ea8658ee1192cdddf5faf99b07ebe1eeaa61bf32821126df6d1b8072b \
+ --hash=sha256:c9a9a39b7cac81dca79fca8c2a6479ef4c7b1aab95fad7544cc0e8fd943595a2 \
+ --hash=sha256:cd4c8d6aa91d3e35016847cbe8d73106e3d1c9a4e6578d38e2c346bfe8edb3ca \
+ --hash=sha256:cf62da7a4cdec9a4b2981fcbd5e08053edffccf20e845c0b6ec1e77eb7fab61d \
+ --hash=sha256:d67225f05f6ea27c8dc57f3fa6397c96d09c42af69d46629f71e82e66d33fa4f \
+ --hash=sha256:dfcd2b9f510411de615ccedd47462dae80e82fdc09fe9ab0f0f32f11cf57eeb5 \
+ --hash=sha256:e1f61355c821e870fb4c17cdb318669cfbcf245a291ce5053b41140870c3e5cc \
+ --hash=sha256:e249190b018d63c901678053b4a43e797ca78b93fb6d17633e3567d4b3ec6107 \
+ --hash=sha256:e2654d1278384cff75952682d17c718ecc1ad1d6227bb0068fd826ba47d426a5 \
+ --hash=sha256:e57d859b972c75ee44ea2ef4758f12821243e99de814030f69a3decb2aa86807 \
+ --hash=sha256:e5a27f348909235a106a3903fc8e70f573d89b41d723a500869c6569a391cff7 \
+ --hash=sha256:ead4f19d0257a756b21ac2e0e85a37a7245ddec36d3b6008d5bfe416525967dc \
+ --hash=sha256:f076b779aa3dc179aa3ed861be063a313ed4e48ae9f6a8370a9b1295d4502111 \
+ --hash=sha256:f1bb3a62395ffe835dbef3a1cbff48fbcce709c78bd1f52e896aee990928432b \
+ --hash=sha256:f2227a08b091bd41df5aadee0a5037673f691e2aa000e1968b1ea2342afc6880 \
+ --hash=sha256:f3754acbd7efc7f1b529039fcffc092a15e1cf045e31f22f6c9c5950c613ec4d \
+ --hash=sha256:fe48f50fb6348511a3268a893bfd4ab5f263f5ac220782449d03cd05964d1ae7 \
+ --hash=sha256:fff7d17d30b2cd45afd654b3fc117755c5d84506ed25fda386494e4e0a3416e1
+ # via
+ # motor
+ # testcontainers
+pyproject-hooks==1.0.0 \
+ --hash=sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8 \
+ --hash=sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5
+ # via build
+pytest==7.4.2 \
+ --hash=sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002 \
+ --hash=sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069
+ # via
+ # -r /workspace/requirements-dev-common.in
+ # pytest-asyncio
+ # pytest-cov
+ # pytest-httpx
+ # pytest-profiling
+pytest-asyncio==0.21.1 \
+ --hash=sha256:40a7eae6dded22c7b604986855ea48400ab15b069ae38116e8c01238e9eeb64d \
+ --hash=sha256:8666c1c8ac02631d7c51ba282e0c69a8a452b211ffedf2599099845da5c5c37b
+ # via -r /workspace/requirements-dev-common.in
+pytest-cov==4.1.0 \
+ --hash=sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6 \
+ --hash=sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a
+ # via -r /workspace/requirements-dev-common.in
+pytest-httpx==0.21.3 \
+ --hash=sha256:50b52b910f6f6cfb0aa65039d6f5bedb6ae3a0c02a98c4a7187543fe437c428a \
+ --hash=sha256:edcb62baceffbd57753c1a7afc4656b0e71e91c7a512e143c0adbac762d979c1
+ # via -r /workspace/requirements-dev-common.in
+pytest-profiling==1.7.0 \
+ --hash=sha256:93938f147662225d2b8bd5af89587b979652426a8a6ffd7e73ec4a23e24b7f29 \
+ --hash=sha256:999cc9ac94f2e528e3f5d43465da277429984a1c237ae9818f8cfd0b06acb019
+ # via -r /workspace/requirements-dev-common.in
+python-dotenv==1.0.0 \
+ --hash=sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba \
+ --hash=sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a
+ # via uvicorn
+pyyaml==6.0 \
+ --hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf \
+ --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
+ --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \
+ --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \
+ --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \
+ --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \
+ --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \
+ --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \
+ --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \
+ --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \
+ --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \
+ --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \
+ --hash=sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782 \
+ --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \
+ --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \
+ --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \
+ --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \
+ --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \
+ --hash=sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1 \
+ --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \
+ --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \
+ --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \
+ --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \
+ --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \
+ --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \
+ --hash=sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d \
+ --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \
+ --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \
+ --hash=sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7 \
+ --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \
+ --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \
+ --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \
+ --hash=sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358 \
+ --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \
+ --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \
+ --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \
+ --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \
+ --hash=sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f \
+ --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \
+ --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5
+ # via
+ # hexkit
+ # jsonschema2md
+ # pre-commit
+ # uvicorn
+referencing==0.30.2 \
+ --hash=sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf \
+ --hash=sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0
+ # via
+ # jsonschema
+ # jsonschema-specifications
+requests==2.31.0 \
+ --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
+ --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
+ # via
+ # -r /workspace/requirements-dev-common.in
+ # docker
+rfc3986[idna2008]==1.5.0 \
+ --hash=sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835 \
+ --hash=sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97
+ # via httpx
+rpds-py==0.10.4 \
+ --hash=sha256:00a88003db3cc953f8656b59fc9af9d0637a1fb93c235814007988f8c153b2f2 \
+ --hash=sha256:049098dabfe705e9638c55a3321137a821399c50940041a6fcce267a22c70db2 \
+ --hash=sha256:08f07150c8ebbdbce1d2d51b8e9f4d588749a2af6a98035485ebe45c7ad9394e \
+ --hash=sha256:125776d5db15162fdd9135372bef7fe4fb7c5f5810cf25898eb74a06a0816aec \
+ --hash=sha256:13cbd79ccedc6b39c279af31ebfb0aec0467ad5d14641ddb15738bf6e4146157 \
+ --hash=sha256:18d5ff7fbd305a1d564273e9eb22de83ae3cd9cd6329fddc8f12f6428a711a6a \
+ --hash=sha256:1c27942722cd5039bbf5098c7e21935a96243fed00ea11a9589f3c6c6424bd84 \
+ --hash=sha256:255a23bded80605e9f3997753e3a4b89c9aec9efb07ec036b1ca81440efcc1a9 \
+ --hash=sha256:2573ec23ad3a59dd2bc622befac845695972f3f2d08dc1a4405d017d20a6c225 \
+ --hash=sha256:2603e084054351cc65097da326570102c4c5bd07426ba8471ceaefdb0b642cc9 \
+ --hash=sha256:28b4942ec7d9d6114c1e08cace0157db92ef674636a38093cab779ace5742d3a \
+ --hash=sha256:28e29dac59df890972f73c511948072897f512974714a803fe793635b80ff8c7 \
+ --hash=sha256:2a97406d5e08b7095428f01dac0d3c091dc072351151945a167e7968d2755559 \
+ --hash=sha256:2a9e864ec051a58fdb6bb2e6da03942adb20273897bc70067aee283e62bbac4d \
+ --hash=sha256:2e0e2e01c5f61ddf47e3ed2d1fe1c9136e780ca6222d57a2517b9b02afd4710c \
+ --hash=sha256:2e79eeeff8394284b09577f36316d410525e0cf0133abb3de10660e704d3d38e \
+ --hash=sha256:2f2ac8bb01f705c5caaa7fe77ffd9b03f92f1b5061b94228f6ea5eaa0fca68ad \
+ --hash=sha256:32819b662e3b4c26355a4403ea2f60c0a00db45b640fe722dd12db3d2ef807fb \
+ --hash=sha256:3507c459767cf24c11e9520e2a37c89674266abe8e65453e5cb66398aa47ee7b \
+ --hash=sha256:362faeae52dc6ccc50c0b6a01fa2ec0830bb61c292033f3749a46040b876f4ba \
+ --hash=sha256:3650eae998dc718960e90120eb45d42bd57b18b21b10cb9ee05f91bff2345d48 \
+ --hash=sha256:36ff30385fb9fb3ac23a28bffdd4a230a5229ed5b15704b708b7c84bfb7fce51 \
+ --hash=sha256:3bc561c183684636c0099f9c3fbab8c1671841942edbce784bb01b4707d17924 \
+ --hash=sha256:3bd38b80491ef9686f719c1ad3d24d14fbd0e069988fdd4e7d1a6ffcdd7f4a13 \
+ --hash=sha256:3e37f1f134037601eb4b1f46854194f0cc082435dac2ee3de11e51529f7831f2 \
+ --hash=sha256:40f6e53461b19ddbb3354fe5bcf3d50d4333604ae4bf25b478333d83ca68002c \
+ --hash=sha256:49db6c0a0e6626c2b97f5e7f8f7074da21cbd8ec73340c25e839a2457c007efa \
+ --hash=sha256:4bcb1abecd998a72ad4e36a0fca93577fd0c059a6aacc44f16247031b98f6ff4 \
+ --hash=sha256:4cb55454a20d1b935f9eaab52e6ceab624a2efd8b52927c7ae7a43e02828dbe0 \
+ --hash=sha256:4f92d2372ec992c82fd7c74aa21e2a1910b3dcdc6a7e6392919a138f21d528a3 \
+ --hash=sha256:576d48e1e45c211e99fc02655ade65c32a75d3e383ccfd98ce59cece133ed02c \
+ --hash=sha256:58bae860d1d116e6b4e1aad0cdc48a187d5893994f56d26db0c5534df7a47afd \
+ --hash=sha256:5bb3f3cb6072c73e6ec1f865d8b80419b599f1597acf33f63fbf02252aab5a03 \
+ --hash=sha256:5db93f9017b384a4f194e1d89e1ce82d0a41b1fafdbbd3e0c8912baf13f2950f \
+ --hash=sha256:5e41d5b334e8de4bc3f38843f31b2afa9a0c472ebf73119d3fd55cde08974bdf \
+ --hash=sha256:60018626e637528a1fa64bb3a2b3e46ab7bf672052316d61c3629814d5e65052 \
+ --hash=sha256:6090ba604ea06b525a231450ae5d343917a393cbf50423900dea968daf61d16f \
+ --hash=sha256:628fbb8be71a103499d10b189af7764996ab2634ed7b44b423f1e19901606e0e \
+ --hash=sha256:6baea8a4f6f01e69e75cfdef3edd4a4d1c4b56238febbdf123ce96d09fbff010 \
+ --hash=sha256:6c5ca3eb817fb54bfd066740b64a2b31536eb8fe0b183dc35b09a7bd628ed680 \
+ --hash=sha256:70563a1596d2e0660ca2cebb738443437fc0e38597e7cbb276de0a7363924a52 \
+ --hash=sha256:7089d8bfa8064b28b2e39f5af7bf12d42f61caed884e35b9b4ea9e6fb1175077 \
+ --hash=sha256:72e9b1e92830c876cd49565d8404e4dcc9928302d348ea2517bc3f9e3a873a2a \
+ --hash=sha256:7c7ca791bedda059e5195cf7c6b77384657a51429357cdd23e64ac1d4973d6dc \
+ --hash=sha256:7f050ceffd8c730c1619a16bbf0b9cd037dcdb94b54710928ba38c7bde67e4a4 \
+ --hash=sha256:83da147124499fe41ed86edf34b4e81e951b3fe28edcc46288aac24e8a5c8484 \
+ --hash=sha256:86e8d6ff15fa7a9590c0addaf3ce52fb58bda4299cab2c2d0afa404db6848dab \
+ --hash=sha256:8709eb4ab477c533b7d0a76cd3065d7d95c9e25e6b9f6e27caeeb8c63e8799c9 \
+ --hash=sha256:8e69bbe0ede8f7fe2616e779421bbdb37f025c802335a90f6416e4d98b368a37 \
+ --hash=sha256:8f90fc6dd505867514c8b8ef68a712dc0be90031a773c1ae2ad469f04062daef \
+ --hash=sha256:9123ba0f3f98ff79780eebca9984a2b525f88563844b740f94cffb9099701230 \
+ --hash=sha256:927e3461dae0c09b1f2e0066e50c1a9204f8a64a3060f596e9a6742d3b307785 \
+ --hash=sha256:94876c21512535955a960f42a155213315e6ab06a4ce8ce372341a2a1b143eeb \
+ --hash=sha256:98c0aecf661c175ce9cb17347fc51a5c98c3e9189ca57e8fcd9348dae18541db \
+ --hash=sha256:9c7e7bd1fa1f535af71dfcd3700fc83a6dc261a1204f8f5327d8ffe82e52905d \
+ --hash=sha256:9e7b3ad9f53ea9e085b3d27286dd13f8290969c0a153f8a52c8b5c46002c374b \
+ --hash=sha256:9f9184744fb800c9f28e155a5896ecb54816296ee79d5d1978be6a2ae60f53c4 \
+ --hash=sha256:a3628815fd170a64624001bfb4e28946fd515bd672e68a1902d9e0290186eaf3 \
+ --hash=sha256:a5c330cb125983c5d380fef4a4155248a276297c86d64625fdaf500157e1981c \
+ --hash=sha256:aa45cc71bf23a3181b8aa62466b5a2b7b7fb90fdc01df67ca433cd4fce7ec94d \
+ --hash=sha256:aab24b9bbaa3d49e666e9309556591aa00748bd24ea74257a405f7fed9e8b10d \
+ --hash=sha256:ac83f5228459b84fa6279e4126a53abfdd73cd9cc183947ee5084153880f65d7 \
+ --hash=sha256:ad21c60fc880204798f320387164dcacc25818a7b4ec2a0bf6b6c1d57b007d23 \
+ --hash=sha256:ae8a32ab77a84cc870bbfb60645851ca0f7d58fd251085ad67464b1445d632ca \
+ --hash=sha256:b0f1d336786cb62613c72c00578c98e5bb8cd57b49c5bae5d4ab906ca7872f98 \
+ --hash=sha256:b28b9668a22ca2cfca4433441ba9acb2899624a323787a509a3dc5fbfa79c49d \
+ --hash=sha256:b953d11b544ca5f2705bb77b177d8e17ab1bfd69e0fd99790a11549d2302258c \
+ --hash=sha256:b9d8884d58ea8801e5906a491ab34af975091af76d1a389173db491ee7e316bb \
+ --hash=sha256:ba3246c60303eab3d0e562addf25a983d60bddc36f4d1edc2510f056d19df255 \
+ --hash=sha256:bd0ad98c7d72b0e4cbfe89cdfa12cd07d2fd6ed22864341cdce12b318a383442 \
+ --hash=sha256:bf032367f921201deaecf221d4cc895ea84b3decf50a9c73ee106f961885a0ad \
+ --hash=sha256:c31ecfc53ac03dad4928a1712f3a2893008bfba1b3cde49e1c14ff67faae2290 \
+ --hash=sha256:cbec8e43cace64e63398155dc585dc479a89fef1e57ead06c22d3441e1bd09c3 \
+ --hash=sha256:cc688a59c100f038fa9fec9e4ab457c2e2d1fca350fe7ea395016666f0d0a2dc \
+ --hash=sha256:cd7da2adc721ccf19ac7ec86cae3a4fcaba03d9c477d5bd64ded6e9bb817bf3f \
+ --hash=sha256:cd7e62e7d5bcfa38a62d8397fba6d0428b970ab7954c2197501cd1624f7f0bbb \
+ --hash=sha256:d0f7f77a77c37159c9f417b8dd847f67a29e98c6acb52ee98fc6b91efbd1b2b6 \
+ --hash=sha256:d230fddc60caced271cc038e43e6fb8f4dd6b2dbaa44ac9763f2d76d05b0365a \
+ --hash=sha256:d37f27ad80f742ef82796af3fe091888864958ad0bc8bab03da1830fa00c6004 \
+ --hash=sha256:d5ad7b1a1f6964d19b1a8acfc14bf7864f39587b3e25c16ca04f6cd1815026b3 \
+ --hash=sha256:d81359911c3bb31c899c6a5c23b403bdc0279215e5b3bc0d2a692489fed38632 \
+ --hash=sha256:d98802b78093c7083cc51f83da41a5be5a57d406798c9f69424bd75f8ae0812a \
+ --hash=sha256:db0589e0bf41ff6ce284ab045ca89f27be1adf19e7bce26c2e7de6739a70c18b \
+ --hash=sha256:ddbd113a37307638f94be5ae232a325155fd24dbfae2c56455da8724b471e7be \
+ --hash=sha256:e3ece9aa6d07e18c966f14b4352a4c6f40249f6174d3d2c694c1062e19c6adbb \
+ --hash=sha256:e3f9c9e5dd8eba4768e15f19044e1b5e216929a43a54b4ab329e103aed9f3eda \
+ --hash=sha256:e41824343c2c129599645373992b1ce17720bb8a514f04ff9567031e1c26951e \
+ --hash=sha256:e5dba1c11e089b526379e74f6c636202e4c5bad9a48c7416502b8a5b0d026c91 \
+ --hash=sha256:e791e3d13b14d0a7921804d0efe4d7bd15508bbcf8cb7a0c1ee1a27319a5f033 \
+ --hash=sha256:ec001689402b9104700b50a005c2d3d0218eae90eaa8bdbbd776fe78fe8a74b7 \
+ --hash=sha256:efffa359cc69840c8793f0c05a7b663de6afa7b9078fa6c80309ee38b9db677d \
+ --hash=sha256:f1f191befea279cb9669b57be97ab1785781c8bab805900e95742ebfaa9cbf1d \
+ --hash=sha256:f3331a3684192659fa1090bf2b448db928152fcba08222e58106f44758ef25f7 \
+ --hash=sha256:f40413d2859737ce6d95c29ce2dde0ef7cdc3063b5830ae4342fef5922c3bba7 \
+ --hash=sha256:f7ea49ddf51d5ec0c3cbd95190dd15e077a3153c8d4b22a33da43b5dd2b3c640 \
+ --hash=sha256:f82abb5c5b83dc30e96be99ce76239a030b62a73a13c64410e429660a5602bfd \
+ --hash=sha256:fc20dadb102140dff63529e08ce6f9745dbd36e673ebb2b1c4a63e134bca81c2 \
+ --hash=sha256:fd37ab9a24021821b715478357af1cf369d5a42ac7405e83e5822be00732f463 \
+ --hash=sha256:ffd539d213c1ea2989ab92a5b9371ae7159c8c03cf2bcb9f2f594752f755ecd3
+ # via
+ # jsonschema
+ # referencing
+ruff==0.0.292 \
+ --hash=sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96 \
+ --hash=sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac \
+ --hash=sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade \
+ --hash=sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205 \
+ --hash=sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4 \
+ --hash=sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68 \
+ --hash=sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0 \
+ --hash=sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9 \
+ --hash=sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81 \
+ --hash=sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8 \
+ --hash=sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0 \
+ --hash=sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016 \
+ --hash=sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7 \
+ --hash=sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003 \
+ --hash=sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a \
+ --hash=sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c \
+ --hash=sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4
+ # via -r /workspace/requirements-dev-common.in
+six==1.16.0 \
+ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
+ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
+ # via
+ # dependency-injector
+ # pytest-profiling
+snakeviz==2.2.0 \
+ --hash=sha256:569e2d71c47f80a886aa6e70d6405cb6d30aa3520969ad956b06f824c5f02b8e \
+ --hash=sha256:7bfd00be7ae147eb4a170a471578e1cd3f41f803238958b6b8efcf2c698a6aa9
+ # via -r /workspace/requirements-dev-common.in
+sniffio==1.3.0 \
+ --hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
+ --hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384
+ # via
+ # anyio
+ # httpcore
+ # httpx
+starlette==0.27.0 \
+ --hash=sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75 \
+ --hash=sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91
+ # via fastapi
+stringcase==1.2.0 \
+ --hash=sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008
+ # via -r /workspace/requirements-dev-common.in
+testcontainers[mongo]==3.7.1 \
+ --hash=sha256:7f48cef4bf0ccd78f1a4534d4b701a003a3bace851f24eae58a32f9e3f0aeba0
+ # via -r /workspace/requirements-dev.in
+tomli==2.0.1 \
+ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
+ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
+ # via
+ # -r /workspace/requirements-dev-common.in
+ # black
+ # build
+ # coverage
+ # mypy
+ # pip-tools
+ # pyproject-hooks
+ # pytest
+tomli-w==1.0.0 \
+ --hash=sha256:9f2a07e8be30a0729e533ec968016807069991ae2fd921a78d42f429ae5f4463 \
+ --hash=sha256:f463434305e0336248cac9c2dc8076b707d8a12d019dd349f5c1e382dd1ae1b9
+ # via -r /workspace/requirements-dev-common.in
+tornado==6.3.3 \
+ --hash=sha256:1bd19ca6c16882e4d37368e0152f99c099bad93e0950ce55e71daed74045908f \
+ --hash=sha256:22d3c2fa10b5793da13c807e6fc38ff49a4f6e1e3868b0a6f4164768bb8e20f5 \
+ --hash=sha256:502fba735c84450974fec147340016ad928d29f1e91f49be168c0a4c18181e1d \
+ --hash=sha256:65ceca9500383fbdf33a98c0087cb975b2ef3bfb874cb35b8de8740cf7f41bd3 \
+ --hash=sha256:71a8db65160a3c55d61839b7302a9a400074c9c753040455494e2af74e2501f2 \
+ --hash=sha256:7ac51f42808cca9b3613f51ffe2a965c8525cb1b00b7b2d56828b8045354f76a \
+ --hash=sha256:7d01abc57ea0dbb51ddfed477dfe22719d376119844e33c661d873bf9c0e4a16 \
+ --hash=sha256:805d507b1f588320c26f7f097108eb4023bbaa984d63176d1652e184ba24270a \
+ --hash=sha256:9dc4444c0defcd3929d5c1eb5706cbe1b116e762ff3e0deca8b715d14bf6ec17 \
+ --hash=sha256:ceb917a50cd35882b57600709dd5421a418c29ddc852da8bcdab1f0db33406b0 \
+ --hash=sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe
+ # via snakeviz
+typer==0.9.0 \
+ --hash=sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2 \
+ --hash=sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee
+ # via
+ # -r /workspace/requirements-dev-common.in
+ # mass (pyproject.toml)
+typing-extensions==4.8.0 \
+ --hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \
+ --hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef
+ # via
+ # black
+ # mypy
+ # pydantic
+ # starlette
+ # typer
+urllib3==2.0.6 \
+ --hash=sha256:7a7c7003b000adf9e7ca2a377c9688bbc54ed41b985789ed576570342a375cd2 \
+ --hash=sha256:b19e1a85d206b56d7df1d5e683df4a7725252a964e3993648dd0fb5a1c157564
+ # via
+ # -r /workspace/requirements-dev-common.in
+ # docker
+ # requests
+uvicorn[standard]==0.20.0 \
+ --hash=sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8 \
+ --hash=sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd
+ # via ghga-service-commons
+uvloop==0.17.0 \
+ --hash=sha256:0949caf774b9fcefc7c5756bacbbbd3fc4c05a6b7eebc7c7ad6f825b23998d6d \
+ --hash=sha256:0ddf6baf9cf11a1a22c71487f39f15b2cf78eb5bde7e5b45fbb99e8a9d91b9e1 \
+ --hash=sha256:1436c8673c1563422213ac6907789ecb2b070f5939b9cbff9ef7113f2b531595 \
+ --hash=sha256:23609ca361a7fc587031429fa25ad2ed7242941adec948f9d10c045bfecab06b \
+ --hash=sha256:2a6149e1defac0faf505406259561bc14b034cdf1d4711a3ddcdfbaa8d825a05 \
+ --hash=sha256:2deae0b0fb00a6af41fe60a675cec079615b01d68beb4cc7b722424406b126a8 \
+ --hash=sha256:307958f9fc5c8bb01fad752d1345168c0abc5d62c1b72a4a8c6c06f042b45b20 \
+ --hash=sha256:30babd84706115626ea78ea5dbc7dd8d0d01a2e9f9b306d24ca4ed5796c66ded \
+ --hash=sha256:3378eb62c63bf336ae2070599e49089005771cc651c8769aaad72d1bd9385a7c \
+ --hash=sha256:3d97672dc709fa4447ab83276f344a165075fd9f366a97b712bdd3fee05efae8 \
+ --hash=sha256:3db8de10ed684995a7f34a001f15b374c230f7655ae840964d51496e2f8a8474 \
+ --hash=sha256:3ebeeec6a6641d0adb2ea71dcfb76017602ee2bfd8213e3fcc18d8f699c5104f \
+ --hash=sha256:45cea33b208971e87a31c17622e4b440cac231766ec11e5d22c76fab3bf9df62 \
+ --hash=sha256:6708f30db9117f115eadc4f125c2a10c1a50d711461699a0cbfaa45b9a78e376 \
+ --hash=sha256:68532f4349fd3900b839f588972b3392ee56042e440dd5873dfbbcd2cc67617c \
+ --hash=sha256:6aafa5a78b9e62493539456f8b646f85abc7093dd997f4976bb105537cf2635e \
+ --hash=sha256:7d37dccc7ae63e61f7b96ee2e19c40f153ba6ce730d8ba4d3b4e9738c1dccc1b \
+ --hash=sha256:864e1197139d651a76c81757db5eb199db8866e13acb0dfe96e6fc5d1cf45fc4 \
+ --hash=sha256:8887d675a64cfc59f4ecd34382e5b4f0ef4ae1da37ed665adba0c2badf0d6578 \
+ --hash=sha256:8efcadc5a0003d3a6e887ccc1fb44dec25594f117a94e3127954c05cf144d811 \
+ --hash=sha256:9b09e0f0ac29eee0451d71798878eae5a4e6a91aa275e114037b27f7db72702d \
+ --hash=sha256:a4aee22ece20958888eedbad20e4dbb03c37533e010fb824161b4f05e641f738 \
+ --hash=sha256:a5abddb3558d3f0a78949c750644a67be31e47936042d4f6c888dd6f3c95f4aa \
+ --hash=sha256:c092a2c1e736086d59ac8e41f9c98f26bbf9b9222a76f21af9dfe949b99b2eb9 \
+ --hash=sha256:c686a47d57ca910a2572fddfe9912819880b8765e2f01dc0dd12a9bf8573e539 \
+ --hash=sha256:cbbe908fda687e39afd6ea2a2f14c2c3e43f2ca88e3a11964b297822358d0e6c \
+ --hash=sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718 \
+ --hash=sha256:dbbaf9da2ee98ee2531e0c780455f2841e4675ff580ecf93fe5c48fe733b5667 \
+ --hash=sha256:f1e507c9ee39c61bfddd79714e4f85900656db1aec4d40c6de55648e85c2799c \
+ --hash=sha256:ff3d00b70ce95adce264462c930fbaecb29718ba6563db354608f37e49e09024
+ # via uvicorn
+virtualenv==20.24.5 \
+ --hash=sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b \
+ --hash=sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752
+ # via pre-commit
+watchfiles==0.20.0 \
+ --hash=sha256:007dcc4a401093010b389c044e81172c8a2520dba257c88f8828b3d460c6bb38 \
+ --hash=sha256:08dc702529bb06a2b23859110c214db245455532da5eaea602921687cfcd23db \
+ --hash=sha256:0d82dbc1832da83e441d112069833eedd4cf583d983fb8dd666fbefbea9d99c0 \
+ --hash=sha256:13f995d5152a8ba4ed7c2bbbaeee4e11a5944defc7cacd0ccb4dcbdcfd78029a \
+ --hash=sha256:3796312bd3587e14926013612b23066912cf45a14af71cf2b20db1c12dadf4e9 \
+ --hash=sha256:5392dd327a05f538c56edb1c6ebba6af91afc81b40822452342f6da54907bbdf \
+ --hash=sha256:570848706440373b4cd8017f3e850ae17f76dbdf1e9045fc79023b11e1afe490 \
+ --hash=sha256:608cd94a8767f49521901aff9ae0c92cc8f5a24d528db7d6b0295290f9d41193 \
+ --hash=sha256:728575b6b94c90dd531514677201e8851708e6e4b5fe7028ac506a200b622019 \
+ --hash=sha256:7d4e66a857621584869cfbad87039e65dadd7119f0d9bb9dbc957e089e32c164 \
+ --hash=sha256:835df2da7a5df5464c4a23b2d963e1a9d35afa422c83bf4ff4380b3114603644 \
+ --hash=sha256:87d9e1f75c4f86c93d73b5bd1ebe667558357548f11b4f8af4e0e272f79413ce \
+ --hash=sha256:89d1de8218874925bce7bb2ae9657efc504411528930d7a83f98b1749864f2ef \
+ --hash=sha256:99f4c65fd2fce61a571b2a6fcf747d6868db0bef8a934e8ca235cc8533944d95 \
+ --hash=sha256:9a0351d20d03c6f7ad6b2e8a226a5efafb924c7755ee1e34f04c77c3682417fa \
+ --hash=sha256:9b5c8d3be7b502f8c43a33c63166ada8828dbb0c6d49c8f9ce990a96de2f5a49 \
+ --hash=sha256:a03d1e6feb7966b417f43c3e3783188167fd69c2063e86bad31e62c4ea794cc5 \
+ --hash=sha256:b17d4176c49d207865630da5b59a91779468dd3e08692fe943064da260de2c7c \
+ --hash=sha256:d0002d81c89a662b595645fb684a371b98ff90a9c7d8f8630c82f0fde8310458 \
+ --hash=sha256:d97db179f7566dcf145c5179ddb2ae2a4450e3a634eb864b09ea04e68c252e8e \
+ --hash=sha256:e43af4464daa08723c04b43cf978ab86cc55c684c16172622bdac64b34e36af0 \
+ --hash=sha256:eccc8942bcdc7d638a01435d915b913255bbd66f018f1af051cd8afddb339ea3
+ # via uvicorn
+websocket-client==1.6.4 \
+ --hash=sha256:084072e0a7f5f347ef2ac3d8698a5e0b4ffbfcab607628cadabc650fc9a83a24 \
+ --hash=sha256:b3324019b3c28572086c4a319f91d1dcd44e6e11cd340232978c684a7650d0df
+ # via docker
+websockets==11.0.3 \
+ --hash=sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd \
+ --hash=sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f \
+ --hash=sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998 \
+ --hash=sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82 \
+ --hash=sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788 \
+ --hash=sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa \
+ --hash=sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f \
+ --hash=sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4 \
+ --hash=sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7 \
+ --hash=sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f \
+ --hash=sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd \
+ --hash=sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69 \
+ --hash=sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb \
+ --hash=sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b \
+ --hash=sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016 \
+ --hash=sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac \
+ --hash=sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4 \
+ --hash=sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb \
+ --hash=sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99 \
+ --hash=sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e \
+ --hash=sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54 \
+ --hash=sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf \
+ --hash=sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007 \
+ --hash=sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3 \
+ --hash=sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6 \
+ --hash=sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86 \
+ --hash=sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1 \
+ --hash=sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61 \
+ --hash=sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11 \
+ --hash=sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8 \
+ --hash=sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f \
+ --hash=sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931 \
+ --hash=sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526 \
+ --hash=sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016 \
+ --hash=sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae \
+ --hash=sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd \
+ --hash=sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b \
+ --hash=sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311 \
+ --hash=sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af \
+ --hash=sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152 \
+ --hash=sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288 \
+ --hash=sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de \
+ --hash=sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97 \
+ --hash=sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d \
+ --hash=sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d \
+ --hash=sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca \
+ --hash=sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0 \
+ --hash=sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9 \
+ --hash=sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b \
+ --hash=sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e \
+ --hash=sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128 \
+ --hash=sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d \
+ --hash=sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c \
+ --hash=sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5 \
+ --hash=sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6 \
+ --hash=sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b \
+ --hash=sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b \
+ --hash=sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280 \
+ --hash=sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c \
+ --hash=sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c \
+ --hash=sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f \
+ --hash=sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20 \
+ --hash=sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8 \
+ --hash=sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb \
+ --hash=sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602 \
+ --hash=sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf \
+ --hash=sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0 \
+ --hash=sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74 \
+ --hash=sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0 \
+ --hash=sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564
+ # via uvicorn
+wheel==0.41.2 \
+ --hash=sha256:0c5ac5ff2afb79ac23ab82bab027a0be7b5dbcf2e54dc50efe4bf507de1f7985 \
+ --hash=sha256:75909db2664838d015e3d9139004ee16711748a52c8f336b52882266540215d8
+ # via pip-tools
+wrapt==1.15.0 \
+ --hash=sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0 \
+ --hash=sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420 \
+ --hash=sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a \
+ --hash=sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c \
+ --hash=sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079 \
+ --hash=sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923 \
+ --hash=sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f \
+ --hash=sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1 \
+ --hash=sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8 \
+ --hash=sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86 \
+ --hash=sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0 \
+ --hash=sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364 \
+ --hash=sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e \
+ --hash=sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c \
+ --hash=sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e \
+ --hash=sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c \
+ --hash=sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727 \
+ --hash=sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff \
+ --hash=sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e \
+ --hash=sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29 \
+ --hash=sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7 \
+ --hash=sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72 \
+ --hash=sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475 \
+ --hash=sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a \
+ --hash=sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317 \
+ --hash=sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2 \
+ --hash=sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd \
+ --hash=sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640 \
+ --hash=sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98 \
+ --hash=sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248 \
+ --hash=sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e \
+ --hash=sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d \
+ --hash=sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec \
+ --hash=sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1 \
+ --hash=sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e \
+ --hash=sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9 \
+ --hash=sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92 \
+ --hash=sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb \
+ --hash=sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094 \
+ --hash=sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46 \
+ --hash=sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29 \
+ --hash=sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd \
+ --hash=sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705 \
+ --hash=sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8 \
+ --hash=sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975 \
+ --hash=sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb \
+ --hash=sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e \
+ --hash=sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b \
+ --hash=sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418 \
+ --hash=sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019 \
+ --hash=sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1 \
+ --hash=sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba \
+ --hash=sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6 \
+ --hash=sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2 \
+ --hash=sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3 \
+ --hash=sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7 \
+ --hash=sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752 \
+ --hash=sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416 \
+ --hash=sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f \
+ --hash=sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1 \
+ --hash=sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc \
+ --hash=sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145 \
+ --hash=sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee \
+ --hash=sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a \
+ --hash=sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7 \
+ --hash=sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b \
+ --hash=sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653 \
+ --hash=sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0 \
+ --hash=sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90 \
+ --hash=sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29 \
+ --hash=sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6 \
+ --hash=sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034 \
+ --hash=sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09 \
+ --hash=sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559 \
+ --hash=sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639
+ # via testcontainers
+zipp==3.17.0 \
+ --hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \
+ --hash=sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0
+ # via importlib-metadata
--r requirements-dev-common.txt
-
-# additional requirements can be listed here
-testcontainers[mongo]==3.4.1
+# WARNING: The following packages were not pinned, but pip requires them to be
+# pinned when the requirements file includes hashes and the requirement is not
+# satisfied by a package already installed. Consider using the --allow-unsafe flag.
+# pip
+# setuptools
diff --git a/requirements.txt b/requirements.txt
index 1783b07..460114b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,761 @@
-# requirements for complete installation
-
-# the ".[all]" states that requirements including the "all" extra are taken from setup.cfg
-.[all]
+#
+# This file is autogenerated by pip-compile with Python 3.9
+# by the following command:
+#
+# pip-compile --constraint=/workspace/requirements-dev.txt --generate-hashes --output-file=/workspace/requirements.txt /tmp/tmpi5lkji3b/pyproject.toml
+#
+aiokafka==0.8.0 \
+ --hash=sha256:021e9f0027ca63c6c04daccfdd0e985f7a56d51bd0d43f482f674a58fada52f5 \
+ --hash=sha256:0e9d61912678ecae6b3d407107c1a935f21d55af4585b70d8f5dcc39ecb949ce \
+ --hash=sha256:12457b59a06cff7369cace8d4460049b2ef2ab7f7cdc9c4924d577b9d4b48bf9 \
+ --hash=sha256:1c3fd832a06fdd68e82f100fe678a800dd6dbf5e8db6af9521be333f965c325b \
+ --hash=sha256:23f1fbdf54790a3751216e33e62228c8e1eb7feebcb19ef532cd3e4f13ae51ce \
+ --hash=sha256:2567465ee6de4d248fc416f2eef7d33bbe246a79073410ae2368b5bdaeb758c1 \
+ --hash=sha256:2760f095a8ffb5b9b97ad28a43a6a93f38d67cf3bc95b42e6b27462b614c8561 \
+ --hash=sha256:4439a03820dc64a8c3aa5fe17809541e6a001f6f6196aad6b6b88e7ded2b5396 \
+ --hash=sha256:49b30479f68ba9a484a0e3362fb9c48797d7320066db9fcd53e755451f389acb \
+ --hash=sha256:5202073bb8d2350b72805d45ff0125c800ed101506a4ba7be2f03ad1ba8ad1e6 \
+ --hash=sha256:539d8584652e354e7f7bbaf8843e936d91bfc28e224a53a82e1bcb64ac7f6dda \
+ --hash=sha256:57aa55b48004da9bf5a5d37d3412c2d373b0bf32118bdc5c78cc5635998674cc \
+ --hash=sha256:5a8038698a47333cdb0cd198cb4b3ccd2fbbc86ba9a4b9afc3eebe6544db0c2f \
+ --hash=sha256:65e1d27a1c1cd38c66e0b22928af74b192f7598da9acd5bb939c6acea5bb5036 \
+ --hash=sha256:6f50a940411ae6cd0d7bcaf2d821539e3a59b6de012f77de18a573565c9f638f \
+ --hash=sha256:7e292841beda7cfdcd6939aab6cc2a623acd3d655b166f7ff97c658f14ced8c5 \
+ --hash=sha256:881209100355a92696c6501ba1c2b32127bb1f7f2f318b400b3973ab0b52efed \
+ --hash=sha256:8857cbd76e97186e54b98ebb3ea7778fb3618826bb9e4d01375bfab0a1d93d69 \
+ --hash=sha256:9364eb81340b3a70a1222a4701c73a49ea0026a79bf138b4aec342f012d6f039 \
+ --hash=sha256:95682f661f295fac2f5f3f0132aea7c44a1b6c92726161daa509af67ac506885 \
+ --hash=sha256:9bf6d0da5804ae8888c357034d1a6750baa610999181e930678da0e87cec610d \
+ --hash=sha256:b36066df820e30f56386deb56d72efba287ba65419848888ea4b42f9e2741cff \
+ --hash=sha256:b86e3c1d649824103427c021593d75f44e01db1ffbc880b154e04098b534355f \
+ --hash=sha256:d459ab92a360cac240cf10b9ce88e64c1e41d942c7f63b1df6c2aafe27f946d0 \
+ --hash=sha256:dea214c2588237cf0d404624ccd99f466a2e853ca22d7153bb680b2d3f25cdde \
+ --hash=sha256:dfb6dfef6c18726a783d102a6c1b0dfb6d43785a46ff34e967ddfa8f774532dd \
+ --hash=sha256:e005b53597fe9bc6681a2a3b50728d235cf2fb8801e52790678c691c85383565 \
+ --hash=sha256:e07f07290a150552273c02bc5109d0a40bc0f32abc0ae5aeaa1e54fb86369251 \
+ --hash=sha256:ec896d114be157a886e3227bbe3f00658dc4d6f17b203bc44075650817703f0b \
+ --hash=sha256:f0a216a27f05b050d5a5308fb3444014fa6bca5f0cd63468eaa169c5f19ea1dd \
+ --hash=sha256:f3f96301337fa7f7242f46651619b8e9e8fa8f23902dc11416fe764436d662d3
+ # via
+ # -c /workspace/requirements-dev.txt
+ # hexkit
+anyio==4.0.0 \
+ --hash=sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f \
+ --hash=sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a
+ # via
+ # -c /workspace/requirements-dev.txt
+ # httpcore
+ # starlette
+ # watchfiles
+async-timeout==4.0.3 \
+ --hash=sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f \
+ --hash=sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028
+ # via
+ # -c /workspace/requirements-dev.txt
+ # aiokafka
+attrs==23.1.0 \
+ --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
+ --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
+ # via
+ # -c /workspace/requirements-dev.txt
+ # jsonschema
+ # referencing
+certifi==2023.7.22 \
+ --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \
+ --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9
+ # via
+ # -c /workspace/requirements-dev.txt
+ # httpcore
+ # httpx
+click==8.1.7 \
+ --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
+ --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
+ # via
+ # -c /workspace/requirements-dev.txt
+ # typer
+ # uvicorn
+dependency-injector==4.41.0 \
+ --hash=sha256:02620454ee8101f77a317f3229935ce687480883d72a40858ff4b0c87c935cce \
+ --hash=sha256:059fbb48333148143e8667a5323d162628dfe27c386bd0ed3deeecfc390338bf \
+ --hash=sha256:05e15ea0f2b14c1127e8b0d1597fef13f98845679f63bf670ba12dbfc12a16ef \
+ --hash=sha256:12e91ac0333e7e589421943ff6c6bf9cf0d9ac9703301cec37ccff3723406332 \
+ --hash=sha256:1662e2ef60ac6e681b9e11b5d8b7c17a0f733688916cf695f9540f8f50a61b1e \
+ --hash=sha256:168334cba3f1cbf55299ef38f0f2e31879115cc767b780c859f7814a52d80abb \
+ --hash=sha256:16de2797dcfcc2263b8672bf0751166f7c7b369ca2ff9246ceb67b65f8e1d802 \
+ --hash=sha256:1baee908f21190bdc46a65ce4c417a5175e9397ca62354928694fce218f84487 \
+ --hash=sha256:22b11dbf696e184f0b3d5ac4e5418aeac3c379ba4ea758c04a83869b7e5d1cbf \
+ --hash=sha256:300838e9d4f3fbf539892a5a4072851728e23b37a1f467afcf393edd994d88f0 \
+ --hash=sha256:3055b3fc47a0d6e5f27defb4166c0d37543a4967c279549b154afaf506ce6efc \
+ --hash=sha256:33a724e0a737baadb4378f5dc1b079867cc3a88552fcca719b3dba84716828b2 \
+ --hash=sha256:3535d06416251715b45f8412482b58ec1c6196a4a3baa207f947f0b03a7c4b44 \
+ --hash=sha256:3588bd887b051d16b8bcabaae1127eb14059a0719a8fe34c8a75ba59321b352c \
+ --hash=sha256:3744c327d18408e74781bd6d8b7738745ee80ef89f2c8daecf9ebd098cb84972 \
+ --hash=sha256:37d5954026e3831663518d78bdf4be9c2dbfea691edcb73c813aa3093aa4363a \
+ --hash=sha256:40936d9384363331910abd59dd244158ec3572abf9d37322f15095315ac99893 \
+ --hash=sha256:409441122f40e1b4b8582845fdd76deb9dc5c9d6eb74a057b85736ef9e9c671f \
+ --hash=sha256:48b6886a87b4ceb9b9f78550f77b2a5c7d2ce33bc83efd886556ad468cc9c85a \
+ --hash=sha256:4a31d9d60be4b585585081109480cfb2ef564d3b851cb32a139bf8408411a93a \
+ --hash=sha256:4a44ca3ce5867513a70b31855b218be3d251f5068ce1c480cc3a4ad24ffd3280 \
+ --hash=sha256:51217cb384b468d7cc355544cec20774859f00812f9a1a71ed7fa701c957b2a7 \
+ --hash=sha256:5168dc59808317dc4cdd235aa5d7d556d33e5600156acaf224cead236b48a3e8 \
+ --hash=sha256:54032d62610cf2f4421c9d92cef52957215aaa0bca403cda580c58eb3f726eda \
+ --hash=sha256:56d37b9d2f50a18f059d9abdbea7669a7518bd42b81603c21a27910a2b3f1657 \
+ --hash=sha256:586a0821720b15932addbefb00f7370fbcd5831d6ebbd6494d774b44ff96d23a \
+ --hash=sha256:5fa3ed8f0700e47a0e7363f949b4525ffa8277aa1c5b10ca5b41fce4dea61bb9 \
+ --hash=sha256:63bfba21f8bff654a80e9b9d06dd6c43a442990b73bf89cd471314c11c541ec2 \
+ --hash=sha256:67b369592c57549ccdcad0d5fef1ddb9d39af7fed8083d76e789ab0111fc6389 \
+ --hash=sha256:6b29abac56ce347d2eb58a560723e1663ee2125cf5cc38866ed92b84319927ec \
+ --hash=sha256:6b98945edae88e777091bf0848f869fb94bd76dfa4066d7c870a5caa933391d0 \
+ --hash=sha256:6ee9810841c6e0599356cb884d16453bfca6ab739d0e4f0248724ed8f9ee0d79 \
+ --hash=sha256:740a8e8106a04d3f44b52b25b80570fdac96a8a3934423de7c9202c5623e7936 \
+ --hash=sha256:75280dfa23f7c88e1bf56c3920d58a43516816de6f6ab2a6650bb8a0f27d5c2c \
+ --hash=sha256:75e7a733b372db3144a34020c4233f6b94db2c6342d6d16bc5245b1b941ee2bd \
+ --hash=sha256:76b94c8310929e54136f3cb3de3adc86d1a657b3984299f40bf1cd2ba0bae548 \
+ --hash=sha256:786f7aac592e191c9caafc47732161d807bad65c62f260cd84cd73c7e2d67d6d \
+ --hash=sha256:7a92680bea1c260e5c0d2d6cd60b0c913cba76a456a147db5ac047ecfcfcc758 \
+ --hash=sha256:7dcba8665cafec825b7095d5dd80afb5cf14404450eca3fe8b66e1edbf4dbc10 \
+ --hash=sha256:7fa4970f12a3fc95d8796938b11c41276ad1ff4c447b0e589212eab3fc527a90 \
+ --hash=sha256:87be84084a1b922c4ba15e2e5aa900ee24b78a5467997cb7aec0a1d6cdb4a00b \
+ --hash=sha256:89c67edffe7007cf33cee79ecbca38f48efcc2add5c280717af434db6c789377 \
+ --hash=sha256:8b51efeaebacaf79ef68edfc65e9687699ccffb3538c4a3ab30d0d77e2db7189 \
+ --hash=sha256:8b8cf1c6c56f5c18bdbd9f5e93b52ca29cb4d99606d4056e91f0c761eef496dc \
+ --hash=sha256:8d670a844268dcd758195e58e9a5b39fc74bb8648aba99a13135a4a10ec9cfac \
+ --hash=sha256:8f0090ff14038f17a026ca408a3a0b0e7affb6aa7498b2b59d670f40ac970fbe \
+ --hash=sha256:939dfc657104bc3e66b67afd3fb2ebb0850c9a1e73d0d26066f2bbdd8735ff9c \
+ --hash=sha256:953bfac819d32dc72b963767589e0ed372e5e9e78b03fb6b89419d0500d34bbe \
+ --hash=sha256:99ed73b1521bf249e2823a08a730c9f9413a58f4b4290da022e0ad4fb333ba3d \
+ --hash=sha256:9e3b9d41e0eff4c8e16fea1e33de66ff0030fe51137ca530f3c52ce110447914 \
+ --hash=sha256:a2381a251b04244125148298212550750e6e1403e9b2850cc62e0e829d050ad3 \
+ --hash=sha256:a2dee5d4abdd21f1a30a51d46645c095be9dcc404c7c6e9f81d0a01415a49e64 \
+ --hash=sha256:a4f113e5d4c3070973ad76e5bda7317e500abae6083d78689f0b6e37cf403abf \
+ --hash=sha256:a8686fa330c83251c75c8238697686f7a0e0f6d40658538089165dc72df9bcff \
+ --hash=sha256:ac79f3c05747f9724bd56c06985e78331fc6c85eb50f3e3f1a35e0c60f9977e9 \
+ --hash=sha256:b0c9c966ff66c77364a2d43d08de9968aff7e3903938fe912ba49796b2133344 \
+ --hash=sha256:b2440b32474d4e747209528ca3ae48f42563b2fbe3d74dbfe949c11dfbfef7c4 \
+ --hash=sha256:b365a8548e9a49049fa6acb24d3cd939f619eeb8e300ca3e156e44402dcc07ec \
+ --hash=sha256:b37f36ecb0c1227f697e1d4a029644e3eda8dd0f0716aa63ad04d96dbb15bbbb \
+ --hash=sha256:b3890a12423ae3a9eade035093beba487f8d092ee6c6cb8706f4e7080a56e819 \
+ --hash=sha256:b8b61a15bc46a3aa7b29bd8a7384b650aa3a7ef943491e93c49a0540a0b3dda4 \
+ --hash=sha256:bc852da612c7e347f2fcf921df2eca2718697a49f648a28a63db3ab504fd9510 \
+ --hash=sha256:c71d30b6708438050675f338edb9a25bea6c258478dbe5ec8405286756a2d347 \
+ --hash=sha256:d03f5fa0fa98a18bd0dfce846db80e2798607f0b861f1f99c97f441f7669d7a2 \
+ --hash=sha256:d09c08c944a25dabfb454238c1a889acd85102b93ae497de523bf9ab7947b28a \
+ --hash=sha256:d283aee588a72072439e6721cb64aa6cba5bc18c576ef0ab28285a6ec7a9d655 \
+ --hash=sha256:d557e40673de984f78dab13ebd68d27fbb2f16d7c4e3b663ea2fa2f9fae6765b \
+ --hash=sha256:e3229d83e99e255451605d5276604386e06ad948e3d60f31ddd796781c77f76f \
+ --hash=sha256:f2842e15bae664a9f69932e922b02afa055c91efec959cb1896f6c499bf68180 \
+ --hash=sha256:f89a507e389b7e4d4892dd9a6f5f4da25849e24f73275478634ac594d621ab3f
+ # via
+ # -c /workspace/requirements-dev.txt
+ # hexkit
+dnspython==2.3.0 \
+ --hash=sha256:224e32b03eb46be70e12ef6d64e0be123a64e621ab4c0822ff6d450d52a540b9 \
+ --hash=sha256:89141536394f909066cabd112e3e1a37e4e654db00a25308b0f130bc3152eb46
+ # via
+ # -c /workspace/requirements-dev.txt
+ # email-validator
+ # ghga-event-schemas
+ # pymongo
+email-validator==2.0.0.post2 \
+ --hash=sha256:1ff6e86044200c56ae23595695c54e9614f4a9551e0e393614f764860b3d7900 \
+ --hash=sha256:2466ba57cda361fb7309fd3d5a225723c788ca4bbad32a0ebd5373b99730285c
+ # via
+ # -c /workspace/requirements-dev.txt
+ # pydantic
+exceptiongroup==1.1.3 \
+ --hash=sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9 \
+ --hash=sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3
+ # via
+ # -c /workspace/requirements-dev.txt
+ # anyio
+fastapi==0.96.1 \
+ --hash=sha256:22d773ce95f14f04f8f37a0c8998fc163e67af83b65510d2879de6cbaaa10215 \
+ --hash=sha256:5c1d243030e63089ccfc0aec69c2da6d619943917727e8e82ee502358d5119bf
+ # via
+ # -c /workspace/requirements-dev.txt
+ # ghga-service-commons
+ghga-event-schemas==0.13.5 \
+ --hash=sha256:1ce129c7c969fe2b263483d55c500a3d7f75b6c97e0acb6f49ddd84eca1e1974 \
+ --hash=sha256:c5fb0abab7d06512b2470bb84247473528051d109200ba69a87b6701e3384d6c
+ # via
+ # -c /workspace/requirements-dev.txt
+ # mass (pyproject.toml)
+ghga-service-commons[api]==0.7.1 \
+ --hash=sha256:70a6bc39d3ab8309524dfbcd4c29e5f37de118c41107fb42054834e5dcf8decc \
+ --hash=sha256:888a789cbea86bcd7433e0b848c137928eea87bd9194de3504a3c25a36ac983c
+ # via
+ # -c /workspace/requirements-dev.txt
+ # mass (pyproject.toml)
+h11==0.14.0 \
+ --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \
+ --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761
+ # via
+ # -c /workspace/requirements-dev.txt
+ # httpcore
+ # uvicorn
+hexkit[akafka,mongodb]==0.10.2 \
+ --hash=sha256:1495f2bc6ae7423874bc20367dd28555cd15a74ccf5cb4997e0fb8307757987e \
+ --hash=sha256:436ea50e706ab616803eb85a9a9f5e7bec727379b750b966650d6e64d4ea5ef0
+ # via
+ # -c /workspace/requirements-dev.txt
+ # mass (pyproject.toml)
+httpcore==0.16.3 \
+ --hash=sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb \
+ --hash=sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0
+ # via
+ # -c /workspace/requirements-dev.txt
+ # httpx
+httptools==0.6.0 \
+ --hash=sha256:03bfd2ae8a2d532952ac54445a2fb2504c804135ed28b53fefaf03d3a93eb1fd \
+ --hash=sha256:0781fedc610293a2716bc7fa142d4c85e6776bc59d617a807ff91246a95dea35 \
+ --hash=sha256:0d0b0571806a5168013b8c3d180d9f9d6997365a4212cb18ea20df18b938aa0b \
+ --hash=sha256:0fb4a608c631f7dcbdf986f40af7a030521a10ba6bc3d36b28c1dc9e9035a3c0 \
+ --hash=sha256:22c01fcd53648162730a71c42842f73b50f989daae36534c818b3f5050b54589 \
+ --hash=sha256:23b09537086a5a611fad5696fc8963d67c7e7f98cb329d38ee114d588b0b74cd \
+ --hash=sha256:259920bbae18740a40236807915def554132ad70af5067e562f4660b62c59b90 \
+ --hash=sha256:26326e0a8fe56829f3af483200d914a7cd16d8d398d14e36888b56de30bec81a \
+ --hash=sha256:274bf20eeb41b0956e34f6a81f84d26ed57c84dd9253f13dcb7174b27ccd8aaf \
+ --hash=sha256:33eb1d4e609c835966e969a31b1dedf5ba16b38cab356c2ce4f3e33ffa94cad3 \
+ --hash=sha256:35a541579bed0270d1ac10245a3e71e5beeb1903b5fbbc8d8b4d4e728d48ff1d \
+ --hash=sha256:38f3cafedd6aa20ae05f81f2e616ea6f92116c8a0f8dcb79dc798df3356836e2 \
+ --hash=sha256:3f96d2a351b5625a9fd9133c95744e8ca06f7a4f8f0b8231e4bbaae2c485046a \
+ --hash=sha256:463c3bc5ef64b9cf091be9ac0e0556199503f6e80456b790a917774a616aff6e \
+ --hash=sha256:47043a6e0ea753f006a9d0dd076a8f8c99bc0ecae86a0888448eb3076c43d717 \
+ --hash=sha256:4e748fc0d5c4a629988ef50ac1aef99dfb5e8996583a73a717fc2cac4ab89932 \
+ --hash=sha256:5dcc14c090ab57b35908d4a4585ec5c0715439df07be2913405991dbb37e049d \
+ --hash=sha256:65d802e7b2538a9756df5acc062300c160907b02e15ed15ba035b02bce43e89c \
+ --hash=sha256:6bdc6675ec6cb79d27e0575750ac6e2b47032742e24eed011b8db73f2da9ed40 \
+ --hash=sha256:6e22896b42b95b3237eccc42278cd72c0df6f23247d886b7ded3163452481e38 \
+ --hash=sha256:721e503245d591527cddd0f6fd771d156c509e831caa7a57929b55ac91ee2b51 \
+ --hash=sha256:72205730bf1be875003692ca54a4a7c35fac77b4746008966061d9d41a61b0f5 \
+ --hash=sha256:72ec7c70bd9f95ef1083d14a755f321d181f046ca685b6358676737a5fecd26a \
+ --hash=sha256:73e9d66a5a28b2d5d9fbd9e197a31edd02be310186db423b28e6052472dc8201 \
+ --hash=sha256:818325afee467d483bfab1647a72054246d29f9053fd17cc4b86cda09cc60339 \
+ --hash=sha256:82c723ed5982f8ead00f8e7605c53e55ffe47c47465d878305ebe0082b6a1755 \
+ --hash=sha256:82f228b88b0e8c6099a9c4757ce9fdbb8b45548074f8d0b1f0fc071e35655d1c \
+ --hash=sha256:93f89975465133619aea8b1952bc6fa0e6bad22a447c6d982fc338fbb4c89649 \
+ --hash=sha256:9fc6e409ad38cbd68b177cd5158fc4042c796b82ca88d99ec78f07bed6c6b796 \
+ --hash=sha256:b0a816bb425c116a160fbc6f34cece097fd22ece15059d68932af686520966bd \
+ --hash=sha256:b703d15dbe082cc23266bf5d9448e764c7cb3fcfe7cb358d79d3fd8248673ef9 \
+ --hash=sha256:cf8169e839a0d740f3d3c9c4fa630ac1a5aaf81641a34575ca6773ed7ce041a1 \
+ --hash=sha256:dea66d94e5a3f68c5e9d86e0894653b87d952e624845e0b0e3ad1c733c6cc75d \
+ --hash=sha256:e41ccac9e77cd045f3e4ee0fc62cbf3d54d7d4b375431eb855561f26ee7a9ec4 \
+ --hash=sha256:f959e4770b3fc8ee4dbc3578fd910fab9003e093f20ac8c621452c4d62e517cb
+ # via
+ # -c /workspace/requirements-dev.txt
+ # uvicorn
+httpx==0.23.3 \
+ --hash=sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9 \
+ --hash=sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6
+ # via
+ # -c /workspace/requirements-dev.txt
+ # ghga-service-commons
+idna==3.4 \
+ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \
+ --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2
+ # via
+ # -c /workspace/requirements-dev.txt
+ # anyio
+ # email-validator
+ # rfc3986
+jsonschema==4.19.1 \
+ --hash=sha256:cd5f1f9ed9444e554b38ba003af06c0a8c2868131e56bfbef0550fb450c0330e \
+ --hash=sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf
+ # via
+ # -c /workspace/requirements-dev.txt
+ # ghga-event-schemas
+ # hexkit
+jsonschema-specifications==2023.7.1 \
+ --hash=sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1 \
+ --hash=sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb
+ # via
+ # -c /workspace/requirements-dev.txt
+ # jsonschema
+kafka-python==2.0.2 \
+ --hash=sha256:04dfe7fea2b63726cd6f3e79a2d86e709d608d74406638c5da33a01d45a9d7e3 \
+ --hash=sha256:2d92418c7cb1c298fa6c7f0fb3519b520d0d7526ac6cb7ae2a4fc65a51a94b6e
+ # via
+ # -c /workspace/requirements-dev.txt
+ # aiokafka
+motor==3.1.1 \
+ --hash=sha256:01d93d7c512810dcd85f4d634a7244ba42ff6be7340c869791fe793561e734da \
+ --hash=sha256:a4bdadf8a08ebb186ba16e557ba432aa867f689a42b80f2e9f8b24bbb1604742
+ # via
+ # -c /workspace/requirements-dev.txt
+ # hexkit
+packaging==23.2 \
+ --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \
+ --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7
+ # via
+ # -c /workspace/requirements-dev.txt
+ # aiokafka
+pydantic[email]==1.10.6 \
+ --hash=sha256:012c99a9c0d18cfde7469aa1ebff922e24b0c706d03ead96940f5465f2c9cf62 \
+ --hash=sha256:0abd9c60eee6201b853b6c4be104edfba4f8f6c5f3623f8e1dba90634d63eb35 \
+ --hash=sha256:12e837fd320dd30bd625be1b101e3b62edc096a49835392dcf418f1a5ac2b832 \
+ --hash=sha256:163e79386c3547c49366e959d01e37fc30252285a70619ffc1b10ede4758250a \
+ --hash=sha256:189318051c3d57821f7233ecc94708767dd67687a614a4e8f92b4a020d4ffd06 \
+ --hash=sha256:1c84583b9df62522829cbc46e2b22e0ec11445625b5acd70c5681ce09c9b11c4 \
+ --hash=sha256:3091d2eaeda25391405e36c2fc2ed102b48bac4b384d42b2267310abae350ca6 \
+ --hash=sha256:32937835e525d92c98a1512218db4eed9ddc8f4ee2a78382d77f54341972c0e7 \
+ --hash=sha256:3a2be0a0f32c83265fd71a45027201e1278beaa82ea88ea5b345eea6afa9ac7f \
+ --hash=sha256:3ac1cd4deed871dfe0c5f63721e29debf03e2deefa41b3ed5eb5f5df287c7b70 \
+ --hash=sha256:3ce13a558b484c9ae48a6a7c184b1ba0e5588c5525482681db418268e5f86186 \
+ --hash=sha256:415a3f719ce518e95a92effc7ee30118a25c3d032455d13e121e3840985f2efd \
+ --hash=sha256:43cdeca8d30de9a897440e3fb8866f827c4c31f6c73838e3a01a14b03b067b1d \
+ --hash=sha256:476f6674303ae7965730a382a8e8d7fae18b8004b7b69a56c3d8fa93968aa21c \
+ --hash=sha256:4c19eb5163167489cb1e0161ae9220dadd4fc609a42649e7e84a8fa8fff7a80f \
+ --hash=sha256:4ca83739c1263a044ec8b79df4eefc34bbac87191f0a513d00dd47d46e307a65 \
+ --hash=sha256:528dcf7ec49fb5a84bf6fe346c1cc3c55b0e7603c2123881996ca3ad79db5bfc \
+ --hash=sha256:53de12b4608290992a943801d7756f18a37b7aee284b9ffa794ee8ea8153f8e2 \
+ --hash=sha256:587d92831d0115874d766b1f5fddcdde0c5b6c60f8c6111a394078ec227fca6d \
+ --hash=sha256:60184e80aac3b56933c71c48d6181e630b0fbc61ae455a63322a66a23c14731a \
+ --hash=sha256:6195ca908045054dd2d57eb9c39a5fe86409968b8040de8c2240186da0769da7 \
+ --hash=sha256:61f1f08adfaa9cc02e0cbc94f478140385cbd52d5b3c5a657c2fceb15de8d1fb \
+ --hash=sha256:72cb30894a34d3a7ab6d959b45a70abac8a2a93b6480fc5a7bfbd9c935bdc4fb \
+ --hash=sha256:751f008cd2afe812a781fd6aa2fb66c620ca2e1a13b6a2152b1ad51553cb4b77 \
+ --hash=sha256:89f15277d720aa57e173954d237628a8d304896364b9de745dcb722f584812c7 \
+ --hash=sha256:8c32b6bba301490d9bb2bf5f631907803135e8085b6aa3e5fe5a770d46dd0160 \
+ --hash=sha256:acc6783751ac9c9bc4680379edd6d286468a1dc8d7d9906cd6f1186ed682b2b0 \
+ --hash=sha256:b1eb6610330a1dfba9ce142ada792f26bbef1255b75f538196a39e9e90388bf4 \
+ --hash=sha256:b243b564cea2576725e77aeeda54e3e0229a168bc587d536cd69941e6797543d \
+ --hash=sha256:b41822064585fea56d0116aa431fbd5137ce69dfe837b599e310034171996084 \
+ --hash=sha256:bbd5c531b22928e63d0cb1868dee76123456e1de2f1cb45879e9e7a3f3f1779b \
+ --hash=sha256:cf95adb0d1671fc38d8c43dd921ad5814a735e7d9b4d9e437c088002863854fd \
+ --hash=sha256:e277bd18339177daa62a294256869bbe84df1fb592be2716ec62627bb8d7c81d \
+ --hash=sha256:ea4e2a7cb409951988e79a469f609bba998a576e6d7b9791ae5d1e0619e1c0f2 \
+ --hash=sha256:f9289065611c48147c1dd1fd344e9d57ab45f1d99b0fb26c51f1cf72cd9bcd31 \
+ --hash=sha256:fd9b9e98068fa1068edfc9eabde70a7132017bdd4f362f8b4fd0abed79c33083
+ # via
+ # -c /workspace/requirements-dev.txt
+ # fastapi
+ # ghga-event-schemas
+ # ghga-service-commons
+ # hexkit
+pymongo==4.5.0 \
+ --hash=sha256:076afa0a4a96ca9f77fec0e4a0d241200b3b3a1766f8d7be9a905ecf59a7416b \
+ --hash=sha256:08819da7864f9b8d4a95729b2bea5fffed08b63d3b9c15b4fea47de655766cf5 \
+ --hash=sha256:0a1f26bc1f5ce774d99725773901820dfdfd24e875028da4a0252a5b48dcab5c \
+ --hash=sha256:0f4b125b46fe377984fbaecf2af40ed48b05a4b7676a2ff98999f2016d66b3ec \
+ --hash=sha256:1240edc1a448d4ada4bf1a0e55550b6292420915292408e59159fd8bbdaf8f63 \
+ --hash=sha256:152259f0f1a60f560323aacf463a3642a65a25557683f49cfa08c8f1ecb2395a \
+ --hash=sha256:168172ef7856e20ec024fe2a746bfa895c88b32720138e6438fd765ebd2b62dd \
+ --hash=sha256:1b1d7d9aabd8629a31d63cd106d56cca0e6420f38e50563278b520f385c0d86e \
+ --hash=sha256:1d40ad09d9f5e719bc6f729cc6b17f31c0b055029719406bd31dde2f72fca7e7 \
+ --hash=sha256:21b953da14549ff62ea4ae20889c71564328958cbdf880c64a92a48dda4c9c53 \
+ --hash=sha256:23cc6d7eb009c688d70da186b8f362d61d5dd1a2c14a45b890bd1e91e9c451f2 \
+ --hash=sha256:2988ef5e6b360b3ff1c6d55c53515499de5f48df31afd9f785d788cdacfbe2d3 \
+ --hash=sha256:2a0aade2b11dc0c326ccd429ee4134d2d47459ff68d449c6d7e01e74651bd255 \
+ --hash=sha256:2b0176f9233a5927084c79ff80b51bd70bfd57e4f3d564f50f80238e797f0c8a \
+ --hash=sha256:2d4fa1b01fa7e5b7bb8d312e3542e211b320eb7a4e3d8dc884327039d93cb9e0 \
+ --hash=sha256:3236cf89d69679eaeb9119c840f5c7eb388a2110b57af6bb6baf01a1da387c18 \
+ --hash=sha256:33faa786cc907de63f745f587e9879429b46033d7d97a7b84b37f4f8f47b9b32 \
+ --hash=sha256:37df8f6006286a5896d1cbc3efb8471ced42e3568d38e6cb00857277047b0d63 \
+ --hash=sha256:3a7166d57dc74d679caa7743b8ecf7dc3a1235a9fd178654dddb2b2a627ae229 \
+ --hash=sha256:3d79ae3bb1ff041c0db56f138c88ce1dfb0209f3546d8d6e7c3f74944ecd2439 \
+ --hash=sha256:3e33064f1984db412b34d51496f4ea785a9cff621c67de58e09fb28da6468a52 \
+ --hash=sha256:3fa3648e4f1e63ddfe53563ee111079ea3ab35c3b09cd25bc22dadc8269a495f \
+ --hash=sha256:40d5f6e853ece9bfc01e9129b228df446f49316a4252bb1fbfae5c3c9dedebad \
+ --hash=sha256:41771b22dd2822540f79a877c391283d4e6368125999a5ec8beee1ce566f3f82 \
+ --hash=sha256:435228d3c16a375274ac8ab9c4f9aef40c5e57ddb8296e20ecec9e2461da1017 \
+ --hash=sha256:44ee985194c426ddf781fa784f31ffa29cb59657b2dba09250a4245431847d73 \
+ --hash=sha256:465fd5b040206f8bce7016b01d7e7f79d2fcd7c2b8e41791be9632a9df1b4999 \
+ --hash=sha256:496c9cbcb4951183d4503a9d7d2c1e3694aab1304262f831d5e1917e60386036 \
+ --hash=sha256:49dce6957598975d8b8d506329d2a3a6c4aee911fa4bbcf5e52ffc6897122950 \
+ --hash=sha256:4c42748ccc451dfcd9cef6c5447a7ab727351fd9747ad431db5ebb18a9b78a4d \
+ --hash=sha256:505f8519c4c782a61d94a17b0da50be639ec462128fbd10ab0a34889218fdee3 \
+ --hash=sha256:53f2dda54d76a98b43a410498bd12f6034b2a14b6844ca08513733b2b20b7ad8 \
+ --hash=sha256:56320c401f544d762fc35766936178fbceb1d9261cd7b24fbfbc8fb6f67aa8a5 \
+ --hash=sha256:58a63a26a1e3dc481dd3a18d6d9f8bd1d576cd1ffe0d479ba7dd38b0aeb20066 \
+ --hash=sha256:5caee7bd08c3d36ec54617832b44985bd70c4cbd77c5b313de6f7fce0bb34f93 \
+ --hash=sha256:631492573a1bef2f74f9ac0f9d84e0ce422c251644cd81207530af4aa2ee1980 \
+ --hash=sha256:63d8019eee119df308a075b8a7bdb06d4720bf791e2b73d5ab0e7473c115d79c \
+ --hash=sha256:6422b6763b016f2ef2beedded0e546d6aa6ba87910f9244d86e0ac7690f75c96 \
+ --hash=sha256:681f252e43b3ef054ca9161635f81b730f4d8cadd28b3f2b2004f5a72f853982 \
+ --hash=sha256:6d64878d1659d2a5bdfd0f0a4d79bafe68653c573681495e424ab40d7b6d6d41 \
+ --hash=sha256:74c0da07c04d0781490b2915e7514b1adb265ef22af039a947988c331ee7455b \
+ --hash=sha256:7591a3beea6a9a4fa3080d27d193b41f631130e3ffa76b88c9ccea123f26dc59 \
+ --hash=sha256:76a262c41c1a7cbb84a3b11976578a7eb8e788c4b7bfbd15c005fb6ca88e6e50 \
+ --hash=sha256:77cfff95c1fafd09e940b3fdcb7b65f11442662fad611d0e69b4dd5d17a81c60 \
+ --hash=sha256:8027c9063579083746147cf401a7072a9fb6829678076cd3deff28bb0e0f50c8 \
+ --hash=sha256:80a167081c75cf66b32f30e2f1eaee9365af935a86dbd76788169911bed9b5d5 \
+ --hash=sha256:840eaf30ccac122df260b6005f9dfae4ac287c498ee91e3e90c56781614ca238 \
+ --hash=sha256:8543253adfaa0b802bfa88386db1009c6ebb7d5684d093ee4edc725007553d21 \
+ --hash=sha256:89b3f2da57a27913d15d2a07d58482f33d0a5b28abd20b8e643ab4d625e36257 \
+ --hash=sha256:8e559116e4128630ad3b7e788e2e5da81cbc2344dee246af44471fa650486a70 \
+ --hash=sha256:9aff6279e405dc953eeb540ab061e72c03cf38119613fce183a8e94f31be608f \
+ --hash=sha256:9c04b9560872fa9a91251030c488e0a73bce9321a70f991f830c72b3f8115d0d \
+ --hash=sha256:9d2346b00af524757576cc2406414562cced1d4349c92166a0ee377a2a483a80 \
+ --hash=sha256:a253b765b7cbc4209f1d8ee16c7287c4268d3243070bf72d7eec5aa9dfe2a2c2 \
+ --hash=sha256:a8127437ebc196a6f5e8fddd746bd0903a400dc6b5ae35df672dd1ccc7170a2a \
+ --hash=sha256:b25f7bea162b3dbec6d33c522097ef81df7c19a9300722fa6853f5b495aecb77 \
+ --hash=sha256:b33c17d9e694b66d7e96977e9e56df19d662031483efe121a24772a44ccbbc7e \
+ --hash=sha256:b4fe46b58010115514b842c669a0ed9b6a342017b15905653a5b1724ab80917f \
+ --hash=sha256:b520aafc6cb148bac09ccf532f52cbd31d83acf4d3e5070d84efe3c019a1adbf \
+ --hash=sha256:b5bbb87fa0511bd313d9a2c90294c88db837667c2bda2ea3fa7a35b59fd93b1f \
+ --hash=sha256:b6d2a56fc2354bb6378f3634402eec788a8f3facf0b3e7d468db5f2b5a78d763 \
+ --hash=sha256:bbd705d5f3c3d1ff2d169e418bb789ff07ab3c70d567cc6ba6b72b04b9143481 \
+ --hash=sha256:bc5d8c3647b8ae28e4312f1492b8f29deebd31479cd3abaa989090fb1d66db83 \
+ --hash=sha256:c3c3525ea8658ee1192cdddf5faf99b07ebe1eeaa61bf32821126df6d1b8072b \
+ --hash=sha256:c9a9a39b7cac81dca79fca8c2a6479ef4c7b1aab95fad7544cc0e8fd943595a2 \
+ --hash=sha256:cd4c8d6aa91d3e35016847cbe8d73106e3d1c9a4e6578d38e2c346bfe8edb3ca \
+ --hash=sha256:cf62da7a4cdec9a4b2981fcbd5e08053edffccf20e845c0b6ec1e77eb7fab61d \
+ --hash=sha256:d67225f05f6ea27c8dc57f3fa6397c96d09c42af69d46629f71e82e66d33fa4f \
+ --hash=sha256:dfcd2b9f510411de615ccedd47462dae80e82fdc09fe9ab0f0f32f11cf57eeb5 \
+ --hash=sha256:e1f61355c821e870fb4c17cdb318669cfbcf245a291ce5053b41140870c3e5cc \
+ --hash=sha256:e249190b018d63c901678053b4a43e797ca78b93fb6d17633e3567d4b3ec6107 \
+ --hash=sha256:e2654d1278384cff75952682d17c718ecc1ad1d6227bb0068fd826ba47d426a5 \
+ --hash=sha256:e57d859b972c75ee44ea2ef4758f12821243e99de814030f69a3decb2aa86807 \
+ --hash=sha256:e5a27f348909235a106a3903fc8e70f573d89b41d723a500869c6569a391cff7 \
+ --hash=sha256:ead4f19d0257a756b21ac2e0e85a37a7245ddec36d3b6008d5bfe416525967dc \
+ --hash=sha256:f076b779aa3dc179aa3ed861be063a313ed4e48ae9f6a8370a9b1295d4502111 \
+ --hash=sha256:f1bb3a62395ffe835dbef3a1cbff48fbcce709c78bd1f52e896aee990928432b \
+ --hash=sha256:f2227a08b091bd41df5aadee0a5037673f691e2aa000e1968b1ea2342afc6880 \
+ --hash=sha256:f3754acbd7efc7f1b529039fcffc092a15e1cf045e31f22f6c9c5950c613ec4d \
+ --hash=sha256:fe48f50fb6348511a3268a893bfd4ab5f263f5ac220782449d03cd05964d1ae7 \
+ --hash=sha256:fff7d17d30b2cd45afd654b3fc117755c5d84506ed25fda386494e4e0a3416e1
+ # via
+ # -c /workspace/requirements-dev.txt
+ # motor
+python-dotenv==1.0.0 \
+ --hash=sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba \
+ --hash=sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a
+ # via
+ # -c /workspace/requirements-dev.txt
+ # uvicorn
+pyyaml==6.0 \
+ --hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf \
+ --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
+ --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \
+ --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \
+ --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \
+ --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \
+ --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \
+ --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \
+ --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \
+ --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \
+ --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \
+ --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \
+ --hash=sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782 \
+ --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \
+ --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \
+ --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \
+ --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \
+ --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \
+ --hash=sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1 \
+ --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \
+ --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \
+ --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \
+ --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \
+ --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \
+ --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \
+ --hash=sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d \
+ --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \
+ --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \
+ --hash=sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7 \
+ --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \
+ --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \
+ --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \
+ --hash=sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358 \
+ --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \
+ --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \
+ --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \
+ --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \
+ --hash=sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f \
+ --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \
+ --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5
+ # via
+ # -c /workspace/requirements-dev.txt
+ # hexkit
+ # uvicorn
+referencing==0.30.2 \
+ --hash=sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf \
+ --hash=sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0
+ # via
+ # -c /workspace/requirements-dev.txt
+ # jsonschema
+ # jsonschema-specifications
+rfc3986[idna2008]==1.5.0 \
+ --hash=sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835 \
+ --hash=sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97
+ # via
+ # -c /workspace/requirements-dev.txt
+ # httpx
+rpds-py==0.10.4 \
+ --hash=sha256:00a88003db3cc953f8656b59fc9af9d0637a1fb93c235814007988f8c153b2f2 \
+ --hash=sha256:049098dabfe705e9638c55a3321137a821399c50940041a6fcce267a22c70db2 \
+ --hash=sha256:08f07150c8ebbdbce1d2d51b8e9f4d588749a2af6a98035485ebe45c7ad9394e \
+ --hash=sha256:125776d5db15162fdd9135372bef7fe4fb7c5f5810cf25898eb74a06a0816aec \
+ --hash=sha256:13cbd79ccedc6b39c279af31ebfb0aec0467ad5d14641ddb15738bf6e4146157 \
+ --hash=sha256:18d5ff7fbd305a1d564273e9eb22de83ae3cd9cd6329fddc8f12f6428a711a6a \
+ --hash=sha256:1c27942722cd5039bbf5098c7e21935a96243fed00ea11a9589f3c6c6424bd84 \
+ --hash=sha256:255a23bded80605e9f3997753e3a4b89c9aec9efb07ec036b1ca81440efcc1a9 \
+ --hash=sha256:2573ec23ad3a59dd2bc622befac845695972f3f2d08dc1a4405d017d20a6c225 \
+ --hash=sha256:2603e084054351cc65097da326570102c4c5bd07426ba8471ceaefdb0b642cc9 \
+ --hash=sha256:28b4942ec7d9d6114c1e08cace0157db92ef674636a38093cab779ace5742d3a \
+ --hash=sha256:28e29dac59df890972f73c511948072897f512974714a803fe793635b80ff8c7 \
+ --hash=sha256:2a97406d5e08b7095428f01dac0d3c091dc072351151945a167e7968d2755559 \
+ --hash=sha256:2a9e864ec051a58fdb6bb2e6da03942adb20273897bc70067aee283e62bbac4d \
+ --hash=sha256:2e0e2e01c5f61ddf47e3ed2d1fe1c9136e780ca6222d57a2517b9b02afd4710c \
+ --hash=sha256:2e79eeeff8394284b09577f36316d410525e0cf0133abb3de10660e704d3d38e \
+ --hash=sha256:2f2ac8bb01f705c5caaa7fe77ffd9b03f92f1b5061b94228f6ea5eaa0fca68ad \
+ --hash=sha256:32819b662e3b4c26355a4403ea2f60c0a00db45b640fe722dd12db3d2ef807fb \
+ --hash=sha256:3507c459767cf24c11e9520e2a37c89674266abe8e65453e5cb66398aa47ee7b \
+ --hash=sha256:362faeae52dc6ccc50c0b6a01fa2ec0830bb61c292033f3749a46040b876f4ba \
+ --hash=sha256:3650eae998dc718960e90120eb45d42bd57b18b21b10cb9ee05f91bff2345d48 \
+ --hash=sha256:36ff30385fb9fb3ac23a28bffdd4a230a5229ed5b15704b708b7c84bfb7fce51 \
+ --hash=sha256:3bc561c183684636c0099f9c3fbab8c1671841942edbce784bb01b4707d17924 \
+ --hash=sha256:3bd38b80491ef9686f719c1ad3d24d14fbd0e069988fdd4e7d1a6ffcdd7f4a13 \
+ --hash=sha256:3e37f1f134037601eb4b1f46854194f0cc082435dac2ee3de11e51529f7831f2 \
+ --hash=sha256:40f6e53461b19ddbb3354fe5bcf3d50d4333604ae4bf25b478333d83ca68002c \
+ --hash=sha256:49db6c0a0e6626c2b97f5e7f8f7074da21cbd8ec73340c25e839a2457c007efa \
+ --hash=sha256:4bcb1abecd998a72ad4e36a0fca93577fd0c059a6aacc44f16247031b98f6ff4 \
+ --hash=sha256:4cb55454a20d1b935f9eaab52e6ceab624a2efd8b52927c7ae7a43e02828dbe0 \
+ --hash=sha256:4f92d2372ec992c82fd7c74aa21e2a1910b3dcdc6a7e6392919a138f21d528a3 \
+ --hash=sha256:576d48e1e45c211e99fc02655ade65c32a75d3e383ccfd98ce59cece133ed02c \
+ --hash=sha256:58bae860d1d116e6b4e1aad0cdc48a187d5893994f56d26db0c5534df7a47afd \
+ --hash=sha256:5bb3f3cb6072c73e6ec1f865d8b80419b599f1597acf33f63fbf02252aab5a03 \
+ --hash=sha256:5db93f9017b384a4f194e1d89e1ce82d0a41b1fafdbbd3e0c8912baf13f2950f \
+ --hash=sha256:5e41d5b334e8de4bc3f38843f31b2afa9a0c472ebf73119d3fd55cde08974bdf \
+ --hash=sha256:60018626e637528a1fa64bb3a2b3e46ab7bf672052316d61c3629814d5e65052 \
+ --hash=sha256:6090ba604ea06b525a231450ae5d343917a393cbf50423900dea968daf61d16f \
+ --hash=sha256:628fbb8be71a103499d10b189af7764996ab2634ed7b44b423f1e19901606e0e \
+ --hash=sha256:6baea8a4f6f01e69e75cfdef3edd4a4d1c4b56238febbdf123ce96d09fbff010 \
+ --hash=sha256:6c5ca3eb817fb54bfd066740b64a2b31536eb8fe0b183dc35b09a7bd628ed680 \
+ --hash=sha256:70563a1596d2e0660ca2cebb738443437fc0e38597e7cbb276de0a7363924a52 \
+ --hash=sha256:7089d8bfa8064b28b2e39f5af7bf12d42f61caed884e35b9b4ea9e6fb1175077 \
+ --hash=sha256:72e9b1e92830c876cd49565d8404e4dcc9928302d348ea2517bc3f9e3a873a2a \
+ --hash=sha256:7c7ca791bedda059e5195cf7c6b77384657a51429357cdd23e64ac1d4973d6dc \
+ --hash=sha256:7f050ceffd8c730c1619a16bbf0b9cd037dcdb94b54710928ba38c7bde67e4a4 \
+ --hash=sha256:83da147124499fe41ed86edf34b4e81e951b3fe28edcc46288aac24e8a5c8484 \
+ --hash=sha256:86e8d6ff15fa7a9590c0addaf3ce52fb58bda4299cab2c2d0afa404db6848dab \
+ --hash=sha256:8709eb4ab477c533b7d0a76cd3065d7d95c9e25e6b9f6e27caeeb8c63e8799c9 \
+ --hash=sha256:8e69bbe0ede8f7fe2616e779421bbdb37f025c802335a90f6416e4d98b368a37 \
+ --hash=sha256:8f90fc6dd505867514c8b8ef68a712dc0be90031a773c1ae2ad469f04062daef \
+ --hash=sha256:9123ba0f3f98ff79780eebca9984a2b525f88563844b740f94cffb9099701230 \
+ --hash=sha256:927e3461dae0c09b1f2e0066e50c1a9204f8a64a3060f596e9a6742d3b307785 \
+ --hash=sha256:94876c21512535955a960f42a155213315e6ab06a4ce8ce372341a2a1b143eeb \
+ --hash=sha256:98c0aecf661c175ce9cb17347fc51a5c98c3e9189ca57e8fcd9348dae18541db \
+ --hash=sha256:9c7e7bd1fa1f535af71dfcd3700fc83a6dc261a1204f8f5327d8ffe82e52905d \
+ --hash=sha256:9e7b3ad9f53ea9e085b3d27286dd13f8290969c0a153f8a52c8b5c46002c374b \
+ --hash=sha256:9f9184744fb800c9f28e155a5896ecb54816296ee79d5d1978be6a2ae60f53c4 \
+ --hash=sha256:a3628815fd170a64624001bfb4e28946fd515bd672e68a1902d9e0290186eaf3 \
+ --hash=sha256:a5c330cb125983c5d380fef4a4155248a276297c86d64625fdaf500157e1981c \
+ --hash=sha256:aa45cc71bf23a3181b8aa62466b5a2b7b7fb90fdc01df67ca433cd4fce7ec94d \
+ --hash=sha256:aab24b9bbaa3d49e666e9309556591aa00748bd24ea74257a405f7fed9e8b10d \
+ --hash=sha256:ac83f5228459b84fa6279e4126a53abfdd73cd9cc183947ee5084153880f65d7 \
+ --hash=sha256:ad21c60fc880204798f320387164dcacc25818a7b4ec2a0bf6b6c1d57b007d23 \
+ --hash=sha256:ae8a32ab77a84cc870bbfb60645851ca0f7d58fd251085ad67464b1445d632ca \
+ --hash=sha256:b0f1d336786cb62613c72c00578c98e5bb8cd57b49c5bae5d4ab906ca7872f98 \
+ --hash=sha256:b28b9668a22ca2cfca4433441ba9acb2899624a323787a509a3dc5fbfa79c49d \
+ --hash=sha256:b953d11b544ca5f2705bb77b177d8e17ab1bfd69e0fd99790a11549d2302258c \
+ --hash=sha256:b9d8884d58ea8801e5906a491ab34af975091af76d1a389173db491ee7e316bb \
+ --hash=sha256:ba3246c60303eab3d0e562addf25a983d60bddc36f4d1edc2510f056d19df255 \
+ --hash=sha256:bd0ad98c7d72b0e4cbfe89cdfa12cd07d2fd6ed22864341cdce12b318a383442 \
+ --hash=sha256:bf032367f921201deaecf221d4cc895ea84b3decf50a9c73ee106f961885a0ad \
+ --hash=sha256:c31ecfc53ac03dad4928a1712f3a2893008bfba1b3cde49e1c14ff67faae2290 \
+ --hash=sha256:cbec8e43cace64e63398155dc585dc479a89fef1e57ead06c22d3441e1bd09c3 \
+ --hash=sha256:cc688a59c100f038fa9fec9e4ab457c2e2d1fca350fe7ea395016666f0d0a2dc \
+ --hash=sha256:cd7da2adc721ccf19ac7ec86cae3a4fcaba03d9c477d5bd64ded6e9bb817bf3f \
+ --hash=sha256:cd7e62e7d5bcfa38a62d8397fba6d0428b970ab7954c2197501cd1624f7f0bbb \
+ --hash=sha256:d0f7f77a77c37159c9f417b8dd847f67a29e98c6acb52ee98fc6b91efbd1b2b6 \
+ --hash=sha256:d230fddc60caced271cc038e43e6fb8f4dd6b2dbaa44ac9763f2d76d05b0365a \
+ --hash=sha256:d37f27ad80f742ef82796af3fe091888864958ad0bc8bab03da1830fa00c6004 \
+ --hash=sha256:d5ad7b1a1f6964d19b1a8acfc14bf7864f39587b3e25c16ca04f6cd1815026b3 \
+ --hash=sha256:d81359911c3bb31c899c6a5c23b403bdc0279215e5b3bc0d2a692489fed38632 \
+ --hash=sha256:d98802b78093c7083cc51f83da41a5be5a57d406798c9f69424bd75f8ae0812a \
+ --hash=sha256:db0589e0bf41ff6ce284ab045ca89f27be1adf19e7bce26c2e7de6739a70c18b \
+ --hash=sha256:ddbd113a37307638f94be5ae232a325155fd24dbfae2c56455da8724b471e7be \
+ --hash=sha256:e3ece9aa6d07e18c966f14b4352a4c6f40249f6174d3d2c694c1062e19c6adbb \
+ --hash=sha256:e3f9c9e5dd8eba4768e15f19044e1b5e216929a43a54b4ab329e103aed9f3eda \
+ --hash=sha256:e41824343c2c129599645373992b1ce17720bb8a514f04ff9567031e1c26951e \
+ --hash=sha256:e5dba1c11e089b526379e74f6c636202e4c5bad9a48c7416502b8a5b0d026c91 \
+ --hash=sha256:e791e3d13b14d0a7921804d0efe4d7bd15508bbcf8cb7a0c1ee1a27319a5f033 \
+ --hash=sha256:ec001689402b9104700b50a005c2d3d0218eae90eaa8bdbbd776fe78fe8a74b7 \
+ --hash=sha256:efffa359cc69840c8793f0c05a7b663de6afa7b9078fa6c80309ee38b9db677d \
+ --hash=sha256:f1f191befea279cb9669b57be97ab1785781c8bab805900e95742ebfaa9cbf1d \
+ --hash=sha256:f3331a3684192659fa1090bf2b448db928152fcba08222e58106f44758ef25f7 \
+ --hash=sha256:f40413d2859737ce6d95c29ce2dde0ef7cdc3063b5830ae4342fef5922c3bba7 \
+ --hash=sha256:f7ea49ddf51d5ec0c3cbd95190dd15e077a3153c8d4b22a33da43b5dd2b3c640 \
+ --hash=sha256:f82abb5c5b83dc30e96be99ce76239a030b62a73a13c64410e429660a5602bfd \
+ --hash=sha256:fc20dadb102140dff63529e08ce6f9745dbd36e673ebb2b1c4a63e134bca81c2 \
+ --hash=sha256:fd37ab9a24021821b715478357af1cf369d5a42ac7405e83e5822be00732f463 \
+ --hash=sha256:ffd539d213c1ea2989ab92a5b9371ae7159c8c03cf2bcb9f2f594752f755ecd3
+ # via
+ # -c /workspace/requirements-dev.txt
+ # jsonschema
+ # referencing
+six==1.16.0 \
+ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
+ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
+ # via
+ # -c /workspace/requirements-dev.txt
+ # dependency-injector
+sniffio==1.3.0 \
+ --hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \
+ --hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384
+ # via
+ # -c /workspace/requirements-dev.txt
+ # anyio
+ # httpcore
+ # httpx
+starlette==0.27.0 \
+ --hash=sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75 \
+ --hash=sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91
+ # via
+ # -c /workspace/requirements-dev.txt
+ # fastapi
+typer==0.9.0 \
+ --hash=sha256:50922fd79aea2f4751a8e0408ff10d2662bd0c8bbfa84755a699f3bada2978b2 \
+ --hash=sha256:5d96d986a21493606a358cae4461bd8cdf83cbf33a5aa950ae629ca3b51467ee
+ # via
+ # -c /workspace/requirements-dev.txt
+ # mass (pyproject.toml)
+typing-extensions==4.8.0 \
+ --hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \
+ --hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef
+ # via
+ # -c /workspace/requirements-dev.txt
+ # pydantic
+ # starlette
+ # typer
+uvicorn[standard]==0.20.0 \
+ --hash=sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8 \
+ --hash=sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd
+ # via
+ # -c /workspace/requirements-dev.txt
+ # ghga-service-commons
+uvloop==0.17.0 \
+ --hash=sha256:0949caf774b9fcefc7c5756bacbbbd3fc4c05a6b7eebc7c7ad6f825b23998d6d \
+ --hash=sha256:0ddf6baf9cf11a1a22c71487f39f15b2cf78eb5bde7e5b45fbb99e8a9d91b9e1 \
+ --hash=sha256:1436c8673c1563422213ac6907789ecb2b070f5939b9cbff9ef7113f2b531595 \
+ --hash=sha256:23609ca361a7fc587031429fa25ad2ed7242941adec948f9d10c045bfecab06b \
+ --hash=sha256:2a6149e1defac0faf505406259561bc14b034cdf1d4711a3ddcdfbaa8d825a05 \
+ --hash=sha256:2deae0b0fb00a6af41fe60a675cec079615b01d68beb4cc7b722424406b126a8 \
+ --hash=sha256:307958f9fc5c8bb01fad752d1345168c0abc5d62c1b72a4a8c6c06f042b45b20 \
+ --hash=sha256:30babd84706115626ea78ea5dbc7dd8d0d01a2e9f9b306d24ca4ed5796c66ded \
+ --hash=sha256:3378eb62c63bf336ae2070599e49089005771cc651c8769aaad72d1bd9385a7c \
+ --hash=sha256:3d97672dc709fa4447ab83276f344a165075fd9f366a97b712bdd3fee05efae8 \
+ --hash=sha256:3db8de10ed684995a7f34a001f15b374c230f7655ae840964d51496e2f8a8474 \
+ --hash=sha256:3ebeeec6a6641d0adb2ea71dcfb76017602ee2bfd8213e3fcc18d8f699c5104f \
+ --hash=sha256:45cea33b208971e87a31c17622e4b440cac231766ec11e5d22c76fab3bf9df62 \
+ --hash=sha256:6708f30db9117f115eadc4f125c2a10c1a50d711461699a0cbfaa45b9a78e376 \
+ --hash=sha256:68532f4349fd3900b839f588972b3392ee56042e440dd5873dfbbcd2cc67617c \
+ --hash=sha256:6aafa5a78b9e62493539456f8b646f85abc7093dd997f4976bb105537cf2635e \
+ --hash=sha256:7d37dccc7ae63e61f7b96ee2e19c40f153ba6ce730d8ba4d3b4e9738c1dccc1b \
+ --hash=sha256:864e1197139d651a76c81757db5eb199db8866e13acb0dfe96e6fc5d1cf45fc4 \
+ --hash=sha256:8887d675a64cfc59f4ecd34382e5b4f0ef4ae1da37ed665adba0c2badf0d6578 \
+ --hash=sha256:8efcadc5a0003d3a6e887ccc1fb44dec25594f117a94e3127954c05cf144d811 \
+ --hash=sha256:9b09e0f0ac29eee0451d71798878eae5a4e6a91aa275e114037b27f7db72702d \
+ --hash=sha256:a4aee22ece20958888eedbad20e4dbb03c37533e010fb824161b4f05e641f738 \
+ --hash=sha256:a5abddb3558d3f0a78949c750644a67be31e47936042d4f6c888dd6f3c95f4aa \
+ --hash=sha256:c092a2c1e736086d59ac8e41f9c98f26bbf9b9222a76f21af9dfe949b99b2eb9 \
+ --hash=sha256:c686a47d57ca910a2572fddfe9912819880b8765e2f01dc0dd12a9bf8573e539 \
+ --hash=sha256:cbbe908fda687e39afd6ea2a2f14c2c3e43f2ca88e3a11964b297822358d0e6c \
+ --hash=sha256:ce9f61938d7155f79d3cb2ffa663147d4a76d16e08f65e2c66b77bd41b356718 \
+ --hash=sha256:dbbaf9da2ee98ee2531e0c780455f2841e4675ff580ecf93fe5c48fe733b5667 \
+ --hash=sha256:f1e507c9ee39c61bfddd79714e4f85900656db1aec4d40c6de55648e85c2799c \
+ --hash=sha256:ff3d00b70ce95adce264462c930fbaecb29718ba6563db354608f37e49e09024
+ # via
+ # -c /workspace/requirements-dev.txt
+ # uvicorn
+watchfiles==0.20.0 \
+ --hash=sha256:007dcc4a401093010b389c044e81172c8a2520dba257c88f8828b3d460c6bb38 \
+ --hash=sha256:08dc702529bb06a2b23859110c214db245455532da5eaea602921687cfcd23db \
+ --hash=sha256:0d82dbc1832da83e441d112069833eedd4cf583d983fb8dd666fbefbea9d99c0 \
+ --hash=sha256:13f995d5152a8ba4ed7c2bbbaeee4e11a5944defc7cacd0ccb4dcbdcfd78029a \
+ --hash=sha256:3796312bd3587e14926013612b23066912cf45a14af71cf2b20db1c12dadf4e9 \
+ --hash=sha256:5392dd327a05f538c56edb1c6ebba6af91afc81b40822452342f6da54907bbdf \
+ --hash=sha256:570848706440373b4cd8017f3e850ae17f76dbdf1e9045fc79023b11e1afe490 \
+ --hash=sha256:608cd94a8767f49521901aff9ae0c92cc8f5a24d528db7d6b0295290f9d41193 \
+ --hash=sha256:728575b6b94c90dd531514677201e8851708e6e4b5fe7028ac506a200b622019 \
+ --hash=sha256:7d4e66a857621584869cfbad87039e65dadd7119f0d9bb9dbc957e089e32c164 \
+ --hash=sha256:835df2da7a5df5464c4a23b2d963e1a9d35afa422c83bf4ff4380b3114603644 \
+ --hash=sha256:87d9e1f75c4f86c93d73b5bd1ebe667558357548f11b4f8af4e0e272f79413ce \
+ --hash=sha256:89d1de8218874925bce7bb2ae9657efc504411528930d7a83f98b1749864f2ef \
+ --hash=sha256:99f4c65fd2fce61a571b2a6fcf747d6868db0bef8a934e8ca235cc8533944d95 \
+ --hash=sha256:9a0351d20d03c6f7ad6b2e8a226a5efafb924c7755ee1e34f04c77c3682417fa \
+ --hash=sha256:9b5c8d3be7b502f8c43a33c63166ada8828dbb0c6d49c8f9ce990a96de2f5a49 \
+ --hash=sha256:a03d1e6feb7966b417f43c3e3783188167fd69c2063e86bad31e62c4ea794cc5 \
+ --hash=sha256:b17d4176c49d207865630da5b59a91779468dd3e08692fe943064da260de2c7c \
+ --hash=sha256:d0002d81c89a662b595645fb684a371b98ff90a9c7d8f8630c82f0fde8310458 \
+ --hash=sha256:d97db179f7566dcf145c5179ddb2ae2a4450e3a634eb864b09ea04e68c252e8e \
+ --hash=sha256:e43af4464daa08723c04b43cf978ab86cc55c684c16172622bdac64b34e36af0 \
+ --hash=sha256:eccc8942bcdc7d638a01435d915b913255bbd66f018f1af051cd8afddb339ea3
+ # via
+ # -c /workspace/requirements-dev.txt
+ # uvicorn
+websockets==11.0.3 \
+ --hash=sha256:01f5567d9cf6f502d655151645d4e8b72b453413d3819d2b6f1185abc23e82dd \
+ --hash=sha256:03aae4edc0b1c68498f41a6772d80ac7c1e33c06c6ffa2ac1c27a07653e79d6f \
+ --hash=sha256:0ac56b661e60edd453585f4bd68eb6a29ae25b5184fd5ba51e97652580458998 \
+ --hash=sha256:0ee68fe502f9031f19d495dae2c268830df2760c0524cbac5d759921ba8c8e82 \
+ --hash=sha256:1553cb82942b2a74dd9b15a018dce645d4e68674de2ca31ff13ebc2d9f283788 \
+ --hash=sha256:1a073fc9ab1c8aff37c99f11f1641e16da517770e31a37265d2755282a5d28aa \
+ --hash=sha256:1d2256283fa4b7f4c7d7d3e84dc2ece74d341bce57d5b9bf385df109c2a1a82f \
+ --hash=sha256:1d5023a4b6a5b183dc838808087033ec5df77580485fc533e7dab2567851b0a4 \
+ --hash=sha256:1fdf26fa8a6a592f8f9235285b8affa72748dc12e964a5518c6c5e8f916716f7 \
+ --hash=sha256:2529338a6ff0eb0b50c7be33dc3d0e456381157a31eefc561771ee431134a97f \
+ --hash=sha256:279e5de4671e79a9ac877427f4ac4ce93751b8823f276b681d04b2156713b9dd \
+ --hash=sha256:2d903ad4419f5b472de90cd2d40384573b25da71e33519a67797de17ef849b69 \
+ --hash=sha256:332d126167ddddec94597c2365537baf9ff62dfcc9db4266f263d455f2f031cb \
+ --hash=sha256:34fd59a4ac42dff6d4681d8843217137f6bc85ed29722f2f7222bd619d15e95b \
+ --hash=sha256:3580dd9c1ad0701169e4d6fc41e878ffe05e6bdcaf3c412f9d559389d0c9e016 \
+ --hash=sha256:3ccc8a0c387629aec40f2fc9fdcb4b9d5431954f934da3eaf16cdc94f67dbfac \
+ --hash=sha256:41f696ba95cd92dc047e46b41b26dd24518384749ed0d99bea0a941ca87404c4 \
+ --hash=sha256:42cc5452a54a8e46a032521d7365da775823e21bfba2895fb7b77633cce031bb \
+ --hash=sha256:4841ed00f1026dfbced6fca7d963c4e7043aa832648671b5138008dc5a8f6d99 \
+ --hash=sha256:4b253869ea05a5a073ebfdcb5cb3b0266a57c3764cf6fe114e4cd90f4bfa5f5e \
+ --hash=sha256:54c6e5b3d3a8936a4ab6870d46bdd6ec500ad62bde9e44462c32d18f1e9a8e54 \
+ --hash=sha256:619d9f06372b3a42bc29d0cd0354c9bb9fb39c2cbc1a9c5025b4538738dbffaf \
+ --hash=sha256:6505c1b31274723ccaf5f515c1824a4ad2f0d191cec942666b3d0f3aa4cb4007 \
+ --hash=sha256:660e2d9068d2bedc0912af508f30bbeb505bbbf9774d98def45f68278cea20d3 \
+ --hash=sha256:6681ba9e7f8f3b19440921e99efbb40fc89f26cd71bf539e45d8c8a25c976dc6 \
+ --hash=sha256:68b977f21ce443d6d378dbd5ca38621755f2063d6fdb3335bda981d552cfff86 \
+ --hash=sha256:69269f3a0b472e91125b503d3c0b3566bda26da0a3261c49f0027eb6075086d1 \
+ --hash=sha256:6f1a3f10f836fab6ca6efa97bb952300b20ae56b409414ca85bff2ad241d2a61 \
+ --hash=sha256:7622a89d696fc87af8e8d280d9b421db5133ef5b29d3f7a1ce9f1a7bf7fcfa11 \
+ --hash=sha256:777354ee16f02f643a4c7f2b3eff8027a33c9861edc691a2003531f5da4f6bc8 \
+ --hash=sha256:84d27a4832cc1a0ee07cdcf2b0629a8a72db73f4cf6de6f0904f6661227f256f \
+ --hash=sha256:8531fdcad636d82c517b26a448dcfe62f720e1922b33c81ce695d0edb91eb931 \
+ --hash=sha256:86d2a77fd490ae3ff6fae1c6ceaecad063d3cc2320b44377efdde79880e11526 \
+ --hash=sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016 \
+ --hash=sha256:8a34e13a62a59c871064dfd8ffb150867e54291e46d4a7cf11d02c94a5275bae \
+ --hash=sha256:8c82f11964f010053e13daafdc7154ce7385ecc538989a354ccc7067fd7028fd \
+ --hash=sha256:92b2065d642bf8c0a82d59e59053dd2fdde64d4ed44efe4870fa816c1232647b \
+ --hash=sha256:97b52894d948d2f6ea480171a27122d77af14ced35f62e5c892ca2fae9344311 \
+ --hash=sha256:9d9acd80072abcc98bd2c86c3c9cd4ac2347b5a5a0cae7ed5c0ee5675f86d9af \
+ --hash=sha256:9f59a3c656fef341a99e3d63189852be7084c0e54b75734cde571182c087b152 \
+ --hash=sha256:aa5003845cdd21ac0dc6c9bf661c5beddd01116f6eb9eb3c8e272353d45b3288 \
+ --hash=sha256:b16fff62b45eccb9c7abb18e60e7e446998093cdcb50fed33134b9b6878836de \
+ --hash=sha256:b30c6590146e53149f04e85a6e4fcae068df4289e31e4aee1fdf56a0dead8f97 \
+ --hash=sha256:b58cbf0697721120866820b89f93659abc31c1e876bf20d0b3d03cef14faf84d \
+ --hash=sha256:b67c6f5e5a401fc56394f191f00f9b3811fe843ee93f4a70df3c389d1adf857d \
+ --hash=sha256:bceab846bac555aff6427d060f2fcfff71042dba6f5fca7dc4f75cac815e57ca \
+ --hash=sha256:bee9fcb41db2a23bed96c6b6ead6489702c12334ea20a297aa095ce6d31370d0 \
+ --hash=sha256:c114e8da9b475739dde229fd3bc6b05a6537a88a578358bc8eb29b4030fac9c9 \
+ --hash=sha256:c1f0524f203e3bd35149f12157438f406eff2e4fb30f71221c8a5eceb3617b6b \
+ --hash=sha256:c792ea4eabc0159535608fc5658a74d1a81020eb35195dd63214dcf07556f67e \
+ --hash=sha256:c7f3cb904cce8e1be667c7e6fef4516b98d1a6a0635a58a57528d577ac18a128 \
+ --hash=sha256:d67ac60a307f760c6e65dad586f556dde58e683fab03323221a4e530ead6f74d \
+ --hash=sha256:dcacf2c7a6c3a84e720d1bb2b543c675bf6c40e460300b628bab1b1efc7c034c \
+ --hash=sha256:de36fe9c02995c7e6ae6efe2e205816f5f00c22fd1fbf343d4d18c3d5ceac2f5 \
+ --hash=sha256:def07915168ac8f7853812cc593c71185a16216e9e4fa886358a17ed0fd9fcf6 \
+ --hash=sha256:df41b9bc27c2c25b486bae7cf42fccdc52ff181c8c387bfd026624a491c2671b \
+ --hash=sha256:e052b8467dd07d4943936009f46ae5ce7b908ddcac3fda581656b1b19c083d9b \
+ --hash=sha256:e063b1865974611313a3849d43f2c3f5368093691349cf3c7c8f8f75ad7cb280 \
+ --hash=sha256:e1459677e5d12be8bbc7584c35b992eea142911a6236a3278b9b5ce3326f282c \
+ --hash=sha256:e1a99a7a71631f0efe727c10edfba09ea6bee4166a6f9c19aafb6c0b5917d09c \
+ --hash=sha256:e590228200fcfc7e9109509e4d9125eace2042fd52b595dd22bbc34bb282307f \
+ --hash=sha256:e6316827e3e79b7b8e7d8e3b08f4e331af91a48e794d5d8b099928b6f0b85f20 \
+ --hash=sha256:e7837cb169eca3b3ae94cc5787c4fed99eef74c0ab9506756eea335e0d6f3ed8 \
+ --hash=sha256:e848f46a58b9fcf3d06061d17be388caf70ea5b8cc3466251963c8345e13f7eb \
+ --hash=sha256:ed058398f55163a79bb9f06a90ef9ccc063b204bb346c4de78efc5d15abfe602 \
+ --hash=sha256:f2e58f2c36cc52d41f2659e4c0cbf7353e28c8c9e63e30d8c6d3494dc9fdedcf \
+ --hash=sha256:f467ba0050b7de85016b43f5a22b46383ef004c4f672148a8abf32bc999a87f0 \
+ --hash=sha256:f61bdb1df43dc9c131791fbc2355535f9024b9a04398d3bd0684fc16ab07df74 \
+ --hash=sha256:fb06eea71a00a7af0ae6aefbb932fb8a7df3cb390cc217d51a9ad7343de1b8d0 \
+ --hash=sha256:ffd7dcaf744f25f82190856bc26ed81721508fc5cbf2a330751e135ff1283564
+ # via
+ # -c /workspace/requirements-dev.txt
+ # uvicorn
diff --git a/scripts/get_package_name.py b/scripts/get_package_name.py
index 7c6b79a..84d15fd 100755
--- a/scripts/get_package_name.py
+++ b/scripts/get_package_name.py
@@ -15,24 +15,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Extracts the package name from the setup.cfg"""
+"""Extracts the package name from pyproject.toml"""
from pathlib import Path
REPO_ROOT_DIR = Path(__file__).parent.parent.resolve()
-SETUP_CFG_PATH = REPO_ROOT_DIR / "setup.cfg"
+PYPROJECT_TOML_PATH = REPO_ROOT_DIR / "pyproject.toml"
NAME_PREFIX = "name = "
def get_package_name() -> str:
"""Extracts the package name"""
- with open(SETUP_CFG_PATH, "r", encoding="utf8") as setup_cfg:
- for line in setup_cfg.readlines():
+ with open(PYPROJECT_TOML_PATH, encoding="utf8") as pyproject_toml:
+ for line in pyproject_toml.readlines():
line_stripped = line.strip()
if line_stripped.startswith(NAME_PREFIX):
package_name = line_stripped[len(NAME_PREFIX) :]
- return package_name
+ return package_name.strip('"')
raise RuntimeError("Could not find package name.")
diff --git a/scripts/license_checker.py b/scripts/license_checker.py
index 0d43a1b..0ddc786 100755
--- a/scripts/license_checker.py
+++ b/scripts/license_checker.py
@@ -15,10 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# pylint: skip-file
-
"""This script checks that the license and license headers
-exists and that they are up to date.
+exist and that they are up to date.
"""
import argparse
@@ -26,7 +24,7 @@
import sys
from datetime import date
from pathlib import Path
-from typing import List, Optional, Tuple, Union
+from typing import Optional, Union
# root directory of the package:
ROOT_DIR = Path(__file__).parent.parent.resolve()
@@ -48,11 +46,14 @@
"sdist",
"wheels",
"pip-wheel-metadata",
+ ".coveragerc",
".git",
".github",
".flake8",
".gitignore",
".pylintrc",
+ ".ruff.toml",
+ ".ruff_cache",
"example_config.yaml",
"config_schema.json",
"LICENSE", # is checked but not for the license header
@@ -74,6 +75,7 @@
# exclude file by file ending from license header check:
EXCLUDE_ENDINGS = [
"html",
+ "in",
"ini",
"jinja",
"json",
@@ -81,6 +83,7 @@
"pub",
"pyc",
"sec",
+ "toml",
"txt",
"xml",
"yaml",
@@ -163,10 +166,10 @@ def __init__(self, file_path: Union[str, Path]):
def get_target_files(
target_dir: Path,
- exclude: List[str] = EXCLUDE,
- exclude_endings: List[str] = EXCLUDE_ENDINGS,
- exclude_pattern: List[str] = EXCLUDE_PATTERN,
-) -> List[Path]:
+ exclude: list[str] = EXCLUDE,
+ exclude_endings: list[str] = EXCLUDE_ENDINGS,
+ exclude_pattern: list[str] = EXCLUDE_PATTERN,
+) -> list[Path]:
"""Get target files that are not match the exclude conditions.
Args:
target_dir (pathlib.Path): The target dir to search.
@@ -200,7 +203,7 @@ def get_target_files(
return target_files
-def normalized_line(line: str, chars_to_trim: List[str] = COMMENT_CHARS) -> str:
+def normalized_line(line: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str:
norm_line = line.strip()
for char in chars_to_trim:
@@ -209,11 +212,11 @@ def normalized_line(line: str, chars_to_trim: List[str] = COMMENT_CHARS) -> str:
return norm_line.strip("\n").strip("\t").strip()
-def normalized_text(text: str, chars_to_trim: List[str] = COMMENT_CHARS) -> str:
+def normalized_text(text: str, chars_to_trim: list[str] = COMMENT_CHARS) -> str:
"Normalize a license header text."
lines = text.split("\n")
- norm_lines: List[str] = []
+ norm_lines: list[str] = []
for line in lines:
stripped_line = line.strip()
@@ -233,13 +236,13 @@ def normalized_text(text: str, chars_to_trim: List[str] = COMMENT_CHARS) -> str:
def format_copyright_template(copyright_template: str, author: str) -> str:
- """Formats license header by inserting the specified author for every occurence of
+ """Formats license header by inserting the specified author for every occurrence of
"{author}" in the header template.
"""
return normalized_text(copyright_template.replace("{author}", author))
-def is_commented_line(line: str, comment_chars: List[str] = COMMENT_CHARS) -> bool:
+def is_commented_line(line: str, comment_chars: list[str] = COMMENT_CHARS) -> bool:
"""Checks whether a line is a comment."""
line_stripped = line.strip()
for comment_char in comment_chars:
@@ -254,12 +257,12 @@ def is_empty_line(line: str) -> bool:
return line.strip("\n").strip("\t").strip() == ""
-def get_header(file_path: Path, comment_chars: List[str] = COMMENT_CHARS):
+def get_header(file_path: Path, comment_chars: list[str] = COMMENT_CHARS):
"""Extracts the header from a file and normalizes it."""
- header_lines: List[str] = []
+ header_lines: list[str] = []
try:
- with open(file_path, "r") as file:
+ with open(file_path) as file:
for line in file:
if is_commented_line(
line, comment_chars=comment_chars
@@ -307,7 +310,7 @@ def check_copyright_notice(
global_copyright: GlobalCopyrightNotice,
copyright_template: str = COPYRIGHT_TEMPLATE,
author: str = AUTHOR,
- comment_chars: List[str] = COMMENT_CHARS,
+ comment_chars: list[str] = COMMENT_CHARS,
min_year: int = MIN_YEAR,
) -> bool:
"""Checks the specified copyright text against a template.
@@ -326,7 +329,7 @@ def check_copyright_notice(
author (str, optional):
The author that shall be included in the license header.
It will replace any appearance of "{author}" in the license
- header. This defaults to an auther info for GHGA.
+ header. This defaults to an author info for GHGA.
"""
# If the global_copyright is already set, check if the current copyright is
@@ -372,12 +375,12 @@ def check_file_headers(
global_copyright: GlobalCopyrightNotice,
copyright_template: str = COPYRIGHT_TEMPLATE,
author: str = AUTHOR,
- exclude: List[str] = EXCLUDE,
- exclude_endings: List[str] = EXCLUDE_ENDINGS,
- exclude_pattern: List[str] = EXCLUDE_PATTERN,
- comment_chars: List[str] = COMMENT_CHARS,
+ exclude: list[str] = EXCLUDE,
+ exclude_endings: list[str] = EXCLUDE_ENDINGS,
+ exclude_pattern: list[str] = EXCLUDE_PATTERN,
+ comment_chars: list[str] = COMMENT_CHARS,
min_year: int = MIN_YEAR,
-) -> Tuple[List[Path], List[Path]]:
+) -> tuple[list[Path], list[Path]]:
"""Check files for presence of a license header and verify that
the copyright notice is up to date (correct year).
@@ -414,8 +417,8 @@ def check_file_headers(
)
# check if license header present in file:
- passed_files: List[Path] = []
- failed_files: List[Path] = []
+ passed_files: list[Path] = []
+ failed_files: list[Path] = []
for target_file in target_files:
try:
@@ -443,7 +446,7 @@ def check_license_file(
global_copyright: GlobalCopyrightNotice,
copyright_template: str = COPYRIGHT_TEMPLATE,
author: str = AUTHOR,
- comment_chars: List[str] = COMMENT_CHARS,
+ comment_chars: list[str] = COMMENT_CHARS,
min_year: int = MIN_YEAR,
) -> bool:
"""Currently only checks if the copyright notice in the
@@ -470,7 +473,7 @@ def check_license_file(
print(f'Could not find license file "{str(license_file)}".')
return False
- with open(license_file, "r") as file_:
+ with open(license_file) as file_:
license_text = normalized_text(file_.read())
# Extract the copyright notice:
@@ -522,7 +525,7 @@ def run():
global_copyright = GlobalCopyrightNotice()
# get global copyright from .devcontainer/license_header.txt file:
- with open(GLOBAL_COPYRIGHT_FILE_PATH, "r") as global_copyright_file:
+ with open(GLOBAL_COPYRIGHT_FILE_PATH) as global_copyright_file:
global_copyright.text = normalized_text(global_copyright_file.read())
if args.no_license_file_check:
diff --git a/scripts/list_outdated_dependencies.py b/scripts/list_outdated_dependencies.py
new file mode 100755
index 0000000..1aa15a1
--- /dev/null
+++ b/scripts/list_outdated_dependencies.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python3
+
+# Copyright 2021 - 2023 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.
+#
+"""Check capped dependencies for newer versions."""
+import sys
+from collections.abc import Sequence
+from pathlib import Path
+from typing import Any, NamedTuple
+
+import httpx
+from packaging.requirements import Requirement
+
+from script_utils import cli, deps, lock_deps
+
+REPO_ROOT_DIR = Path(__file__).parent.parent.resolve()
+PYPROJECT_TOML_PATH = REPO_ROOT_DIR / "pyproject.toml"
+DEV_DEPS_PATH = REPO_ROOT_DIR / "requirements-dev.in"
+LOCK_FILE_PATH = REPO_ROOT_DIR / "requirements-dev.txt"
+
+
+class OutdatedDep(NamedTuple):
+ """Encapsulates data of an outdated dependency"""
+
+ name: str
+ specified_version: str
+ pypi_version: str
+
+
+def get_main_deps_pyproject(modified_pyproject: dict[str, Any]) -> list[Requirement]:
+ """Get a list of the dependencies from pyproject.toml"""
+
+ dependencies: list[str] = []
+ if "dependencies" in modified_pyproject["project"]:
+ dependencies = modified_pyproject["project"]["dependencies"]
+
+ return [Requirement(dependency) for dependency in dependencies]
+
+
+def get_optional_deps_pyproject(
+ modified_pyproject: dict[str, Any]
+) -> list[Requirement]:
+ """Get a list of the optional dependencies from pyproject.toml"""
+
+ dependencies: list[str] = []
+
+ if "optional-dependencies" in modified_pyproject["project"]:
+ for optional_dependency_list in modified_pyproject["project"][
+ "optional-dependencies"
+ ]:
+ dependencies.extend(
+ modified_pyproject["project"]["optional-dependencies"][
+ optional_dependency_list
+ ]
+ )
+
+ return [Requirement(dependency) for dependency in dependencies]
+
+
+def get_deps_dev() -> list[Requirement]:
+ """Get a list of raw dependency strings from requirements-dev.in"""
+ with open(DEV_DEPS_PATH, encoding="utf-8") as dev_deps:
+ dependencies = [
+ line
+ for line in (line.strip() for line in dev_deps)
+ if line # skip empty lines
+ and not line.startswith("#") # skip comments
+ and "requirements-dev-common.in" not in line # skip inclusion line
+ ]
+
+ return [Requirement(dependency) for dependency in dependencies]
+
+
+def get_version_from_pypi(package_name: str, client: httpx.Client) -> str:
+ """Make a call to PyPI to get the version information about `package_name`."""
+ try:
+ response = client.get(f"https://pypi.org/pypi/{package_name}/json")
+ body = response.json()
+ version = body["info"]["version"]
+ except (httpx.RequestError, KeyError):
+ cli.echo_failure(f"Unable to retrieve information for package '{package_name}'")
+ sys.exit(1)
+
+ return version
+
+
+def get_outdated_deps(
+ requirements: list[Requirement], strip: bool = False
+) -> list[OutdatedDep]:
+ """Determine which packages have updates available outside of pinned ranges."""
+ outdated: list[OutdatedDep] = []
+ with httpx.Client(timeout=10) as client:
+ for requirement in requirements:
+ pypi_version = get_version_from_pypi(requirement.name, client)
+
+ specified = str(requirement.specifier)
+
+ # Strip the specifier symbols from the front of the string if desired
+ if strip:
+ specified = specified.lstrip("<=>!~")
+
+ # append package name, specified version, and latest available version
+ if not requirement.specifier.contains(pypi_version):
+ outdated.append(OutdatedDep(requirement.name, specified, pypi_version))
+ outdated.sort()
+ return outdated
+
+
+def print_table(
+ rows: Sequence[tuple[str, ...]],
+ headers: tuple[str, ...],
+ delimiter: str = " | ",
+):
+ """
+ List outdated dependencies in a formatted table.
+
+ Args:
+ `outdated`: A sequence of tuples containing strings.
+ `headers`: A tuple containing the header strings for the table columns.
+ """
+ if rows and len(rows[0]) != len(headers):
+ raise RuntimeError("Number of headers doesn't match number of columns")
+
+ header_lengths = [len(header) for header in headers]
+
+ # Find the maximum length of each column
+ col_widths = [max(len(str(cell)) for cell in col) for col in zip(*rows)]
+
+ # Create a row format based on the maximum column widths
+ row_format = delimiter.join(
+ f"{{:<{max(width, header_len)}}}"
+ for width, header_len in zip(col_widths, header_lengths)
+ )
+
+ print(" " + row_format.format(*headers))
+ for dependency in rows:
+ print(" " + row_format.format(*dependency))
+
+
+def main(transitive: bool = False):
+ """Check capped dependencies for newer versions.
+
+ Examine `pyproject.toml` and `requirements-dev.in` for capped dependencies.
+ Make a call to PyPI to see if any newer versions exist.
+
+ Use `transitive` to show outdated transitive dependencies.
+ """
+ modified_pyproject: dict[str, Any] = deps.get_modified_pyproject(
+ PYPROJECT_TOML_PATH
+ )
+ main_dependencies = get_main_deps_pyproject(modified_pyproject)
+ optional_dependencies = get_optional_deps_pyproject(modified_pyproject)
+ dev_dependencies = get_deps_dev()
+
+ outdated_main = get_outdated_deps(main_dependencies)
+ outdated_optional = get_outdated_deps(optional_dependencies)
+ outdated_dev = get_outdated_deps(dev_dependencies)
+
+ found_outdated = any([outdated_main, outdated_optional, outdated_dev])
+ transitive_headers = ("PACKAGE", "SPECIFIED", "AVAILABLE")
+ if outdated_main:
+ location = PYPROJECT_TOML_PATH.name + " - dependencies"
+ cli.echo_failure(f"Outdated dependencies from {location}:")
+ print_table(outdated_main, transitive_headers)
+ if outdated_optional:
+ location = PYPROJECT_TOML_PATH.name + " - optional-dependencies"
+ cli.echo_failure(f"Outdated dependencies from {location}:")
+ print_table(outdated_optional, transitive_headers)
+ if outdated_dev:
+ cli.echo_failure(f"Outdated dependencies from {DEV_DEPS_PATH.name}:")
+ print_table(outdated_dev, transitive_headers)
+
+ if not found_outdated:
+ cli.echo_success("All top-level dependencies up to date.")
+
+ if transitive:
+ top_level: set[str] = {
+ item.name for item in outdated_main + outdated_optional + outdated_dev
+ }
+
+ print("\nRetrieving transitive dependency information...")
+ transitive_dependencies = lock_deps.get_lock_file_deps(
+ LOCK_FILE_PATH, exclude=top_level
+ )
+ outdated_transitive = get_outdated_deps(transitive_dependencies, strip=True)
+
+ if outdated_transitive:
+ transitive_headers = ("PACKAGE", "PINNED", "AVAILABLE")
+
+ cli.echo_failure("Outdated transitive dependencies:")
+ print_table(outdated_transitive, transitive_headers)
+ else:
+ cli.echo_success("All transitive dependencies up to date.")
+
+
+if __name__ == "__main__":
+ cli.run(main)
diff --git a/scripts/script_utils/deps.py b/scripts/script_utils/deps.py
new file mode 100644
index 0000000..5d65cf7
--- /dev/null
+++ b/scripts/script_utils/deps.py
@@ -0,0 +1,74 @@
+# Copyright 2021 - 2023 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.
+#
+"""Contains utils for working with dependencies, lock files, etc."""
+from copy import deepcopy
+from pathlib import Path
+from typing import Any
+
+import stringcase
+import tomli
+
+
+def exclude_from_dependency_list(*, package_name: str, dependencies: list) -> list:
+ """Exclude the specified package from the provided dependency list."""
+
+ return [
+ dependency
+ for dependency in dependencies
+ if not dependency.startswith(package_name)
+ ]
+
+
+def remove_self_dependencies(pyproject: dict) -> dict:
+ """Filter out self dependencies (dependencies of the package on it self) from the
+ dependencies and optional-dependencies in the provided pyproject metadata."""
+
+ if "project" not in pyproject:
+ return pyproject
+
+ modified_pyproject = deepcopy(pyproject)
+
+ project_metadata = modified_pyproject["project"]
+
+ package_name = stringcase.spinalcase(project_metadata.get("name"))
+
+ if not package_name:
+ raise ValueError("The provided project metadata does not contain a name.")
+
+ if "dependencies" in project_metadata:
+ project_metadata["dependencies"] = exclude_from_dependency_list(
+ package_name=package_name, dependencies=project_metadata["dependencies"]
+ )
+
+ if "optional-dependencies" in project_metadata:
+ for group in project_metadata["optional-dependencies"]:
+ project_metadata["optional-dependencies"][
+ group
+ ] = exclude_from_dependency_list(
+ package_name=package_name,
+ dependencies=project_metadata["optional-dependencies"][group],
+ )
+
+ return modified_pyproject
+
+
+def get_modified_pyproject(pyproject_toml_path: Path) -> dict[str, Any]:
+ """Get a copy of pyproject.toml with any self-referencing dependencies removed."""
+ with open(pyproject_toml_path, "rb") as pyproject_toml:
+ pyproject = tomli.load(pyproject_toml)
+
+ modified_pyproject = remove_self_dependencies(pyproject)
+ return modified_pyproject
diff --git a/scripts/script_utils/fastapi_app_location.py b/scripts/script_utils/fastapi_app_location.py
index 7000d9d..b506ab5 100644
--- a/scripts/script_utils/fastapi_app_location.py
+++ b/scripts/script_utils/fastapi_app_location.py
@@ -22,4 +22,4 @@
from mass.config import Config
from mass.main import get_rest_api
-app = get_rest_api(config=Config())
+app = get_rest_api(config=Config()) # type: ignore[call-arg]
diff --git a/scripts/script_utils/lock_deps.py b/scripts/script_utils/lock_deps.py
new file mode 100644
index 0000000..60d857e
--- /dev/null
+++ b/scripts/script_utils/lock_deps.py
@@ -0,0 +1,46 @@
+# Copyright 2021 - 2023 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.
+#
+"""Provides a function to get all dependencies from the lock file"""
+import re
+from pathlib import Path
+from typing import Optional
+
+from packaging.requirements import Requirement
+
+
+def get_lock_file_deps(
+ lock_file_path: Path,
+ exclude: Optional[set[str]] = None,
+) -> list[Requirement]:
+ """Inspect the lock file to get the dependencies.
+
+ Return a list of Requirements objects that contain the dependency info.
+ """
+ dependency_pattern = re.compile(r"([^=\s]+==[^\s]*?)\s")
+
+ # Get the set of dependencies from the provided lock file
+ with open(lock_file_path, encoding="utf-8") as lock_file:
+ lines = lock_file.readlines()
+
+ dependencies: list[Requirement] = []
+ for line in lines:
+ if match := re.match(dependency_pattern, line):
+ dependency_string = match.group(1)
+ requirement = Requirement(dependency_string)
+ if not exclude or requirement.name not in exclude:
+ dependencies.append(requirement)
+
+ return dependencies
diff --git a/scripts/update_config_docs.py b/scripts/update_config_docs.py
index c5457ad..b1be57a 100755
--- a/scripts/update_config_docs.py
+++ b/scripts/update_config_docs.py
@@ -25,10 +25,11 @@
import sys
from difflib import unified_diff
from pathlib import Path
-from typing import Any, Type
+from typing import Any
import yaml
from pydantic import BaseSettings
+
from script_utils.cli import echo_failure, echo_success, run
HERE = Path(__file__).parent.resolve()
@@ -43,7 +44,7 @@ class ValidationError(RuntimeError):
"""Raised when validation of config documentation fails."""
-def get_config_class() -> Type[BaseSettings]:
+def get_config_class() -> type[BaseSettings]:
"""
Dynamically imports and returns the Config class from the current service.
This makes the script service repo agnostic.
@@ -121,7 +122,7 @@ def check_docs():
"""
example_expected = get_example()
- with open(EXAMPLE_CONFIG_YAML, "r", encoding="utf-8") as example_file:
+ with open(EXAMPLE_CONFIG_YAML, encoding="utf-8") as example_file:
example_observed = example_file.read()
if example_expected != example_observed:
print_diff(example_expected, example_observed)
@@ -130,7 +131,7 @@ def check_docs():
)
schema_expected = get_schema()
- with open(CONFIG_SCHEMA_JSON, "r", encoding="utf-8") as schema_file:
+ with open(CONFIG_SCHEMA_JSON, encoding="utf-8") as schema_file:
schema_observed = schema_file.read()
if schema_expected != schema_observed:
raise ValidationError(
diff --git a/scripts/update_hook_revs.py b/scripts/update_hook_revs.py
new file mode 100755
index 0000000..3d653f8
--- /dev/null
+++ b/scripts/update_hook_revs.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+
+# Copyright 2021 - 2023 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.
+#
+"""Script to ensure the pre-commit hook revs match what is installed."""
+import re
+import sys
+from functools import partial
+from pathlib import Path
+
+from packaging.requirements import Requirement
+
+from script_utils import cli, lock_deps
+
+REPO_ROOT_DIR = Path(__file__).parent.parent.resolve()
+PRE_COMMIT_CFG_PATH = REPO_ROOT_DIR / ".pre-commit-config.yaml"
+LOCK_FILE_PATH = REPO_ROOT_DIR / "requirements-dev.txt"
+
+
+def make_dependency_dict(requirements: list[Requirement]) -> dict[str, str]:
+ """Accept a list of Requirement objects and convert to dict"""
+ processed = {
+ req.name: str(req.specifier).removeprefix("==") for req in requirements
+ }
+
+ return processed
+
+
+def get_repl_value(match, dependencies: dict[str, str], outdated_hooks: list[str]):
+ """Look up pre-commit hook id in list of dependencies. If there's a match, update
+ `outdated_hooks` and return the hook version stored in the dictionary"""
+ ver, name = match.groups()
+ if name in dependencies:
+ new_ver = dependencies[name].strip()
+
+ # Use the v prefix if it was used before
+ if ver.startswith("v"):
+ new_ver = ver[0] + new_ver
+
+ # Make a list of what's outdated
+ if new_ver != ver:
+ msg = f"\t{name} (configured: {ver}, expected: {new_ver})"
+ outdated_hooks.append(msg)
+ return new_ver
+ return ver
+
+
+def get_config():
+ """Obtain the current pre-commit hook config from .pre-commit-config.yaml"""
+ with open(PRE_COMMIT_CFG_PATH, encoding="utf-8") as pre_commit_config:
+ return pre_commit_config.read()
+
+
+def process_config(dependencies: dict[str, str], config: str) -> tuple[str, list[str]]:
+ """Compare pre-commit config with lock file dependencies.
+
+ Create a modified copy of the existing config file contents with the hook versions
+ synchronized to the lock file dependencies.
+
+ Returns:
+ `new_config`: the updated/synchronized pre-commit config.
+
+ `outdated_hooks`: a list of any outdated hooks with version discrepancy details.
+ """
+ outdated_hooks: list[str] = []
+
+ hook_rev = re.compile(r"([^\s\n]+)(?=\s*hooks:\s*- id: ([^\s]+))")
+
+ new_config = re.sub(
+ hook_rev,
+ repl=partial(
+ get_repl_value, dependencies=dependencies, outdated_hooks=outdated_hooks
+ ),
+ string=config,
+ )
+
+ return new_config, outdated_hooks
+
+
+def update_config(new_config: str):
+ """Write `new_config` to .pre-commit-config.yaml"""
+ with open(PRE_COMMIT_CFG_PATH, "w", encoding="utf-8") as pre_commit_config:
+ pre_commit_config.write(new_config)
+ cli.echo_success(f"Updated '{PRE_COMMIT_CFG_PATH}'")
+
+
+def output_failure(outdated_hooks: list[str]):
+ """Notify the user that some pre-commit hooks are outdated, and list those hooks."""
+ cli.echo_failure("The following pre-commit hook versions are outdated:")
+ for hook in outdated_hooks:
+ print(hook)
+ print("Run 'scripts/update_hook_revs.py' to update")
+ sys.exit(1)
+
+
+def main(check: bool = False):
+ """Compare configured pre-commit hooks with the installed dependencies.
+
+ For the set that overlap (e.g. `black`, `mypy`, `ruff`, etc.), make sure the
+ versions match. If running with `--check`, exit with status code 1 if anything is
+ outdated. If running without `--check`, update `.pre-commit-config.yaml` as needed.
+ """
+
+ dependencies: list[Requirement] = lock_deps.get_lock_file_deps(LOCK_FILE_PATH)
+ dependency_dict: dict[str, str] = make_dependency_dict(dependencies)
+ config = get_config()
+ new_config, outdated_hooks = process_config(dependency_dict, config)
+
+ if config != new_config:
+ if check:
+ output_failure(outdated_hooks)
+ else:
+ update_config(new_config)
+ else:
+ cli.echo_success("Pre-commit hooks are up to date.")
+
+
+if __name__ == "__main__":
+ cli.run(main)
diff --git a/scripts/update_lock.py b/scripts/update_lock.py
new file mode 100755
index 0000000..825aade
--- /dev/null
+++ b/scripts/update_lock.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python3
+
+# Copyright 2021 - 2023 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.
+#
+
+"""Update the dependency lock files located at 'requirements.txt' and
+'requirements-dev.txt'.
+"""
+
+import os
+import re
+import subprocess
+from pathlib import Path
+from tempfile import TemporaryDirectory
+
+import tomli_w
+
+from script_utils import cli, deps
+
+REPO_ROOT_DIR = Path(__file__).parent.parent.resolve()
+
+PYPROJECT_TOML_PATH = REPO_ROOT_DIR / "pyproject.toml"
+DEV_DEPS_PATH = REPO_ROOT_DIR / "requirements-dev.in"
+OUTPUT_LOCK_PATH = REPO_ROOT_DIR / "requirements.txt"
+OUTPUT_DEV_LOCK_PATH = REPO_ROOT_DIR / "requirements-dev.txt"
+
+
+def fix_temp_dir_comments(file_path: Path):
+ """Fix the temp_dir comments so they don't cause a noisy diff
+
+ This will leave the top compile message intact as a point of sanity to verify that
+ the requirements are indeed being generated if nothing else changes.
+ """
+
+ with open(file_path, encoding="utf-8") as file:
+ lines = file.readlines()
+
+ with open(file_path, "w", encoding="utf-8") as file:
+ for line in lines:
+ # Remove random temp directory name
+ line = re.sub(
+ r"\([^\)\(]*?pyproject\.toml\)",
+ "(pyproject.toml)",
+ line,
+ )
+ file.write(line)
+
+
+def is_file_outdated(old_file: Path, new_file: Path) -> bool:
+ """Compares two lock files and returns True if there is a difference, else False"""
+
+ header_comment = "# pip-compile"
+ outdated = False
+
+ with open(old_file, encoding="utf-8") as old:
+ with open(new_file, encoding="utf-8") as new:
+ old_lines = old.readlines()
+ new_lines = new.readlines()
+ if len(old_lines) != len(new_lines):
+ outdated = True
+ if not outdated:
+ for old_line, new_line in zip(old_lines, new_lines):
+ if old_line.startswith(header_comment):
+ continue
+ if old_line != new_line:
+ outdated = True
+ break
+ if outdated:
+ cli.echo_failure(f"{str(old_file)} is out of date!")
+ return outdated
+
+
+def compile_lock_file(
+ sources: list[Path],
+ output: Path,
+ upgrade: bool,
+ extras: bool,
+) -> None:
+ """From the specified sources compile a lock file using pip-compile from pip-tools
+ and write it to the specified output location.
+ """
+
+ print(f"Updating '{output.name}'...")
+
+ command = [
+ "pip-compile",
+ "--rebuild",
+ "--generate-hashes",
+ "--annotate",
+ ]
+
+ if upgrade:
+ command.append("--upgrade")
+
+ if extras:
+ command.append("--all-extras")
+
+ command.extend(["--output-file", str(output.absolute())])
+
+ command.extend([str(source.absolute()) for source in sources])
+
+ # constrain the production deps by what's pinned in requirements-dev.txt
+ if output.name == OUTPUT_LOCK_PATH.name:
+ command.extend(["-c", str(OUTPUT_DEV_LOCK_PATH)])
+
+ completed_process = subprocess.run(
+ args=command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ check=False,
+ )
+ if completed_process.returncode != 0:
+ std_out = completed_process.stdout
+ log = std_out.decode("utf-8") if std_out else "no log available."
+ raise RuntimeError(f"Failed to compile lock file:\n{log}")
+
+ fix_temp_dir_comments(output.absolute())
+
+
+def ensure_lock_files_exist():
+ """Make sure that the lock files exist if in check mode"""
+ for output in [OUTPUT_DEV_LOCK_PATH, OUTPUT_LOCK_PATH]:
+ if not os.path.exists(output):
+ cli.echo_failure(f"{output} is missing")
+ return
+
+
+def main(upgrade: bool = False, check: bool = False):
+ """Update the dependency lock files located at 'requirements.txt' and
+ 'requirements-dev.txt'.
+
+ For the 'requirements.txt' only the package with its dependencies being defined in
+ the 'pyproject.toml' are considered. Thereby, all recursive dependencies of the
+ package on itself are removed.
+
+ For the 'requirements-dev.txt', in addition to the filtered 'pyproject.toml' the
+ 'requirements-dev.in' is considered.
+
+ The `upgrade` parameter can be used to indicate that dependencies found in existing
+ lock files should be upgraded. Default pip-compile behavior is to leave them as is.
+ """
+
+ # if --check is used, quickly ensure that there is something to compare against
+ if check:
+ ensure_lock_files_exist()
+
+ modified_pyproject = deps.get_modified_pyproject(PYPROJECT_TOML_PATH)
+
+ extras = (
+ "optional-dependencies" in modified_pyproject["project"]
+ and modified_pyproject["project"]["optional-dependencies"]
+ )
+ with TemporaryDirectory() as temp_dir:
+ modified_pyproject_path = Path(temp_dir) / "pyproject.toml"
+ with open(modified_pyproject_path, "wb") as modified_pyproject_toml:
+ tomli_w.dump(modified_pyproject, modified_pyproject_toml)
+
+ # make src dir next to TOML to satisfy build system
+ os.makedirs(Path(temp_dir) / "src")
+
+ # temporary test files
+ check_dev_path = Path(temp_dir) / OUTPUT_DEV_LOCK_PATH.name
+ check_prod_path = Path(temp_dir) / OUTPUT_LOCK_PATH.name
+
+ # compile requirements-dev.txt (includes all dependencies)
+ compile_lock_file(
+ sources=[modified_pyproject_path, DEV_DEPS_PATH],
+ output=check_dev_path if check else OUTPUT_DEV_LOCK_PATH,
+ upgrade=upgrade,
+ extras=extras,
+ )
+
+ if check and is_file_outdated(OUTPUT_DEV_LOCK_PATH, check_dev_path):
+ return
+
+ # compile requirements.txt (only includes production-related subset of above)
+ compile_lock_file(
+ sources=[modified_pyproject_path],
+ output=check_prod_path if check else OUTPUT_LOCK_PATH,
+ upgrade=upgrade,
+ extras=extras,
+ )
+
+ if check and is_file_outdated(OUTPUT_LOCK_PATH, check_prod_path):
+ return
+
+ if check:
+ cli.echo_success("Lock files are up to date.")
+ else:
+ cli.echo_success(
+ f"Successfully updated lock files at '{OUTPUT_LOCK_PATH}' and"
+ + f" '{OUTPUT_DEV_LOCK_PATH}'."
+ )
+
+
+if __name__ == "__main__":
+ cli.run(main)
diff --git a/scripts/update_openapi_docs.py b/scripts/update_openapi_docs.py
index 456f514..c7c049c 100755
--- a/scripts/update_openapi_docs.py
+++ b/scripts/update_openapi_docs.py
@@ -22,6 +22,7 @@
from pathlib import Path
import yaml
+
from script_utils.cli import echo_failure, echo_success, run
from script_utils.fastapi_app_location import app
@@ -72,7 +73,7 @@ def check_docs():
"""
openapi_expected = get_openapi_spec()
- with open(OPENAPI_YAML, "r", encoding="utf-8") as openapi_file:
+ with open(OPENAPI_YAML, encoding="utf-8") as openapi_file:
openapi_observed = openapi_file.read()
if openapi_expected != openapi_observed:
diff --git a/scripts/update_readme.py b/scripts/update_readme.py
index 594aedf..244d0ba 100755
--- a/scripts/update_readme.py
+++ b/scripts/update_readme.py
@@ -24,13 +24,14 @@
from string import Template
import jsonschema2md
+import tomli
from pydantic import BaseModel, Field
-from script_utils.cli import echo_failure, echo_success, run
-from setuptools.config.setupcfg import read_configuration
from stringcase import spinalcase, titlecase
+from script_utils.cli import echo_failure, echo_success, run
+
ROOT_DIR = Path(__file__).parent.parent.resolve()
-SETUP_CFG_PATH = ROOT_DIR / "setup.cfg"
+PYPROJECT_TOML_PATH = ROOT_DIR / "pyproject.toml"
DESCRIPTION_PATH = ROOT_DIR / ".description.md"
DESIGN_PATH = ROOT_DIR / ".design.md"
README_TEMPLATE_PATH = ROOT_DIR / ".readme_template.md"
@@ -89,16 +90,17 @@ class PackageDetails(PackageHeader, PackageName):
)
-def read_package_header() -> PackageHeader:
- """Read basic information about the package from the setup.cfg."""
+def read_toml_package_header() -> PackageHeader:
+ """Read basic information about the package from the pyproject.toml"""
- setup_config = read_configuration(SETUP_CFG_PATH)
- setup_metadata = setup_config["metadata"]
- return PackageHeader(
- shortname=setup_metadata["name"],
- version=setup_metadata["version"],
- summary=setup_metadata["description"],
- )
+ with open(PYPROJECT_TOML_PATH, "rb") as pyproject_toml:
+ pyproject = tomli.load(pyproject_toml)
+ pyproject_project = pyproject["project"]
+ return PackageHeader(
+ shortname=pyproject_project["name"],
+ version=pyproject_project["version"],
+ summary=pyproject_project["description"],
+ )
def read_package_name() -> PackageName:
@@ -141,12 +143,12 @@ def generate_config_docs() -> str:
examples_as_yaml=False,
show_examples="all",
)
- with open(CONFIG_SCHEMA_PATH, "r", encoding="utf-8") as json_file:
+ with open(CONFIG_SCHEMA_PATH, encoding="utf-8") as json_file:
config_schema = json.load(json_file)
md_lines = parser.parse_schema(config_schema)
- # ignore everything before the properites header:
+ # ignore everything before the properties header:
properties_index = md_lines.index("## Properties\n\n")
md_lines = md_lines[properties_index + 1 :]
@@ -173,7 +175,7 @@ def generate_openapi_docs() -> str:
def get_package_details() -> PackageDetails:
"""Get details required to build documentation for the package."""
- header = read_package_header()
+ header = read_toml_package_header()
name = read_package_name()
description = read_package_description()
config_description = generate_config_docs()
diff --git a/scripts/update_template_files.py b/scripts/update_template_files.py
index 952fe2c..d3313fd 100755
--- a/scripts/update_template_files.py
+++ b/scripts/update_template_files.py
@@ -58,7 +58,7 @@ class ValidationError(RuntimeError):
def get_file_list(list_name: str) -> list[str]:
"""Return a list of all file names specified in a given list file."""
list_path = REPO_ROOT_DIR / list_name
- with open(list_path, "r", encoding="utf8") as list_file:
+ with open(list_path, encoding="utf8") as list_file:
file_list = [
clean_line
for clean_line in (
@@ -127,7 +127,7 @@ def check_file(relative_file_path: str, diff: bool = False) -> bool:
print(f" - {local_file_path}: cannot check, remote is missing")
return True
- with open(local_file_path, "r", encoding="utf8") as file:
+ with open(local_file_path, encoding="utf8") as file:
return diff_content(local_file_path, file.read(), template_file_content)
return False
@@ -153,7 +153,7 @@ def update_file(relative_file_path: str, diff: bool = False) -> bool:
return True
if diff and local_file_path.exists():
- with open(local_file_path, "r", encoding="utf8") as file:
+ with open(local_file_path, encoding="utf8") as file:
if file.read() == template_file_content:
return False
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 09930a9..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2021 - 2023 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.
-
-[metadata]
-name = mass
-version = attr: mass.__version__
-description = Metadata Artifact Search Service - A service for searching metadata artifacts and filtering results.
-url = https://github.com/ghga-de/mass
-long_description = file: README.md
-long_description_content_type = text/markdown; charset=UTF-8
-author = German Human Genome Phenome Archive (GHGA)
-author_email = contact@ghga.de
-license = Apache 2.0
-classifiers =
- Operating System :: POSIX :: Linux
- Programming Language :: Python :: 3.9
- License :: OSI Approved :: Apache Software License
- Topic :: Internet :: WWW/HTTP :: HTTP Servers
- Topic :: Scientific/Engineering :: Bio-Informatics
-
-[options]
-zip_safe = False
-include_package_data = True
-packages = find:
-install_requires =
- # Please adapt to the current versions of the libraries
- # and remove the unneeded libraries and extras:
- typer==0.7.0
- ghga-service-commons[api]==0.4.3
- ghga-event-schemas==0.13.4
- hexkit[mongodb,akafka]==0.10.2
-
-python_requires = >= 3.9
-
-[options.entry_points]
-console_scripts =
- mass = mass.__main__:cli
-
-[options.extras_require]
-all =
-
-[options.packages.find]
-exclude = tests
diff --git a/setup.py b/setup.py
deleted file mode 100755
index bee8984..0000000
--- a/setup.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright 2021 - 2023 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.
-
-"""Setup script for pip. This setup configs are specified in the `setup.cfg` file"""
-
-import setuptools
-
-if __name__ == "__main__":
- setuptools.setup()
diff --git a/mass/__init__.py b/src/mass/__init__.py
similarity index 91%
rename from mass/__init__.py
rename to src/mass/__init__.py
index 3b80f67..ef96d40 100644
--- a/mass/__init__.py
+++ b/src/mass/__init__.py
@@ -15,4 +15,6 @@
"""A service for searching metadata artifacts and filtering results."""
-__version__ = "0.3.2"
+from importlib.metadata import version
+
+__version__ = version(__package__)
diff --git a/mass/__main__.py b/src/mass/__main__.py
similarity index 100%
rename from mass/__main__.py
rename to src/mass/__main__.py
diff --git a/mass/adapters/__init__.py b/src/mass/adapters/__init__.py
similarity index 100%
rename from mass/adapters/__init__.py
rename to src/mass/adapters/__init__.py
diff --git a/mass/adapters/inbound/__init__.py b/src/mass/adapters/inbound/__init__.py
similarity index 100%
rename from mass/adapters/inbound/__init__.py
rename to src/mass/adapters/inbound/__init__.py
diff --git a/mass/adapters/inbound/event_sub.py b/src/mass/adapters/inbound/event_sub.py
similarity index 100%
rename from mass/adapters/inbound/event_sub.py
rename to src/mass/adapters/inbound/event_sub.py
diff --git a/mass/adapters/inbound/fastapi_/__init__.py b/src/mass/adapters/inbound/fastapi_/__init__.py
similarity index 100%
rename from mass/adapters/inbound/fastapi_/__init__.py
rename to src/mass/adapters/inbound/fastapi_/__init__.py
diff --git a/mass/adapters/inbound/fastapi_/models.py b/src/mass/adapters/inbound/fastapi_/models.py
similarity index 100%
rename from mass/adapters/inbound/fastapi_/models.py
rename to src/mass/adapters/inbound/fastapi_/models.py
diff --git a/mass/adapters/inbound/fastapi_/routes.py b/src/mass/adapters/inbound/fastapi_/routes.py
similarity index 99%
rename from mass/adapters/inbound/fastapi_/routes.py
rename to src/mass/adapters/inbound/fastapi_/routes.py
index eb60ffa..7a5eca9 100644
--- a/mass/adapters/inbound/fastapi_/routes.py
+++ b/src/mass/adapters/inbound/fastapi_/routes.py
@@ -46,7 +46,6 @@ async def search_options(
search hits for further search refinement. They contain a key, which is used by the
system, and a name, which is more user-friendly.
"""
-
return config.searchable_classes
diff --git a/mass/adapters/outbound/__init__.py b/src/mass/adapters/outbound/__init__.py
similarity index 100%
rename from mass/adapters/outbound/__init__.py
rename to src/mass/adapters/outbound/__init__.py
diff --git a/mass/adapters/outbound/aggregator.py b/src/mass/adapters/outbound/aggregator.py
similarity index 96%
rename from mass/adapters/outbound/aggregator.py
rename to src/mass/adapters/outbound/aggregator.py
index 4fea938..04c0cfb 100644
--- a/mass/adapters/outbound/aggregator.py
+++ b/src/mass/adapters/outbound/aggregator.py
@@ -35,10 +35,10 @@ class Aggregator(AggregatorPort):
"""Concrete implementation of an Aggregator"""
def __init__(self, *, collection):
- """initialize with a MongoDB collection"""
+ """Initialize with a MongoDB collection"""
self._collection = collection
- async def aggregate(
+ async def aggregate( # noqa: PLR0913, D102
self,
*,
query: str,
@@ -113,7 +113,7 @@ async def construct(
):
"""Initialize the Aggregator collection with one Aggregator for each resource class"""
aggregators: dict[str, AggregatorPort] = {}
- for name in config.searchable_classes.keys():
+ for name in config.searchable_classes:
aggregators[name] = aggregator_factory.get_aggregator(name=name)
return cls(aggregators=aggregators)
@@ -131,7 +131,6 @@ def get_aggregator(self, *, class_name: str) -> AggregatorPort:
Raises:
AggregatorNotFoundError: if the aggregator isn't found
"""
-
try:
return self._aggregators[class_name]
except KeyError as err:
diff --git a/mass/adapters/outbound/dao.py b/src/mass/adapters/outbound/dao.py
similarity index 95%
rename from mass/adapters/outbound/dao.py
rename to src/mass/adapters/outbound/dao.py
index d796415..bda2294 100644
--- a/mass/adapters/outbound/dao.py
+++ b/src/mass/adapters/outbound/dao.py
@@ -41,7 +41,7 @@ async def construct(
):
"""Initialize the DAO collection with one DAO for each resource class"""
resource_daos: dict[str, ResourceDao] = {}
- for name in config.searchable_classes.keys():
+ for name in config.searchable_classes:
resource_daos[name] = await dao_factory.get_dao(
name=name, dto_model=models.Resource, id_field="id_"
)
@@ -59,7 +59,7 @@ def __init__(
self._indexes_created = False
def get_dao(self, *, class_name: str) -> ResourceDao:
- """returns a dao for the given resource class name
+ """Returns a dao for the given resource class name
Raises:
DaoNotFoundError: if the DAO isn't found
@@ -69,7 +69,7 @@ def get_dao(self, *, class_name: str) -> ResourceDao:
except KeyError as err:
raise DaoNotFoundError(class_name=class_name) from err
- def create_collections_and_indexes_if_needed(self) -> None:
+ def create_collections_and_indexes_if_needed(self) -> None: # noqa: D102
# This only needs to be done once, so exit if we've already created the indexes
if self._indexes_created:
return
diff --git a/mass/adapters/outbound/utils.py b/src/mass/adapters/outbound/utils.py
similarity index 98%
rename from mass/adapters/outbound/utils.py
rename to src/mass/adapters/outbound/utils.py
index 09298cd..e21d2a2 100644
--- a/mass/adapters/outbound/utils.py
+++ b/src/mass/adapters/outbound/utils.py
@@ -36,7 +36,7 @@ def pipeline_match_text_search(*, query: str) -> JsonObject:
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"""
+ """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:
@@ -49,7 +49,6 @@ def args_for_getfield(*, root_object_name: str, field_name: str) -> tuple[str, s
def pipeline_match_filters_stage(*, filters: list[models.Filter]) -> JsonObject:
"""Build segment of pipeline to apply search filters"""
-
segment: dict[str, dict[str, list[str]]] = defaultdict(lambda: {"$in": []})
for item in filters:
filter_key = "content." + str(item.key)
@@ -118,7 +117,7 @@ def pipeline_project(*, facet_fields: list[models.FacetLabel]) -> JsonObject:
return {"$project": segment}
-def build_pipeline(
+def build_pipeline( # noqa: PLR0913
*,
query: str,
filters: list[models.Filter],
diff --git a/mass/cli.py b/src/mass/cli.py
similarity index 99%
rename from mass/cli.py
rename to src/mass/cli.py
index 73aa383..f5ce871 100644
--- a/mass/cli.py
+++ b/src/mass/cli.py
@@ -27,12 +27,10 @@
@cli.command(name="run-rest")
def sync_run_api():
"""Run the HTTP REST API."""
-
asyncio.run(run_rest())
@cli.command(name="consume-events")
def sync_consume_events(run_forever: bool = True):
"""Run an event consumer listening to the specified topic."""
-
asyncio.run(consume_events(run_forever=run_forever))
diff --git a/mass/config.py b/src/mass/config.py
similarity index 100%
rename from mass/config.py
rename to src/mass/config.py
diff --git a/mass/container.py b/src/mass/container.py
similarity index 100%
rename from mass/container.py
rename to src/mass/container.py
diff --git a/mass/core/__init__.py b/src/mass/core/__init__.py
similarity index 100%
rename from mass/core/__init__.py
rename to src/mass/core/__init__.py
diff --git a/mass/core/models.py b/src/mass/core/models.py
similarity index 100%
rename from mass/core/models.py
rename to src/mass/core/models.py
diff --git a/mass/core/query_handler.py b/src/mass/core/query_handler.py
similarity index 96%
rename from mass/core/query_handler.py
rename to src/mass/core/query_handler.py
index cbef4ef..e4c5e5a 100644
--- a/mass/core/query_handler.py
+++ b/src/mass/core/query_handler.py
@@ -42,7 +42,9 @@ def __init__(
self._aggregator_collection = aggregator_collection
self._dao_collection = dao_collection
- async def load_resource(self, *, resource: models.Resource, class_name: str):
+ async def load_resource( # noqa: D102
+ self, *, resource: models.Resource, class_name: str
+ ):
if class_name not in self._config.searchable_classes:
raise self.ClassNotConfiguredError(class_name=class_name)
@@ -52,7 +54,7 @@ async def load_resource(self, *, resource: models.Resource, class_name: str):
await dao.upsert(resource)
- async def delete_resource(self, *, resource_id: str, class_name: str):
+ async def delete_resource(self, *, resource_id: str, class_name: str): # noqa: D102
if class_name not in self._config.searchable_classes:
raise self.ClassNotConfiguredError(class_name=class_name)
@@ -63,7 +65,7 @@ async def delete_resource(self, *, resource_id: str, class_name: str):
except ResourceNotFoundError as err:
raise self.ResourceNotFoundError(resource_id=resource_id) from err
- async def handle_query(
+ async def handle_query( # noqa: PLR0913, D102
self,
*,
class_name: str,
diff --git a/mass/main.py b/src/mass/main.py
similarity index 95%
rename from mass/main.py
rename to src/mass/main.py
index b1e5f36..d4f5242 100644
--- a/mass/main.py
+++ b/src/mass/main.py
@@ -24,7 +24,6 @@
def get_configured_container(*, config: Config) -> Container:
"""Create and configure a DI container."""
-
container = Container()
container.config.load_config(config)
container.wire(modules=["mass.adapters.inbound.fastapi_.routes"])
@@ -38,7 +37,6 @@ def get_rest_api(*, config: Config) -> FastAPI:
For full functionality of the api, run in the context of a CI container with
correct wiring and initialized resources (see the run_api function below).
"""
-
api = FastAPI()
api.include_router(router=router)
configure_app(api, config=config)
@@ -47,7 +45,7 @@ def get_rest_api(*, config: Config) -> FastAPI:
async def run_rest():
"""Run the server"""
- config = Config()
+ config = Config() # type: ignore[call-arg]
async with get_configured_container(config=config):
api = get_rest_api(config=config)
@@ -56,8 +54,7 @@ async def run_rest():
async def consume_events(run_forever: bool = True):
"""Run the event consumer"""
-
- config = Config()
+ config = Config() # type: ignore[call-arg]
async with get_configured_container(config=config) as container:
event_subscriber = await container.event_subscriber()
diff --git a/mass/ports/__init__.py b/src/mass/ports/__init__.py
similarity index 100%
rename from mass/ports/__init__.py
rename to src/mass/ports/__init__.py
diff --git a/mass/ports/inbound/__init__.py b/src/mass/ports/inbound/__init__.py
similarity index 100%
rename from mass/ports/inbound/__init__.py
rename to src/mass/ports/inbound/__init__.py
diff --git a/mass/ports/inbound/query_handler.py b/src/mass/ports/inbound/query_handler.py
similarity index 98%
rename from mass/ports/inbound/query_handler.py
rename to src/mass/ports/inbound/query_handler.py
index f7aa2eb..7febd0d 100644
--- a/mass/ports/inbound/query_handler.py
+++ b/src/mass/ports/inbound/query_handler.py
@@ -69,7 +69,7 @@ async def delete_resource(self, *, resource_id: str, class_name: str):
"""
@abstractmethod
- async def handle_query(
+ async def handle_query( # noqa: PLR0913
self,
*,
class_name: str,
diff --git a/mass/ports/outbound/__init__.py b/src/mass/ports/outbound/__init__.py
similarity index 100%
rename from mass/ports/outbound/__init__.py
rename to src/mass/ports/outbound/__init__.py
diff --git a/mass/ports/outbound/aggregator.py b/src/mass/ports/outbound/aggregator.py
similarity index 98%
rename from mass/ports/outbound/aggregator.py
rename to src/mass/ports/outbound/aggregator.py
index c50e974..99b8c5c 100644
--- a/mass/ports/outbound/aggregator.py
+++ b/src/mass/ports/outbound/aggregator.py
@@ -35,7 +35,7 @@ class AggregatorPort(ABC):
"""Describes an aggregator class, which performs aggregation ops on a mongodb collection"""
@abstractmethod
- async def aggregate(
+ async def aggregate( # noqa: PLR0913
self,
*,
query: str,
diff --git a/mass/ports/outbound/dao.py b/src/mass/ports/outbound/dao.py
similarity index 95%
rename from mass/ports/outbound/dao.py
rename to src/mass/ports/outbound/dao.py
index bdbe518..4fab46c 100644
--- a/mass/ports/outbound/dao.py
+++ b/src/mass/ports/outbound/dao.py
@@ -40,7 +40,7 @@ def get_dao(self, *, class_name: str) -> ResourceDao:
"""
...
- def create_collections_and_indexes_if_needed(self) -> None:
+ def create_collections_and_indexes_if_needed(self) -> None: # noqa: B027
"""Creates `MongoDB` collections and indexes.
Creates collections for all configured classes in `searchable_classes` if they don't
diff --git a/tests/conftest.py b/tests/conftest.py
index 77c2b4a..26b08a4 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -28,7 +28,7 @@
@pytest_asyncio.fixture(autouse=True)
-async def reset_state(joint_fixture: JointFixture): # noqa: F811
+async def reset_state(joint_fixture: JointFixture):
"""Clear joint_fixture state before tests.
This is a function-level fixture because it needs to run in each test.
diff --git a/tests/fixtures/config.py b/tests/fixtures/config.py
index 7c6dac7..ab2291f 100644
--- a/tests/fixtures/config.py
+++ b/tests/fixtures/config.py
@@ -16,7 +16,7 @@
"""Test config"""
from pathlib import Path
-from typing import Dict, List, Optional
+from typing import Optional
from pydantic.env_settings import BaseSettings
@@ -27,12 +27,13 @@
def get_config(
- sources: Optional[List[BaseSettings]] = None,
+ sources: Optional[list[BaseSettings]] = None,
default_config_yaml: Path = TEST_CONFIG_YAML,
) -> Config:
"""Merges parameters from the default TEST_CONFIG_YAML with params inferred
- from testcontainers."""
- sources_dict: Dict[str, object] = {}
+ from testcontainers.
+ """
+ sources_dict: dict[str, object] = {}
if sources is not None:
for source in sources:
diff --git a/tests/fixtures/joint.py b/tests/fixtures/joint.py
index 36e3ff1..761c550 100644
--- a/tests/fixtures/joint.py
+++ b/tests/fixtures/joint.py
@@ -19,14 +19,14 @@
import glob
import re
+from collections.abc import AsyncGenerator
from dataclasses import dataclass
-from typing import AsyncGenerator
import pytest_asyncio
from ghga_service_commons.api.testing import AsyncTestClient
from hexkit.custom_types import JsonObject
from hexkit.providers.akafka.testutils import KafkaFixture
-from hexkit.providers.mongodb.testutils import MongoDbFixture # noqa: F401
+from hexkit.providers.mongodb.testutils import MongoDbFixture
from pytest_asyncio.plugin import _ScopeName
from mass.config import Config
@@ -80,13 +80,12 @@ async def call_search_endpoint(
async def joint_fixture_function(
- mongodb_fixture: MongoDbFixture, kafka_fixture: KafkaFixture # noqa: F811
+ mongodb_fixture: MongoDbFixture, kafka_fixture: KafkaFixture
) -> AsyncGenerator[JointFixture, None]:
"""A fixture that embeds all other fixtures for API-level integration testing
**Do not call directly** Instead, use get_joint_fixture().
"""
-
# merge configs from different sources with the default one:
config = get_config(sources=[mongodb_fixture.config, kafka_fixture.config])
diff --git a/tests/fixtures/utils.py b/tests/fixtures/utils.py
index f88966c..671643b 100644
--- a/tests/fixtures/utils.py
+++ b/tests/fixtures/utils.py
@@ -34,7 +34,7 @@ def dto_to_document(dto: BaseModel):
def get_resources_from_file(filename: str) -> list[Resource]:
"""Utility function to load resources from a file"""
- with open(filename, "r", encoding="utf-8") as file:
+ with open(filename, encoding="utf-8") as file:
json_object = json.loads(file.read())
resources: list[Resource] = []
for item in json_object["items"]:
diff --git a/tests/test_api.py b/tests/test_api.py
index 3318a8f..99b5074 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -32,7 +32,7 @@ def compare(
count: int,
hit_length: int,
hits: Optional[list[models.Resource]] = None,
- facets: Optional[list[models.Facet]] = None
+ facets: Optional[list[models.Facet]] = None,
) -> None:
"""Perform common comparisons for results"""
assert results.count == count
@@ -54,7 +54,6 @@ def compare(
@pytest.mark.asyncio
async def test_search_options(joint_fixture: JointFixture):
"""Verify that we can request the configured resource class information correctly"""
-
response = await joint_fixture.rest_client.get(url="/rpc/search-options")
assert response.json() == joint_fixture.config.searchable_classes
@@ -62,7 +61,6 @@ async def test_search_options(joint_fixture: JointFixture):
@pytest.mark.asyncio
async def test_malformed_document(joint_fixture: JointFixture):
"""Test behavior from API perspective upon querying when bad doc exists"""
-
joint_fixture.remove_db_data()
query_handler = await joint_fixture.container.query_handler()
@@ -130,7 +128,7 @@ async def test_search_with_limit(joint_fixture: JointFixture):
"type": "resort",
},
}
- hits = [models.Resource(**hit)]
+ hits = [models.Resource(**hit)] # type: ignore[arg-type]
compare(results=results, count=3, hit_length=1, hits=hits)
diff --git a/tests/test_consumer.py b/tests/test_consumer.py
index f48d204..fb19d16 100644
--- a/tests/test_consumer.py
+++ b/tests/test_consumer.py
@@ -34,7 +34,6 @@ async def test_resource_upsert(
joint_fixture: JointFixture, resource_id: str, is_insert: bool
):
"""Try upserting with no pre-existing resource with matching ID (i.e. insert)"""
-
query_handler = await joint_fixture.container.query_handler()
# get all the documents in the collection
diff --git a/tests/test_logging.py b/tests/test_logging.py
index ed98295..b340d12 100644
--- a/tests/test_logging.py
+++ b/tests/test_logging.py
@@ -21,8 +21,9 @@
import pytest
from ghga_event_schemas.pydantic_ import (
MetadataDatasetID, # used for intentionally failing validation
+ SearchableResource,
+ SearchableResourceInfo,
)
-from ghga_event_schemas.pydantic_ import SearchableResource, SearchableResourceInfo
from mass.adapters.inbound.event_sub import (
CLASS_NOT_CONFIGURED_LOG_MSG,
diff --git a/tests/test_resources.py b/tests/test_resources.py
index f324e5e..f5b9c38 100644
--- a/tests/test_resources.py
+++ b/tests/test_resources.py
@@ -26,7 +26,6 @@
@pytest.mark.asyncio
async def test_basic_query(joint_fixture: JointFixture):
"""Make sure we can pull back the documents as expected"""
-
# pull back all 3 test documents
query_handler = await joint_fixture.container.query_handler()
results = await query_handler.handle_query(
@@ -39,7 +38,6 @@ async def test_basic_query(joint_fixture: JointFixture):
@pytest.mark.asyncio
async def test_text_search(joint_fixture: JointFixture):
"""Test basic text search"""
-
query_handler = await joint_fixture.container.query_handler()
results_text = await query_handler.handle_query(
class_name="DatasetEmbedded", query="poolside", filters=[]
@@ -52,7 +50,6 @@ async def test_text_search(joint_fixture: JointFixture):
@pytest.mark.asyncio
async def test_filters_work(joint_fixture: JointFixture):
"""Test a query with filters selected but no query string"""
-
query_handler = await joint_fixture.container.query_handler()
results_filtered = await query_handler.handle_query(
class_name="DatasetEmbedded",
@@ -142,7 +139,7 @@ async def test_skip_parameter(joint_fixture: JointFixture):
@pytest.mark.asyncio
async def test_all_parameters(joint_fixture: JointFixture):
- """sanity check - make sure it all works together"""
+ """Sanity check - make sure it all works together"""
query_handler = await joint_fixture.container.query_handler()
results_all = await query_handler.handle_query(
class_name="DatasetEmbedded",
@@ -255,7 +252,6 @@ async def test_resource_deletion(joint_fixture: JointFixture):
Verify that the targeted resource is deleted and nothing else.
"""
-
query_handler = await joint_fixture.container.query_handler()
all_resources = await query_handler.handle_query(
class_name="DatasetEmbedded", query="", filters=[]
@@ -280,7 +276,6 @@ async def test_resource_deletion(joint_fixture: JointFixture):
@pytest.mark.asyncio
async def test_resource_deletion_failure(joint_fixture: JointFixture):
"""Test for correct error when failing to delete a resource"""
-
query_handler = await joint_fixture.container.query_handler()
all_resources = await query_handler.handle_query(
class_name="DatasetEmbedded", query="", filters=[]
diff --git a/tests/test_sorting.py b/tests/test_sorting.py
index 2f8fe2a..46bb21f 100644
--- a/tests/test_sorting.py
+++ b/tests/test_sorting.py
@@ -74,7 +74,6 @@ def multi_column_sort(
@pytest.mark.asyncio
async def test_api_without_sort_parameters(joint_fixture: JointFixture):
"""Make sure default Pydantic model parameter works as expected"""
-
search_parameters: JsonObject = {
"class_name": CLASS_NAME,
"query": "",
@@ -147,7 +146,6 @@ async def test_sort_with_invalid_field(joint_fixture: JointFixture):
value for it. If we sort with a truly invalid field, it should have no impact on the
resulting sort order.
"""
-
search_parameters: JsonObject = {
"class_name": CLASS_NAME,
"query": "",