commit 6d955c99250fbeb40a59deb2421c5e6a5b448e25 Author: Mustafa Gezen Date: Wed Feb 1 22:37:16 2023 +0100 Initial commit diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.bazelignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..e3f894c --- /dev/null +++ b/.bazelrc @@ -0,0 +1,35 @@ +# Java +test --test_env='LC_ALL=en_US.UTF-8' +test --test_env='LANG=en_US.UTF-8' +test --jvmopt='-Dsun.jnu.encoding=UTF-8' +test --jvmopt='-Dfile.encoding=UTF-8' +build --test_env='LC_ALL=en_US.UTF-8' +build --jvmopt='-Dsun.jnu.encoding=UTF-8' +build --jvmopt='-Dfile.encoding=UTF-8' +build --test_env='LANG=en_US.UTF-8' +test --test_env=PATH + +build --java_language_version=11 +test --java_language_version=11 + +# C++ +build --client_env=CC=clang +build --copt=-DGRPC_BAZEL_BUILD +build --cxxopt='-std=c++14' +build --action_env=GRPC_BAZEL_RUNTIME=1 +build --define=use_fast_cpp_protos=true + +# Just build tests when testing +test --build_tests_only + +build --incompatible_strict_action_env=true + +# Minimize what is downloaded +build:inmemory --experimental_inmemory_jdeps_files +build:inmemory --experimental_inmemory_dotd_files + +# Minimize what is downloaded +build:toplevel --config=inmemory +build:toplevel --experimental_remote_download_outputs=toplevel + +build --stamp=true \ No newline at end of file diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..1e20ec3 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +5.4.0 \ No newline at end of file diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2593ef5 --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: Google \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000..53c736d --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DATABASE_URL="postgres://postgres:postgres@localhost:5432/apollo2development?sslmode=disable" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87f3f8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.idea +.ijwb +/.*.venv +.venv +*.pyc +bazel-* +__pycache__ +node_modules \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..84ff079 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +hoist=false diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..1fb73bc --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 2, + "singleQuote": true +} diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..36e12fa --- /dev/null +++ b/.pylintrc @@ -0,0 +1,432 @@ +# This Pylint rcfile contains a best-effort configuration to uphold the +# best-practices and style described in the Google Python style guide: +# https://google.github.io/styleguide/pyguide.html +# +# Its canonical open-source location is: +# https://google.github.io/styleguide/pylintrc + +[MASTER] + +# Files or directories to be skipped. They should be base names, not paths. +ignore-paths=third_party + +# Files or directories matching the regex patterns are skipped. The regex +# matches against base names, not paths. +ignore-patterns=^third_party/.*$, + ^generated/.*$ + +# Pickle collected data for later comparisons. +persistent=no + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins=tortoise.contrib.pylint + +# Use multiple processes to speed up Pylint. +jobs=4 + +# 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= + +# 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= + +# 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=abstract-method, + apply-builtin, + arguments-differ, + attribute-defined-outside-init, + backtick, + bad-option-value, + basestring-builtin, + buffer-builtin, + c-extension-no-member, + consider-using-enumerate, + cmp-builtin, + cmp-method, + coerce-builtin, + coerce-method, + delslice-method, + div-method, + duplicate-code, + eq-without-hash, + execfile-builtin, + file-builtin, + filter-builtin-not-iterating, + fixme, + getslice-method, + global-statement, + hex-method, + idiv-method, + implicit-str-concat, + import-error, + import-self, + import-star-module-level, + inconsistent-return-statements, + input-builtin, + intern-builtin, + invalid-str-codec, + locally-disabled, + long-builtin, + long-suffix, + map-builtin-not-iterating, + misplaced-comparison-constant, + missing-function-docstring, + missing-module-docstring, + missing-class-docstring, + metaclass-assignment, + next-method-called, + next-method-defined, + no-absolute-import, + no-else-break, + no-else-continue, + no-else-raise, + no-else-return, + no-init, # added + no-member, + no-name-in-module, + no-self-use, + nonzero-method, + oct-method, + old-division, + old-ne-operator, + old-octal-literal, + old-raise-syntax, + parameter-unpacking, + print-statement, + raising-string, + range-builtin-not-iterating, + raw_input-builtin, + rdiv-method, + reduce-builtin, + relative-import, + reload-builtin, + round-builtin, + setslice-method, + signature-differs, + standarderror-builtin, + suppressed-message, + sys-max-int, + too-few-public-methods, + too-many-ancestors, + too-many-arguments, + too-many-boolean-expressions, + too-many-branches, + too-many-instance-attributes, + too-many-locals, + too-many-nested-blocks, + too-many-public-methods, + too-many-return-statements, + too-many-statements, + trailing-newlines, + unichr-builtin, + unicode-builtin, + unnecessary-pass, + unpacking-in-except, + useless-else-on-loop, + useless-object-inheritance, + useless-suppression, + using-cmp-argument, + wrong-import-order, + xrange-builtin, + zip-builtin-not-iterating, + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This 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= + + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +good-names=main,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl + +# Regular expression matching correct function names +function-rgx=^(?:(?PsetUp|tearDown|setUpModule|tearDownModule)|(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct constant names +const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ + +# Regular expression matching correct attribute names +attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ + +# Regular expression matching correct argument names +argument-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=^_?[A-Z][a-zA-Z0-9]*$ + +# Regular expression matching correct module names +module-rgx=^(_?[a-z][a-z0-9_]*|__init__|__main__)$ + +# Regular expression matching correct method names +method-rgx=(?x)^(?:(?P_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P_{0,2}[a-z][a-z0-9_]*))$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=10 + + +[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,contextlib2.contextmanager + +# 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 + +# 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= + +# 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 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= + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=160 + +# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt +# lines made too long by directives to pytype. + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=(?x)( + ^\s*(\#\ )??$| + ^\s*(from\s+\S+\s+)?import\s+.+$) + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=yes + +# Maximum number of lines in a module +max-module-lines=99999 + +# String used as indentation unit. The internal Google style guide mandates 2 +# spaces. Google's externaly-published style guide says 4, consistent with +# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google +# projects (like TensorFlow). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=TODO + + +[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=yes + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-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 + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging,absl.logging,tensorflow.io.logging + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub, + TERMIOS, + Bastion, + rexec, + sets + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in 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, absl + +# 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 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp + +# 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, + class_ + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=StandardError, + Exception, + BaseException \ No newline at end of file diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000..3ca1dfc --- /dev/null +++ b/.style.yapf @@ -0,0 +1,3 @@ +[style] +based_on_style = facebook +indent_width = 4 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c51e800 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,18 @@ +{ + "python.analysis.extraPaths": [ + "bazel-bin", + ".venv/lib/python3.9/site-packages" + ], + "protoc": { + "options": ["--proto_path=third_party/googleapis"] + }, + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.provider": "yapf", + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.formatOnSave": true, + "[python]": { + "editor.tabSize": 4 + } +} diff --git a/.yapfignore b/.yapfignore new file mode 100644 index 0000000..f90d6ad --- /dev/null +++ b/.yapfignore @@ -0,0 +1,2 @@ +generated/**/*.py +third_party/**/*.py \ No newline at end of file diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..7ceacb0 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,79 @@ +load("@bazel_gazelle//:def.bzl", "gazelle", "gazelle_binary") +load("@pypi//:requirements.bzl", "all_whl_requirements") +load("@rules_python_gazelle_plugin//:def.bzl", "GAZELLE_PYTHON_RUNTIME_DEPS") +load("@rules_python_gazelle_plugin//manifest:defs.bzl", "gazelle_python_manifest") +load("@rules_python_gazelle_plugin//modules_mapping:def.bzl", "modules_mapping") +load("@rules_python//python:defs.bzl", "py_runtime", "py_runtime_pair") +load("@rules_python//python/pip_install:requirements.bzl", "compile_pip_requirements") +load("@npm//:defs.bzl", "npm_link_all_packages") + +# gazelle:prefix github.com/rocky-linux/peridot +# gazelle:go_generate_proto false + +# gazelle:python_library_naming_convention $package_name$_lib +# gazelle:python_binary_naming_convention $package_name$ +# gazelle:map_kind py_binary py_binary @aspect_rules_py//py:defs.bzl +# gazelle:map_kind py_library py_library @aspect_rules_py//py:defs.bzl + +# gazelle:exclude *.venv +# gazelle:exclude third_party/googleapis + +# gazelle:resolve py grpc @pypi_grpcio//:pkg +# gazelle:resolve py apollo.proto.v1.apollo_pb2_grpc //apollo/proto/v1:apollopb_py_pb2_grpc + +gazelle_python_manifest( + name = "gazelle_python_manifest", + modules_mapping = ":modules_map", + pip_repository_name = "pypi", + requirements = "requirements_lock.txt", +) + +gazelle_binary( + name = "gazelle_bin", + languages = [ + "@bazel_gazelle//language/go", + "@bazel_gazelle//language/proto", + "@rules_python_gazelle_plugin//python", + ], +) + +gazelle( + name = "gazelle", + data = GAZELLE_PYTHON_RUNTIME_DEPS, + gazelle = ":gazelle_bin", +) + +compile_pip_requirements( + name = "requirements", + extra_args = ["--allow-unsafe"], + requirements_in = "requirements.txt", + requirements_txt = "requirements_lock.txt", +) + +modules_mapping( + name = "modules_map", + wheels = all_whl_requirements, +) + +py_runtime( + name = "container_py3_runtime", + interpreter_path = "/usr/bin/python3", + python_version = "PY3", +) + +py_runtime_pair( + name = "container_py_runtime_pair", + py2_runtime = None, + py3_runtime = ":container_py3_runtime", +) + +toolchain( + name = "container_py_toolchain", + exec_compatible_with = [ + "@io_bazel_rules_docker//platforms:run_in_container", + ], + toolchain = ":container_py_runtime_pair", + toolchain_type = "@bazel_tools//tools/python:toolchain_type", +) + +npm_link_all_packages(name = "node_modules") diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..ada909e --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,217 @@ +workspace( + name = "depot", +) + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +# Aspect Bazel Lib +http_archive( + name = "aspect_bazel_lib", + sha256 = "79623d656aa23ad3fd4692ab99786c613cd36e49f5566469ed97bc9b4c655f03", + strip_prefix = "bazel-lib-1.23.3", + url = "https://github.com/aspect-build/bazel-lib/archive/refs/tags/v1.23.3.tar.gz", +) + +load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies", "register_copy_directory_toolchains", "register_copy_to_directory_toolchains") + +aspect_bazel_lib_dependencies() + +# Python +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "aspect_rules_py", + sha256 = "66da30b09cf47ee40f2ae1c46346cc9a412940965d04899bd68d06a9d3380085", + strip_prefix = "rules_py-0.1.0", + url = "https://github.com/aspect-build/rules_py/archive/refs/tags/v0.1.0.tar.gz", +) + +# Fetches the rules_py dependencies. +# If you want to have a different version of some dependency, +# you should fetch it *before* calling this. +# Alternatively, you can skip calling this function, so long as you've +# already fetched all the dependencies. +load("@aspect_rules_py//py:repositories.bzl", "rules_py_dependencies") + +http_archive( + name = "rules_python", + patch_args = ["-p1"], + patch_cmds = ["""\ +cat >> python/BUILD.bazel < list[PortalProduct]: + if self.__products: + return self.__products + + self.__products = [] + for product in self.portal_product_filter: + try: + if product.startswith("Red Hat Enterprise Linux"): + variant, name, version, arch = product.split("|") + major_version = version + if "." in version: + version_split = version.split(".") + major_version = int(version_split[0]) + minor_version = int(version_split[1]) + else: + major_version = int(version) + minor_version = None + self.__products.append( + PortalProduct( + variant, name, major_version, minor_version, arch + ) + ) + except ValueError: + pass + + return self.__products + + def affects_rhel_version_arch( + self, major_version: int, minor_version: int | None, arch: Architecture + ) -> bool: + """ + Returns whether this advisory affects the given RHEL version and architecture. + """ + for product in self.get_products(): + if product.variant == "Red Hat Enterprise Linux" and product.major_version == major_version and product.minor_version == minor_version and product.arch == arch.value: + return True + + return False + + +class API: + """ + The Red Hat Errata API. + """ + + url = None + + def __init__(self, url=DEFAULT_URL): + if not url: + url = DEFAULT_URL + self.url = url + + async def search( + self, + kind: DocumentKind = DocumentKind.ERRATA, + sort_asc: bool = False, + rows: int = 10, + query: str = "*:*", + distro: str = "Red%5C+Hat%5C+Enterprise%5C+Linux%7C%2A%7C%2A%7C%2A", + detected_product: str = "rhel", + from_date: str = None + ) -> list[Advisory]: + params = "" + + # Set query + params += f"q={query}" + + # Set rows + params += f"&rows={rows}" + + # Set sorting + sorting = "portal_publication_date+asc" if sort_asc else "portal_publication_date+desc" + params += f"&sort={sorting}" + + # Set start + params += "&start=0" + + # Set distribution + params += f"&fq=portal_product_filter:{distro}" + + # Set from-to + if from_date: + params += f"&fq=portal_publication_date%3A%5B{quote(from_date)}%20TO%20NOW%5D" + + # Set document kind + params += f"&fq=documentKind:{kind.value}" + + # Set detected product + if detected_product: + params += f"&fq=detectedProducts:{detected_product}" + + async with aiohttp.ClientSession() as session: + async with session.get( + URL(f"{self.url}?{params}", encoded=True) + ) as response: + body = await response.json() + if response.status != 200: + raise Exception((await response.text())) + elif body.get("response", {}).get("numFound", 0) == 0: + return [] + return Advisory.from_list(list(body["response"]["docs"])) diff --git a/apollo/rherrata/example.py b/apollo/rherrata/example.py new file mode 100644 index 0000000..9d97fa9 --- /dev/null +++ b/apollo/rherrata/example.py @@ -0,0 +1,34 @@ +import asyncio +import datetime +from __init__ import API, Architecture + + +async def main(): + api = API() + res = await api.search( + detected_product="rhel", + rows=1000, + from_date="2019-05-05T22:00:00Z", + sort_asc=True + ) + contains_9 = 0 + contains_8 = 0 + contains_90eus = 0 + for advisory in res: + if advisory.affects_rhel_version_arch(9, None, Architecture.X86_64): + print(f"{advisory.id} affects RHEL 9 x86_64") + contains_9 += 1 + elif advisory.affects_rhel_version_arch(9, 0, Architecture.X86_64): + print(f"{advisory.id} affects RHEL 9.0 EUS x86_64") + contains_90eus += 1 + elif advisory.affects_rhel_version_arch(8, None, Architecture.X86_64): + print(f"{advisory.id} affects RHEL 8 x86_64") + contains_8 += 1 + print(f"Found {contains_9} advisories that affect RHEL 9 x86_64") + print(f"Found {contains_8} advisories that affect RHEL 8 x86_64") + print(f"Found {contains_90eus} advisories that affect RHEL 9.0 EUS x86_64") + print(f"Found {len(res)} advisories in total") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/apollo/rhwebscraper/BUILD.bazel b/apollo/rhwebscraper/BUILD.bazel new file mode 100644 index 0000000..b891d6e --- /dev/null +++ b/apollo/rhwebscraper/BUILD.bazel @@ -0,0 +1,14 @@ +load("@aspect_rules_py//py:defs.bzl", "py_library") + +# gazelle:exclude example.py + +py_library( + name = "rhwebscraper_lib", + srcs = ["__init__.py"], + imports = ["../.."], + visibility = ["//:__subpackages__"], + deps = [ + "@pypi_aiohttp//:pkg", + "@pypi_beautifulsoup4//:pkg", + ], +) diff --git a/apollo/rhwebscraper/__init__.py b/apollo/rhwebscraper/__init__.py new file mode 100644 index 0000000..4334071 --- /dev/null +++ b/apollo/rhwebscraper/__init__.py @@ -0,0 +1,50 @@ +from typing import Optional +from dataclasses import dataclass + +import aiohttp +from bs4 import BeautifulSoup + + +@dataclass +class RHWebAdvisoryFix: + id: str + description: str + + +@dataclass +class PartialRHWebAdvisory: + name: str + topic: Optional[str] + fixes: Optional[list[RHWebAdvisoryFix]] + + +async def get_advisory_topic_and_fixes( + advisory_name: str +) -> Optional[PartialRHWebAdvisory]: + async with aiohttp.ClientSession() as session: + async with session.get( + f"https://access.redhat.com/errata/{advisory_name}" + ) as response: + if response.status == 200: + html = await response.text() + soup = BeautifulSoup(html, "html.parser") + + topic = soup.select("div#topic > p") + if topic: + topic = "\n\n".join([p.text for p in topic]) + + parsed_fixes = [] + fixes = soup.select("div#fixes > ul > li") + if fixes: + for fix in fixes: + bugzilla_id = fix.find("a").attrs.get("href" + ).split("id=")[1] + description = fix.find("a").next_sibling.text.strip( + ).removeprefix("- ") + parsed_fixes.append( + RHWebAdvisoryFix(bugzilla_id, description) + ) + + return PartialRHWebAdvisory(advisory_name, topic, parsed_fixes) + else: + return None diff --git a/apollo/rhwebscraper/example.py b/apollo/rhwebscraper/example.py new file mode 100644 index 0000000..67624dd --- /dev/null +++ b/apollo/rhwebscraper/example.py @@ -0,0 +1,12 @@ +import asyncio + +from __init__ import get_advisory_topic_and_fixes + + +async def main(): + advisory = await get_advisory_topic_and_fixes("RHSA-2023:0536") + print(advisory) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/apollo/rhworker/BUILD.bazel b/apollo/rhworker/BUILD.bazel new file mode 100644 index 0000000..c2d6349 --- /dev/null +++ b/apollo/rhworker/BUILD.bazel @@ -0,0 +1,35 @@ +load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_library") + +py_binary( + name = "rhworker", + srcs = ["__main__.py"], + imports = ["../.."], + main = "__main__.py", + visibility = ["//:__subpackages__"], + deps = [ + ":rhworker_lib", + "//common:common_lib", + "@pypi_click//:pkg", + "@pypi_temporalio//:pkg", + ], +) + +py_library( + name = "rhworker_lib", + srcs = [ + "poll_rh_activities.py", + "poll_rh_workflow.py", + "temporal.py", + ], + imports = ["../.."], + visibility = ["//:__subpackages__"], + deps = [ + "//apollo/db:db_lib", + "//apollo/rherrata:rherrata_lib", + "//apollo/rhwebscraper:rhwebscraper_lib", + "//common:common_lib", + "@pypi_aiohttp//:pkg", + "@pypi_temporalio//:pkg", + "@pypi_tortoise_orm//:pkg", + ], +) diff --git a/apollo/rhworker/__main__.py b/apollo/rhworker/__main__.py new file mode 100644 index 0000000..254b74c --- /dev/null +++ b/apollo/rhworker/__main__.py @@ -0,0 +1,51 @@ +""" +Apollo RH Temporal Worker + +This worker only executes tasks that are related to Red Hat. +""" +import asyncio + +from temporalio.worker import Worker +import click + +from common.database import Database +from common.info import Info +from common.logger import Logger +from common.temporal import Temporal + +from apollo.rhworker.temporal import TASK_QUEUE +from apollo.rhworker.poll_rh_workflow import PollRHAdvisoriesWorkflow +from apollo.rhworker.poll_rh_activities import get_last_indexed_date, get_rh_advisories + + +async def run(): + db = Database(True) + await db.init(["apollo.db"]) + + temporal = Temporal(True) + await temporal.connect() + + worker = Worker( + temporal.client, + task_queue=TASK_QUEUE, + workflows=[ + PollRHAdvisoriesWorkflow, + ], + activities=[ + get_last_indexed_date, + get_rh_advisories, + ] + ) + + await worker.run() + + +@click.command() +def main(): + Info("apollorhworker", "apollo2") + Logger() + asyncio.run(run()) + + +if __name__ == "__main__": + main() diff --git a/apollo/rhworker/poll_rh_activities.py b/apollo/rhworker/poll_rh_activities.py new file mode 100644 index 0000000..666aeff --- /dev/null +++ b/apollo/rhworker/poll_rh_activities.py @@ -0,0 +1,230 @@ +import datetime +import re +import bz2 +from typing import Optional +from xml.etree import ElementTree as ET + +import aiohttp +from temporalio import activity +from tortoise.transactions import in_transaction + +from apollo.db import RedHatIndexState, RedHatAdvisory, RedHatAdvisoryPackage +from apollo.db import RedHatAdvisoryBugzillaBug, RedHatAdvisoryAffectedProduct, RedHatAdvisoryCVE +from apollo.rherrata import API + +from common.logger import Logger + +OVAL_NS = {"": "http://oval.mitre.org/XMLSchema/oval-definitions-5"} +bz_re = re.compile(r"BZ#([0-9]+)") + + +def parse_red_hat_date(rhdate: str) -> datetime.datetime: + return datetime.datetime.fromisoformat(rhdate.removesuffix("Z")) + + +@activity.defn +async def get_last_indexed_date() -> Optional[str]: + state = await RedHatIndexState.get_or_none() + return re.sub( + r"\+\d\d:\d\d", "", + state.last_indexed_at.isoformat("T") + "Z" + ) if state else None + + +async def fetch_mapped_oval() -> dict[str, ET.ElementTree]: + # Download the oval_url using aiohttp, decompress using bzip and parse + oval_url = "https://access.redhat.com/security/data/oval/com.redhat.rhsa-all.xml.bz2" + async with aiohttp.ClientSession() as session: + async with session.get(oval_url) as response: + if response.status == 200: + data = await response.read() + tree = ET.fromstring(bz2.decompress(data)) + + # Index by advisory name + def_map = {} + definitions = tree.findall("definitions/definition", OVAL_NS) + for definition in definitions: + def_id = definition.attrib["id"] + id_split = def_id.split(":") + name = f"{id_split[1].split('.')[2].upper()}-{id_split[3][0:4]}:{id_split[3][4:]}" + def_map[name] = definition + + return def_map + else: + raise Exception("Failed to fetch OVAL data") + + +@activity.defn +async def get_rh_advisories(from_timestamp: str = None) -> None: + logger = Logger() + advisories = await API().search( + from_date=from_timestamp, rows=10000, sort_asc=True + ) + oval = await fetch_mapped_oval() + + for advisory in advisories: + async with in_transaction(): + advisory_last_indexed_at = parse_red_hat_date( + advisory.portal_publication_date + ) + state = await RedHatIndexState.first() + if state: + state.last_indexed_at = advisory_last_indexed_at + await state.save() + else: + await RedHatIndexState().create( + last_index_at=advisory_last_indexed_at + ) + + logger.info("Processing advisory %s", advisory.id) + + existing_advisory = await RedHatAdvisory.filter(name=advisory.id + ).get_or_none() + if existing_advisory: + logger.info("Advisory %s already exists, skipping", advisory.id) + continue + + kind = "Security" + if "Enhancement" in advisory.portal_advisory_type: + kind = "Enhancement" + elif "Bug Fix" in advisory.portal_advisory_type: + kind = "Bug Fix" + + issued_at = parse_red_hat_date(advisory.portal_publication_date) + severity = advisory.portal_severity + if not severity or severity == "": + severity = "None" + + ra = await RedHatAdvisory.create( + name=advisory.id, + red_hat_issued_at=issued_at, + synopsis=advisory.portal_synopsis, + description=advisory.portal_description, + kind=kind, + severity=severity, + topic="", + ) + + if advisory.portal_package: + await RedHatAdvisoryPackage.bulk_create( + [ + RedHatAdvisoryPackage( + **{ + "red_hat_advisory_id": ra.id, + "nevra": nevra + } + ) for nevra in advisory.portal_package + ], + ignore_conflicts=True + ) + + if advisory.portal_CVE: + cves_to_save = [] + + definition = oval.get(advisory.id) + if not definition: + # Fill in CVEs from Errata + for advisory_cve in advisory.portal_CVE: + cves_to_save.append( + RedHatAdvisoryCVE( + **{ + "red_hat_advisory_id": ra.id, + "cve": advisory_cve, + "cvss3_scoring_vector": "UNKNOWN", + "cvss3_base_score": "UNKNOWN", + "cwe": "UNKNOWN", + } + ) + ) + else: + # Fetch CVEs from the OVAL + cves = definition.findall("metadata/advisory/cve", OVAL_NS) + for cve in cves: + cvss3_scoring_vector = "UNKNOWN" + cvss3_base_score = "UNKNOWN" + + cvss3 = cve.attrib.get("cvss3") + if cvss3: + cvss3_raw = cvss3.split("/", 1) + cvss3_scoring_vector = cvss3_raw[ + 1] if cvss3_raw else "UNKNOWN" + cvss3_base_score = cvss3_raw[ + 0] if cvss3_raw else "UNKNOWN" + + cwe = cve.attrib.get("cwe") + if not cwe: + cwe = "UNKNOWN" + + cves_to_save.append( + RedHatAdvisoryCVE( + **{ + "red_hat_advisory_id": + ra.id, + "cve": + cve.text, + "cvss3_scoring_vector": + cvss3_scoring_vector, + "cvss3_base_score": + cvss3_base_score, + "cwe": + cwe, + } + ) + ) + + if not cves_to_save: + raise Exception(f"Failed to find CVEs for {advisory.id}") + + await RedHatAdvisoryCVE.bulk_create( + cves_to_save, + ignore_conflicts=True, + ) + + if advisory.portal_BZ: + bz_map = {} + if advisory.portal_description: + for line in advisory.portal_description.splitlines(): + search = bz_re.search(line) + if search: + bz_id = search.group(1) + bz_line = line.removeprefix("* ") + bz_line = line.removeprefix("* ") + bz_line = line.removeprefix("- ") + bz_line = line.replace(f"(BZ#{bz_id})", "") + bz_line = bz_line.strip() + bz_map[bz_id] = bz_line + + await RedHatAdvisoryBugzillaBug.bulk_create( + [ + RedHatAdvisoryBugzillaBug( + **{ + "red_hat_advisory_id": ra.id, + "bugzilla_bug_id": bugzilla_bug_id, + "description": bz_map.get(bugzilla_bug_id, ""), + } + ) for bugzilla_bug_id in advisory.portal_BZ + ], + ignore_conflicts=True + ) + + affected_products = advisory.get_products() + if affected_products: + await RedHatAdvisoryAffectedProduct.bulk_create( + [ + RedHatAdvisoryAffectedProduct( + **{ + "red_hat_advisory_id": ra.id, + "variant": product.variant, + "name": product.name, + "major_version": product.major_version, + "minor_version": product.minor_version, + "arch": product.arch + } + ) for product in affected_products + ], + ignore_conflicts=True + ) + + logger.info("Processed advisory %s", advisory.id) + + return None diff --git a/apollo/rhworker/poll_rh_workflow.py b/apollo/rhworker/poll_rh_workflow.py new file mode 100644 index 0000000..c303cfd --- /dev/null +++ b/apollo/rhworker/poll_rh_workflow.py @@ -0,0 +1,24 @@ +import datetime + +from temporalio import workflow + + +@workflow.defn +class PollRHAdvisoriesWorkflow: + """ + Polls Red Hat Errata for new advisories. + """ + @workflow.run + async def run(self) -> None: + from_timestamp = await workflow.execute_activity( + "get_last_indexed_date", + start_to_close_timeout=datetime.timedelta(seconds=20), + ) + + await workflow.execute_activity( + "get_rh_advisories", + from_timestamp, + start_to_close_timeout=datetime.timedelta(hours=2), + ) + + return None diff --git a/apollo/rhworker/temporal.py b/apollo/rhworker/temporal.py new file mode 100644 index 0000000..3816928 --- /dev/null +++ b/apollo/rhworker/temporal.py @@ -0,0 +1 @@ +TASK_QUEUE = "v2-rhworker" diff --git a/apollo/rpmworker/BUILD.bazel b/apollo/rpmworker/BUILD.bazel new file mode 100644 index 0000000..8dafce7 --- /dev/null +++ b/apollo/rpmworker/BUILD.bazel @@ -0,0 +1,35 @@ +load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_library") + +py_library( + name = "rpmworker_lib", + srcs = [ + "repomd.py", + "rh_matcher_activities.py", + "rh_matcher_workflows.py", + "temporal.py", + ], + imports = ["../.."], + visibility = ["//:__subpackages__"], + deps = [ + "//apollo/db:db_lib", + "//common:common_lib", + "@pypi_aiohttp//:pkg", + "@pypi_pyyaml//:pkg", + "@pypi_temporalio//:pkg", + "@pypi_tortoise_orm//:pkg", + ], +) + +py_binary( + name = "rpmworker", + srcs = ["__main__.py"], + imports = ["../.."], + main = "__main__.py", + visibility = ["//:__subpackages__"], + deps = [ + ":rpmworker_lib", + "//common:common_lib", + "@pypi_click//:pkg", + "@pypi_temporalio//:pkg", + ], +) diff --git a/apollo/rpmworker/__main__.py b/apollo/rpmworker/__main__.py new file mode 100644 index 0000000..7e0e0b2 --- /dev/null +++ b/apollo/rpmworker/__main__.py @@ -0,0 +1,51 @@ +""" +Apollo RPM Temporal Worker + +This worker only executes tasks that are related to RPMs. +""" +import asyncio + +from temporalio.worker import Worker +import click + +from apollo.rpmworker.rh_matcher_workflows import RhMatcherWorkflow, RhDefunctWorkflow +from apollo.rpmworker.rh_matcher_activities import get_supported_products_with_rh_mirrors, match_rh_repos, block_remaining_rh_advisories +from apollo.rpmworker.temporal import TASK_QUEUE + +from common.database import Database +from common.info import Info +from common.temporal import Temporal + + +async def run(): + db = Database(True) + await db.init(["apollo.db"]) + + temporal = Temporal(True) + await temporal.connect() + + worker = Worker( + temporal.client, + task_queue=TASK_QUEUE, + workflows=[ + RhMatcherWorkflow, + RhDefunctWorkflow, + ], + activities=[ + get_supported_products_with_rh_mirrors, + match_rh_repos, + block_remaining_rh_advisories, + ] + ) + + await worker.run() + + +@click.command() +def main(): + Info("apollorpmworker", "apollo2") + asyncio.run(run()) + + +if __name__ == "__main__": + main() diff --git a/apollo/rpmworker/repomd.py b/apollo/rpmworker/repomd.py new file mode 100644 index 0000000..da8165b --- /dev/null +++ b/apollo/rpmworker/repomd.py @@ -0,0 +1,116 @@ +import gzip +import lzma +import re +from xml.etree import ElementTree as ET +from urllib.parse import urlparse +from os import path + +import aiohttp +import yaml + +from common.logger import Logger + +NVRA_RE = re.compile( + r"^(\S+)-([\w~%.+]+)-(\w+(?:\.[\w~%+]+)+?)(?:\.(\w+))?(?:\.rpm)?$" +) +DIST_RE = re.compile(r"(\.el\d(?:_\d|))") +MODULE_DIST_RE = re.compile(r"\.module.+$") + + +def clean_nvra_pkg(matching_pkg: ET.Element) -> str: + name = matching_pkg.find("{http://linux.duke.edu/metadata/common}name").text + version = matching_pkg.find( + "{http://linux.duke.edu/metadata/common}version" + ).attrib["ver"] + release = matching_pkg.find( + "{http://linux.duke.edu/metadata/common}version" + ).attrib["rel"] + arch = matching_pkg.find("{http://linux.duke.edu/metadata/common}arch").text + + clean_release = MODULE_DIST_RE.sub("", DIST_RE.sub("", release)) + return f"{name}-{version}-{clean_release}.{arch}" + + +def clean_nvra(nvra_raw: str) -> str: + nvra = NVRA_RE.search(nvra_raw) + name = nvra.group(1) + version = nvra.group(2) + release = nvra.group(3) + arch = nvra.group(4) + + clean_release = MODULE_DIST_RE.sub("", DIST_RE.sub("", release)) + return f"{name}-{version}-{clean_release}.{arch}" + + +async def download_xml( + url: str, gz: bool = False, xz: bool = False +) -> ET.Element: + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + if resp.status != 200: + raise Exception(f"Failed to get {url}: {resp.status}") + # Do an in memory gzip decompression if gz is set + if gz: + return ET.fromstring( + gzip.decompress(await resp.read()).decode("utf-8") + ) + elif xz: + return ET.fromstring( + lzma.decompress(await resp.read()).decode("utf-8") + ) + return ET.fromstring(await resp.text()) + + +async def download_yaml(url: str, gz: bool = False, xz: bool = False) -> any: + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + if resp.status != 200: + raise Exception(f"Failed to get {url}: {resp.status}") + # Do an in memory gzip decompression if gz is set + if gz: + return yaml.full_load_all( + gzip.decompress(await resp.read()).decode("utf-8") + ) + elif xz: + return yaml.full_load_all( + lzma.decompress(await resp.read()).decode("utf-8") + ) + + return yaml.full_load_all(await resp.text()) + + +async def get_data_from_repomd( + url: str, + data_type: str, + el: ET.Element, + is_yaml=False, +): + # There is a top-most repomd element in repomd + # Under there is revision and multiple data elements + # We want the data element with type="data_type" + # Under that is location with href + # That href is the location of the data + for data in el.findall("{http://linux.duke.edu/metadata/repo}data"): + if data.attrib["type"] == data_type: + location = data.find( + "{http://linux.duke.edu/metadata/repo}location" + ) + parsed_url = urlparse(url) + new_path = path.abspath( + path.join(parsed_url.path, "../..", location.attrib["href"]) + ) + data_url = parsed_url._replace(path=new_path).geturl() + + if is_yaml: + return await download_yaml( + data_url, + gz=data_url.endswith(".gz"), + xz=data_url.endswith(".xz"), + ) + return await download_xml( + data_url, + gz=data_url.endswith(".gz"), + xz=data_url.endswith(".xz"), + ) + + return None diff --git a/apollo/rpmworker/rh_matcher_activities.py b/apollo/rpmworker/rh_matcher_activities.py new file mode 100644 index 0000000..01aa5f6 --- /dev/null +++ b/apollo/rpmworker/rh_matcher_activities.py @@ -0,0 +1,562 @@ +import datetime +from dataclasses import dataclass +from xml.etree import ElementTree as ET + +from temporalio import activity +from tortoise.transactions import in_transaction + +from apollo.db import SupportedProduct, SupportedProductsRhMirror, SupportedProductsRpmRepomd, SupportedProductsRpmRhOverride, SupportedProductsRhBlock +from apollo.db import RedHatAdvisory, Advisory, AdvisoryAffectedProduct, AdvisoryCVE, AdvisoryFix, AdvisoryPackage +from apollo.rpmworker import repomd + +from common.logger import Logger + + +@dataclass +class NewPackage: + nevra: str + checksum: str + checksum_type: str + module_context: str + module_name: str + module_stream: str + module_version: str + repo_name: str + package_name: str + mirror_id: int + supported_product_id: int + product_name: str + + +@activity.defn +async def get_supported_products_with_rh_mirrors() -> list[int]: + """ + Get supported product IDs that has an RH mirror configuration + """ + rh_mirrors = await SupportedProductsRhMirror.all().prefetch_related( + "rpm_repomds", + ) + + ret = [] + for rh_mirror in rh_mirrors: + if rh_mirror.supported_product_id not in ret and rh_mirror.rpm_repomds: + ret.append(rh_mirror.supported_product_id) + + return ret + + +async def get_matching_rh_advisories( + mirror: SupportedProductsRhMirror +) -> list[RedHatAdvisory]: + # First get advisories that matches the mirrored product + # And also the overrides + # Also exclude blocked advisories and advisories without packages + advisories = await RedHatAdvisory.filter( + affected_products__variant=mirror.match_variant, + affected_products__major_version=mirror.match_major_version, + affected_products__minor_version=mirror.match_minor_version, + affected_products__arch=mirror.match_arch, + ).order_by("red_hat_issued_at").prefetch_related( + "packages", "cves", "bugzilla_tickets" + ) + + override_ids = [] + overrides = await SupportedProductsRpmRhOverride.filter( + supported_products_rh_mirror_id=mirror.id + ).prefetch_related("red_hat_advisory") + for override in overrides: + override_ids.append(override.red_hat_advisory_id) + advisories.append(override.red_hat_advisory) + + blocked = await SupportedProductsRhBlock.filter( + supported_products_rh_mirror_id=mirror.id, + ).all() + blocked_ids = [] + now = datetime.datetime.now(datetime.timezone.utc) + for b in blocked: + delta = now - b.created_at + if delta.days >= 14: + blocked_ids.append(b.red_hat_advisory_id) + + # Remove all advisories without packages and blocked advisories + final = [] + for advisory in advisories: + if advisory.packages and ( + advisory.id not in blocked_ids and advisory.id not in override_ids + ): + final.append(advisory) + + return final + + +async def clone_advisory( + product: SupportedProduct, + mirrors: list[SupportedProductsRhMirror], + advisory: RedHatAdvisory, + all_pkgs: list[ET.ElementTree], + module_pkgs: dict, + published_at: datetime.datetime, +): + logger = Logger() + logger.info("Cloning advisory %s to %s", advisory.name, product.name) + + acceptable_arches = list(set([x.match_arch for x in mirrors])) + acceptable_arches.extend(["src", "noarch"]) + for mirror in mirrors: + if mirror.match_arch == "x86_64": + acceptable_arches.append("i686") + break + + clean_advisory_nvras = {} + for advisory_pkg in advisory.packages: + nvra = repomd.NVRA_RE.search(advisory_pkg.nevra) + if nvra.group(4) not in acceptable_arches: + continue + cleaned = repomd.clean_nvra(advisory_pkg.nevra) + if cleaned not in clean_advisory_nvras: + clean_advisory_nvras[cleaned] = True + + if not clean_advisory_nvras: + logger.info( + "Blocking advisory %s, no packages match arches", + advisory.name, + ) + await SupportedProductsRhBlock.bulk_create( + [ + SupportedProductsRhBlock( + **{ + "supported_products_rh_mirror_id": mirror.id, + "red_hat_advisory_id": advisory.id, + } + ) for mirror in mirrors + ], + ignore_conflicts=True, + ) + overrides = await SupportedProductsRpmRhOverride.filter( + supported_products_rh_mirror_id__in=[x.id for x in mirrors], + red_hat_advisory_id=advisory.id, + ).all() + if overrides: + for override in overrides: + await override.delete() + return + + pkg_nvras = {} + for pkgs in all_pkgs: + for pkg in pkgs: + cleaned = repomd.clean_nvra_pkg(pkg) + if cleaned not in pkg_nvras: + pkg_nvras[cleaned] = [pkg] + else: + pkg_nvras[cleaned].append(pkg) + + async with in_transaction(): + # Create advisory + name = f"{product.code.code}{advisory.name.removeprefix('RH')}" + synopsis = advisory.synopsis.replace( + "Red Hat Enterprise Linux", product.name + ) + synopsis = synopsis.replace("RHEL", product.name) + synopsis = synopsis.replace("Red Hat", product.vendor) + synopsis = synopsis.replace(advisory.name, name) + description = advisory.description.replace( + "Red Hat Enterprise Linux", product.name + ) + description = description.replace("RHEL", product.name) + description = description.replace("Red Hat", product.vendor) + description = description.replace(advisory.name, name) + + existing_advisory = await Advisory.filter(name=name).get_or_none() + if not existing_advisory: + new_advisory = await Advisory.create( + name=name, + synopsis=synopsis, + description=description, + kind=advisory.kind, + severity=advisory.severity, + red_hat_advisory_id=advisory.id, + published_at=published_at, + topic=advisory.topic, + ) + else: + new_advisory = existing_advisory + + # Clone packages + new_pkgs = [] + for advisory_nvra, _ in clean_advisory_nvras.items(): + if advisory_nvra not in pkg_nvras: + continue + + pkgs_to_process = pkg_nvras[advisory_nvra] + for pkg in pkgs_to_process: + pkg_name = pkg.find( + "{http://linux.duke.edu/metadata/common}name" + ).text + version_tree = pkg.find( + "{http://linux.duke.edu/metadata/common}version" + ) + version = version_tree.attrib["ver"] + release = version_tree.attrib["rel"] + epoch = version_tree.attrib["epoch"] + arch = pkg.find( + "{http://linux.duke.edu/metadata/common}arch" + ).text + nevra = f"{pkg_name}-{epoch}:{version}-{release}.{arch}.rpm" + + source_rpm = pkg.find( + "{http://linux.duke.edu/metadata/common}format" + ).find("{http://linux.duke.edu/metadata/rpm}sourcerpm") + + # This means we're checking a source RPM + if advisory_nvra.endswith(".src.rpm" + ) or advisory_nvra.endswith(".src"): + source_nvra = repomd.NVRA_RE.search(advisory_nvra) + package_name = source_nvra.group(1) + else: + source_nvra = repomd.NVRA_RE.search(source_rpm.text) + package_name = source_nvra.group(1) + + checksum_tree = pkg.find( + "{http://linux.duke.edu/metadata/common}checksum" + ) + checksum = checksum_tree.text + checksum_type = checksum_tree.attrib["type"] + + module_context = None + module_name = None + module_stream = None + module_version = None + + if ".module+" in release: + for module_pkg, data in module_pkgs.items(): + if module_pkg == nevra.removesuffix(".rpm"): + module_name = data[0] + module_stream = data[1] + module_version = data[2] + module_context = data[3] + + for mirror in mirrors: + if pkg.attrib["mirror_id"] != str(mirror.id): + continue + + new_pkgs.append( + NewPackage( + nevra=nevra, + checksum=checksum, + checksum_type=checksum_type, + module_context=module_context, + module_name=module_name, + module_stream=module_stream, + module_version=module_version, + repo_name=pkg.attrib["repo_name"], + package_name=package_name, + mirror_id=mirror.id, + supported_product_id=mirror.supported_product_id, + product_name=mirror.name, + ) + ) + + if not new_pkgs: + logger.info( + "Blocking advisory %s, no packages", + advisory.name, + ) + if not existing_advisory: + await new_advisory.delete() + await SupportedProductsRhBlock.bulk_create( + [ + SupportedProductsRhBlock( + **{ + "supported_products_rh_mirror_id": mirror.id, + "red_hat_advisory_id": advisory.id, + } + ) for mirror in mirrors + ], + ignore_conflicts=True, + ) + overrides = await SupportedProductsRpmRhOverride.filter( + supported_products_rh_mirror_id__in=[x.id for x in mirrors], + red_hat_advisory_id=advisory.id, + ).all() + if overrides: + for override in overrides: + await override.delete() + return + + await AdvisoryPackage.bulk_create( + [ + AdvisoryPackage( + **{ + "advisory_id": new_advisory.id, + "nevra": pkg.nevra, + "checksum": pkg.checksum, + "checksum_type": pkg.checksum_type, + "module_context": pkg.module_context, + "module_name": pkg.module_name, + "module_stream": pkg.module_stream, + "module_version": pkg.module_version, + "repo_name": pkg.repo_name, + "package_name": pkg.package_name, + "supported_products_rh_mirror_id": pkg.mirror_id, + "supported_product_id": pkg.supported_product_id, + "product_name": pkg.product_name, + } + ) for pkg in new_pkgs + ], + ignore_conflicts=True, + ) + + # Clone CVEs + if advisory.cves: + await AdvisoryCVE.bulk_create( + [ + AdvisoryCVE( + **{ + "advisory_id": new_advisory.id, + "cve": cve.cve, + "cvss3_scoring_vector": cve.cvss3_scoring_vector, + "cvss3_base_score": cve.cvss3_base_score, + "cwe": cve.cwe, + } + ) for cve in advisory.cves + ], + ignore_conflicts=True, + ) + + # Clone fixes + if advisory.bugzilla_tickets: + await AdvisoryFix.bulk_create( + [ + AdvisoryFix( + **{ + "advisory_id": + new_advisory.id, + "ticket_id": + fix.bugzilla_bug_id, + "source": + f"https://bugzilla.redhat.com/show_bug.cgi?id={fix.bugzilla_bug_id}", + "description": + fix.description, + } + ) for fix in advisory.bugzilla_tickets + ], + ignore_conflicts=True, + ) + + # Add affected products + await AdvisoryAffectedProduct.bulk_create( + [ + AdvisoryAffectedProduct( + **{ + "advisory_id": new_advisory.id, + "variant": product.name, + "name": mirror.name, + "major_version": mirror.match_major_version, + "minor_version": mirror.match_minor_version, + "arch": mirror.match_arch, + "supported_product_id": mirror.supported_product_id, + } + ) for mirror in mirrors + ], + ignore_conflicts=True, + ) + + # Check if topic is empty, if so construct it + if not new_advisory.topic: + package_names = list(set([p.package_name for p in new_pkgs])) + affected_products = list( + set( + [ + f"{product.name} {mirror.match_major_version}" + for mirror in mirrors + ] + ) + ) + topic = f"""An update is available for {', '.join(package_names)}. +This update affects {', '.join(affected_products)}. +A Common Vulnerability Scoring System (CVSS) base score, which gives a detailed severity rating, is available for each vulnerability from the CVE list""" + new_advisory.topic = topic + + await new_advisory.save() + + # Block advisory from being attempted to be mirrored again + await SupportedProductsRhBlock.bulk_create( + [ + SupportedProductsRhBlock( + **{ + "supported_products_rh_mirror_id": mirror.id, + "red_hat_advisory_id": advisory.id, + } + ) for mirror in mirrors + ], + ignore_conflicts=True, + ) + + +async def process_repomd( + mirror: SupportedProductsRhMirror, + rpm_repomd: SupportedProductsRpmRepomd, + advisories: list[RedHatAdvisory], +): + logger = Logger() + + all_pkgs = [] + urls_to_fetch = [ + rpm_repomd.url, rpm_repomd.debug_url, rpm_repomd.source_url + ] + module_packages = {} + for url in urls_to_fetch: + logger.info("Fetching %s", url) + repomd_xml = await repomd.download_xml(url) + primary_xml = await repomd.get_data_from_repomd( + url, "primary", repomd_xml + ) + pkgs = primary_xml.findall( + "{http://linux.duke.edu/metadata/common}package" + ) + all_pkgs.extend(pkgs) + + module_yaml_data = await repomd.get_data_from_repomd( + url, + "modules", + repomd_xml, + is_yaml=True, + ) + if module_yaml_data: + logger.info("Found modules.yaml") + for module_data in module_yaml_data: + if module_data.get("document") != "modulemd": + continue + data = module_data.get("data") + if not data.get("artifacts"): + continue + for nevra in data.get("artifacts").get("rpms"): + module_packages[nevra] = ( + data.get("name"), + data.get("stream"), + data.get("version"), + data.get("context"), + ) + + ret = {} + + pkg_nvras = {} + for pkg in all_pkgs: + cleaned = repomd.clean_nvra_pkg(pkg) + if cleaned not in pkg_nvras: + pkg_nvras[cleaned] = pkg + + check_pkgs = [] + + # Now check against advisories, and see if we're matching any + # If we match, that means we can start creating the supporting + # mirror advisories + for advisory in advisories: + clean_advisory_nvras = {} + for advisory_pkg in advisory.packages: + cleaned = repomd.clean_nvra(advisory_pkg.nevra) + if cleaned not in clean_advisory_nvras: + clean_advisory_nvras[cleaned] = True + + if not clean_advisory_nvras: + continue + + did_match_any = False + + for nevra, _ in clean_advisory_nvras.items(): + if nevra in pkg_nvras: + pkg = pkg_nvras[nevra] + # Set repo name as an attribute to packages + pkg.set("repo_name", rpm_repomd.repo_name) + pkg.set("mirror_id", str(mirror.id)) + check_pkgs.append(pkg) + did_match_any = True + + if did_match_any: + ret.update( + { + advisory.name: + { + "advisory": advisory, + "packages": [check_pkgs], + "module_packages": module_packages, + } + } + ) + + return ret + + +@activity.defn +async def match_rh_repos(supported_product_id: int) -> None: + """ + Process the repomd files for the supported product + """ + logger = Logger() + + supported_product = await SupportedProduct.filter( + id=supported_product_id + ).first().prefetch_related("rh_mirrors", "rh_mirrors__rpm_repomds", "code") + + all_advisories = {} + + for mirror in supported_product.rh_mirrors: + logger.info("Processing mirror: %s", mirror.name) + advisories = await get_matching_rh_advisories(mirror) + for rpm_repomd in mirror.rpm_repomds: + if rpm_repomd.arch != mirror.match_arch: + continue + published_at = None + if rpm_repomd.production: + published_at = datetime.datetime.now() + advisory_map = await process_repomd(mirror, rpm_repomd, advisories) + if advisory_map: + for advisory_name, obj in advisory_map.items(): + if advisory_name in all_advisories: + all_advisories[advisory_name]["packages"].extend( + obj["packages"] + ) + all_advisories[advisory_name]["mirrors"].append(mirror) + + for key, val in obj["module_packages"].items(): + all_advisories[advisory_name]["module_packages"][ + key] = val + else: + new_obj = dict(obj) + new_obj["published_at"] = published_at + new_obj["mirrors"] = [mirror] + all_advisories.update({advisory_name: new_obj}) + + for advisory_name, obj in all_advisories.items(): + await clone_advisory( + supported_product, + list(set(obj["mirrors"])), + obj["advisory"], + obj["packages"], + obj["module_packages"], + obj["published_at"], + ) + + +@activity.defn +async def block_remaining_rh_advisories(supported_product_id: int) -> None: + supported_product = await SupportedProduct.filter( + id=supported_product_id + ).first().prefetch_related("rh_mirrors") + for mirror in supported_product.rh_mirrors: + mirrors = await SupportedProductsRhMirror.filter( + supported_product_id=supported_product_id + ) + for mirror in mirrors: + advisories = await get_matching_rh_advisories(mirror) + await SupportedProductsRhBlock.bulk_create( + [ + SupportedProductsRhBlock( + **{ + "supported_products_rh_mirror_id": mirror.id, + "red_hat_advsiory_id": advisory.id, + } + ) for advisory in advisories + ], + ignore_conflicts=True + ) diff --git a/apollo/rpmworker/rh_matcher_workflows.py b/apollo/rpmworker/rh_matcher_workflows.py new file mode 100644 index 0000000..5b9ae5e --- /dev/null +++ b/apollo/rpmworker/rh_matcher_workflows.py @@ -0,0 +1,39 @@ +import datetime +from dataclasses import dataclass + +from temporalio import workflow + + +@dataclass +class RhDefunctWorkflowInput: + supported_product_id: int + + +@workflow.defn +class RhMatcherWorkflow: + @workflow.run + async def run(self) -> list[int]: + supported_product_ids = await workflow.execute_activity( + "get_supported_products_with_rh_mirrors", + start_to_close_timeout=datetime.timedelta(seconds=20), + ) + + for supported_product_id in supported_product_ids: + await workflow.execute_activity( + "match_rh_repos", + supported_product_id, + start_to_close_timeout=datetime.timedelta(hours=12), + ) + + return supported_product_ids + + +@workflow.defn +class RhDefunctWorkflow: + @workflow.run + async def run(self, wf_in: RhDefunctWorkflowInput) -> None: + await workflow.execute_activity( + "block_remaining_rh_advisories", + wf_in.supported_product_id, + start_to_close_timeout=datetime.timedelta(hours=12), + ) diff --git a/apollo/rpmworker/rocky_linux.sql b/apollo/rpmworker/rocky_linux.sql new file mode 100644 index 0000000..b71b5db --- /dev/null +++ b/apollo/rpmworker/rocky_linux.sql @@ -0,0 +1,49 @@ +insert into supported_products_rpm_repomds +(supported_products_rh_mirror_id, production, arch, url, debug_url, source_url, repo_name) +values +(2, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/8/BaseOS/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/BaseOS/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(2, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/8/AppStream/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/AppStream/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(2, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/8/HighAvailability/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/HighAvailability/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(2, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/8/NFV/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/NFV/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(2, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/8/PowerTools/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/PowerTools/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/PowerTools/source/tree/repodata/repomd.xml', 'PowerTools'), +(2, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/8/RT/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/RT/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/RT/source/tree/repodata/repomd.xml', 'RT'), +(2, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/8/ResilientStorage/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/ResilientStorage/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(3, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/8/BaseOS/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/BaseOS/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(3, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/8/AppStream/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/AppStream/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(3, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/8/HighAvailability/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/HighAvailability/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(3, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/8/NFV/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/NFV/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(3, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/8/PowerTools/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/PowerTools/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/PowerTools/source/tree/repodata/repomd.xml', 'PowerTools'), +(3, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/8/ResilientStorage/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/ResilientStorage/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/8/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(4, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(4, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(4, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(4, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/9/NFV/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/NFV/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(4, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/9/CRB/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/CRB/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/CRB/source/tree/repodata/repomd.xml', 'CRB'), +(4, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/9/RT/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/RT/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/RT/source/tree/repodata/repomd.xml', 'RT'), +(4, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(4, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/9/SAP/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAP/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAP/source/tree/repodata/repomd.xml', 'SAP'), +(4, true, 'x86_64', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/source/tree/repodata/repomd.xml', 'SAPHANA'), +(5, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(5, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(5, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(5, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/9/NFV/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/NFV/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(5, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/9/CRB/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/CRB/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/CRB/source/tree/repodata/repomd.xml', 'CRB'), +(5, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(5, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/9/SAP/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAP/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAP/source/tree/repodata/repomd.xml', 'SAP'), +(5, true, 'aarch64', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/source/tree/repodata/repomd.xml', 'SAPHANA'), +(6, true, 's390x', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(6, true, 's390x', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(6, true, 's390x', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(6, true, 's390x', 'http://dl.rockylinux.org/pub/rocky/9/NFV/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/NFV/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(6, true, 's390x', 'http://dl.rockylinux.org/pub/rocky/9/CRB/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/CRB/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/CRB/source/tree/repodata/repomd.xml', 'CRB'), +(6, true, 's390x', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(6, true, 's390x', 'http://dl.rockylinux.org/pub/rocky/9/SAP/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAP/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAP/source/tree/repodata/repomd.xml', 'SAP'), +(6, true, 's390x', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/source/tree/repodata/repomd.xml', 'SAPHANA'), +(7, true, 'ppc64le', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(7, true, 'ppc64le', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(7, true, 'ppc64le', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(7, true, 'ppc64le', 'http://dl.rockylinux.org/pub/rocky/9/NFV/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/NFV/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(7, true, 'ppc64le', 'http://dl.rockylinux.org/pub/rocky/9/CRB/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/CRB/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/CRB/source/tree/repodata/repomd.xml', 'CRB'), +(7, true, 'ppc64le', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(7, true, 'ppc64le', 'http://dl.rockylinux.org/pub/rocky/9/SAP/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAP/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAP/source/tree/repodata/repomd.xml', 'SAP'), +(7, true, 'ppc64le', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/pub/rocky/9/SAPHANA/source/tree/repodata/repomd.xml', 'SAPHANA'); diff --git a/apollo/rpmworker/rocky_linux_older.sql b/apollo/rpmworker/rocky_linux_older.sql new file mode 100644 index 0000000..506dff3 --- /dev/null +++ b/apollo/rpmworker/rocky_linux_older.sql @@ -0,0 +1,75 @@ +insert into supported_products_rpm_repomds +(supported_products_rh_mirror_id, production, arch, url, debug_url, source_url, repo_name) +values +(8, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.4/BaseOS/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/BaseOS/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(8, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.4/AppStream/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/AppStream/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(8, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.4/HighAvailability/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/HighAvailability/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(8, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.4/nfv/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/nfv/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/nfv/source/tree/repodata/repomd.xml', 'NFV'), +(8, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.4/PowerTools/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/PowerTools/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/PowerTools/source/tree/repodata/repomd.xml', 'PowerTools'), +(8, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.4/RT/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/RT/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/RT/source/tree/repodata/repomd.xml', 'RT'), +(8, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.4/ResilientStorage/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/ResilientStorage/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(9, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.4/BaseOS/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/BaseOS/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(9, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.4/AppStream/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/AppStream/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(9, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.4/HighAvailability/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/HighAvailability/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(9, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.4/nfv/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/nfv/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/nfv/source/tree/repodata/repomd.xml', 'NFV'), +(9, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.4/PowerTools/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/PowerTools/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/PowerTools/source/tree/repodata/repomd.xml', 'PowerTools'), +(9, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.4/ResilientStorage/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/ResilientStorage/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.4/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(10, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.5/BaseOS/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/BaseOS/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(10, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.5/AppStream/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/AppStream/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(10, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.5/HighAvailability/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/HighAvailability/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(10, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.5/nfv/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/nfv/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/nfv/source/tree/repodata/repomd.xml', 'NFV'), +(10, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.5/PowerTools/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/PowerTools/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/PowerTools/source/tree/repodata/repomd.xml', 'PowerTools'), +(10, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.5/RT/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/RT/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/RT/source/tree/repodata/repomd.xml', 'RT'), +(10, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.5/ResilientStorage/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/ResilientStorage/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(11, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.5/BaseOS/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/BaseOS/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(11, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.5/AppStream/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/AppStream/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(11, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.5/HighAvailability/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/HighAvailability/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(11, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.5/nfv/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/nfv/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/nfv/source/tree/repodata/repomd.xml', 'NFV'), +(11, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.5/PowerTools/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/PowerTools/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/PowerTools/source/tree/repodata/repomd.xml', 'PowerTools'), +(11, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.5/ResilientStorage/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/ResilientStorage/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.5/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(12, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.6/BaseOS/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/BaseOS/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(12, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.6/AppStream/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/AppStream/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(12, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.6/HighAvailability/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/HighAvailability/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(12, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.6/NFV/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/NFV/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(12, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.6/PowerTools/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/PowerTools/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/PowerTools/source/tree/repodata/repomd.xml', 'PowerTools'), +(12, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.6/RT/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/RT/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/RT/source/tree/repodata/repomd.xml', 'RT'), +(12, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/8.6/ResilientStorage/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/ResilientStorage/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(13, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.6/BaseOS/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/BaseOS/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(13, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.6/AppStream/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/AppStream/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(13, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.6/HighAvailability/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/HighAvailability/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(13, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.6/NFV/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/NFV/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(13, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.6/PowerTools/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/PowerTools/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/PowerTools/source/tree/repodata/repomd.xml', 'PowerTools'), +(13, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/8.6/ResilientStorage/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/ResilientStorage/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/8.6/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(14, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(14, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(14, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(14, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(14, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/source/tree/repodata/repomd.xml', 'CRB'), +(14, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/9.0/RT/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/RT/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/RT/source/tree/repodata/repomd.xml', 'RT'), +(14, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(14, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/source/tree/repodata/repomd.xml', 'SAP'), +(14, true, 'x86_64', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/x86_64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/x86_64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/source/tree/repodata/repomd.xml', 'SAPHANA'), +(15, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(15, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(15, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(15, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(15, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/source/tree/repodata/repomd.xml', 'CRB'), +(15, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(15, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/source/tree/repodata/repomd.xml', 'SAP'), +(15, true, 'aarch64', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/aarch64/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/aarch64/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/source/tree/repodata/repomd.xml', 'SAPHANA'), +(16, true, 's390x', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(16, true, 's390x', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(16, true, 's390x', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(16, true, 's390x', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(16, true, 's390x', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/source/tree/repodata/repomd.xml', 'CRB'), +(16, true, 's390x', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(16, true, 's390x', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/source/tree/repodata/repomd.xml', 'SAP'), +(16, true, 's390x', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/s390x/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/s390x/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/source/tree/repodata/repomd.xml', 'SAPHANA'), +(17, true, 'ppc64le', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/BaseOS/source/tree/repodata/repomd.xml', 'BaseOS'), +(17, true, 'ppc64le', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/AppStream/source/tree/repodata/repomd.xml', 'AppStream'), +(17, true, 'ppc64le', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/HighAvailability/source/tree/repodata/repomd.xml', 'HighAvailability'), +(17, true, 'ppc64le', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/NFV/source/tree/repodata/repomd.xml', 'NFV'), +(17, true, 'ppc64le', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/CRB/source/tree/repodata/repomd.xml', 'CRB'), +(17, true, 'ppc64le', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/ResilientStorage/source/tree/repodata/repomd.xml', 'ResilientStorage'), +(17, true, 'ppc64le', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAP/source/tree/repodata/repomd.xml', 'SAP'), +(17, true, 'ppc64le', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/ppc64le/os/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/ppc64le/debug/tree/repodata/repomd.xml', 'http://dl.rockylinux.org/vault/rocky/9.0/SAPHANA/source/tree/repodata/repomd.xml', 'SAPHANA'); diff --git a/apollo/rpmworker/temporal.py b/apollo/rpmworker/temporal.py new file mode 100644 index 0000000..e3ff9af --- /dev/null +++ b/apollo/rpmworker/temporal.py @@ -0,0 +1 @@ +TASK_QUEUE = "v2-rpmworker" diff --git a/apollo/schema.sql b/apollo/schema.sql new file mode 100644 index 0000000..c1c1141 --- /dev/null +++ b/apollo/schema.sql @@ -0,0 +1,591 @@ +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: codes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.codes ( + id text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone, + archived_at timestamp without time zone, + description text NOT NULL +); + + +-- +-- Name: red_hat_advisories; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.red_hat_advisories ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone, + red_hat_issued_at timestamp with time zone NOT NULL, + name text NOT NULL, + synopsis text NOT NULL, + description text NOT NULL, + kind text NOT NULL, + severity text NOT NULL +); + + +-- +-- Name: red_hat_advisories_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.red_hat_advisories_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: red_hat_advisories_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.red_hat_advisories_id_seq OWNED BY public.red_hat_advisories.id; + + +-- +-- Name: red_hat_advisory_affected_products; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.red_hat_advisory_affected_products ( + id bigint NOT NULL, + red_hat_advisory_id bigint, + variant text NOT NULL, + name text NOT NULL, + major_version numeric NOT NULL, + minor_version numeric, + arch text NOT NULL +); + + +-- +-- Name: red_hat_advisory_affected_products_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.red_hat_advisory_affected_products_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: red_hat_advisory_affected_products_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.red_hat_advisory_affected_products_id_seq OWNED BY public.red_hat_advisory_affected_products.id; + + +-- +-- Name: red_hat_advisory_bugzilla_bugs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.red_hat_advisory_bugzilla_bugs ( + id bigint NOT NULL, + red_hat_advisory_id bigint, + bugzilla_bug_id text NOT NULL +); + + +-- +-- Name: red_hat_advisory_bugzilla_bugs_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.red_hat_advisory_bugzilla_bugs_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: red_hat_advisory_bugzilla_bugs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.red_hat_advisory_bugzilla_bugs_id_seq OWNED BY public.red_hat_advisory_bugzilla_bugs.id; + + +-- +-- Name: red_hat_advisory_cves; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.red_hat_advisory_cves ( + id bigint NOT NULL, + red_hat_advisory_id bigint, + cve text NOT NULL +); + + +-- +-- Name: red_hat_advisory_cves_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.red_hat_advisory_cves_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: red_hat_advisory_cves_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.red_hat_advisory_cves_id_seq OWNED BY public.red_hat_advisory_cves.id; + + +-- +-- Name: red_hat_advisory_packages; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.red_hat_advisory_packages ( + id bigint NOT NULL, + red_hat_advisory_id bigint, + nevra text NOT NULL +); + + +-- +-- Name: red_hat_advisory_packages_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.red_hat_advisory_packages_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: red_hat_advisory_packages_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.red_hat_advisory_packages_id_seq OWNED BY public.red_hat_advisory_packages.id; + + +-- +-- Name: red_hat_index_state; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.red_hat_index_state ( + id bigint NOT NULL, + last_indexed_at timestamp with time zone +); + + +-- +-- Name: red_hat_index_state_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.red_hat_index_state_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: red_hat_index_state_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.red_hat_index_state_id_seq OWNED BY public.red_hat_index_state.id; + + +-- +-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.schema_migrations ( + version character varying(255) NOT NULL +); + + +-- +-- Name: supported_products; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.supported_products ( + id bigint NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone, + eol_at timestamp with time zone, + name text NOT NULL, + rhel_major_version numeric, + rhel_minor_version numeric +); + + +-- +-- Name: supported_products_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.supported_products_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: supported_products_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.supported_products_id_seq OWNED BY public.supported_products.id; + + +-- +-- Name: red_hat_advisories id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisories ALTER COLUMN id SET DEFAULT nextval('public.red_hat_advisories_id_seq'::regclass); + + +-- +-- Name: red_hat_advisory_affected_products id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_affected_products ALTER COLUMN id SET DEFAULT nextval('public.red_hat_advisory_affected_products_id_seq'::regclass); + + +-- +-- Name: red_hat_advisory_bugzilla_bugs id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_bugzilla_bugs ALTER COLUMN id SET DEFAULT nextval('public.red_hat_advisory_bugzilla_bugs_id_seq'::regclass); + + +-- +-- Name: red_hat_advisory_cves id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_cves ALTER COLUMN id SET DEFAULT nextval('public.red_hat_advisory_cves_id_seq'::regclass); + + +-- +-- Name: red_hat_advisory_packages id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_packages ALTER COLUMN id SET DEFAULT nextval('public.red_hat_advisory_packages_id_seq'::regclass); + + +-- +-- Name: red_hat_index_state id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_index_state ALTER COLUMN id SET DEFAULT nextval('public.red_hat_index_state_id_seq'::regclass); + + +-- +-- Name: supported_products id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.supported_products ALTER COLUMN id SET DEFAULT nextval('public.supported_products_id_seq'::regclass); + + +-- +-- Name: codes codes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.codes + ADD CONSTRAINT codes_pkey PRIMARY KEY (id); + + +-- +-- Name: red_hat_advisories red_hat_advisories_name_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisories + ADD CONSTRAINT red_hat_advisories_name_key UNIQUE (name); + + +-- +-- Name: red_hat_advisories red_hat_advisories_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisories + ADD CONSTRAINT red_hat_advisories_pkey PRIMARY KEY (id); + + +-- +-- Name: red_hat_advisory_affected_products red_hat_advisory_affected_pro_red_hat_advisory_id_variant_n_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_affected_products + ADD CONSTRAINT red_hat_advisory_affected_pro_red_hat_advisory_id_variant_n_key UNIQUE (red_hat_advisory_id, variant, name, major_version, minor_version, arch); + + +-- +-- Name: red_hat_advisory_affected_products red_hat_advisory_affected_products_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_affected_products + ADD CONSTRAINT red_hat_advisory_affected_products_pkey PRIMARY KEY (id); + + +-- +-- Name: red_hat_advisory_bugzilla_bugs red_hat_advisory_bugzilla_bug_red_hat_advisory_id_bugzilla__key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_bugzilla_bugs + ADD CONSTRAINT red_hat_advisory_bugzilla_bug_red_hat_advisory_id_bugzilla__key UNIQUE (red_hat_advisory_id, bugzilla_bug_id); + + +-- +-- Name: red_hat_advisory_bugzilla_bugs red_hat_advisory_bugzilla_bugs_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_bugzilla_bugs + ADD CONSTRAINT red_hat_advisory_bugzilla_bugs_pkey PRIMARY KEY (id); + + +-- +-- Name: red_hat_advisory_cves red_hat_advisory_cves_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_cves + ADD CONSTRAINT red_hat_advisory_cves_pkey PRIMARY KEY (id); + + +-- +-- Name: red_hat_advisory_cves red_hat_advisory_cves_red_hat_advisory_id_cve_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_cves + ADD CONSTRAINT red_hat_advisory_cves_red_hat_advisory_id_cve_key UNIQUE (red_hat_advisory_id, cve); + + +-- +-- Name: red_hat_advisory_packages red_hat_advisory_packages_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_packages + ADD CONSTRAINT red_hat_advisory_packages_pkey PRIMARY KEY (id); + + +-- +-- Name: red_hat_advisory_packages red_hat_advisory_packages_red_hat_advisory_id_nevra_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_packages + ADD CONSTRAINT red_hat_advisory_packages_red_hat_advisory_id_nevra_key UNIQUE (red_hat_advisory_id, nevra); + + +-- +-- Name: red_hat_index_state red_hat_index_state_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_index_state + ADD CONSTRAINT red_hat_index_state_pkey PRIMARY KEY (id); + + +-- +-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.schema_migrations + ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version); + + +-- +-- Name: supported_products supported_products_name_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.supported_products + ADD CONSTRAINT supported_products_name_key UNIQUE (name); + + +-- +-- Name: supported_products supported_products_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.supported_products + ADD CONSTRAINT supported_products_pkey PRIMARY KEY (id); + + +-- +-- Name: red_hat_advisories_kindx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisories_kindx ON public.red_hat_advisories USING btree (kind); + + +-- +-- Name: red_hat_advisories_namex; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisories_namex ON public.red_hat_advisories USING btree (name); + + +-- +-- Name: red_hat_advisories_red_hat_issued_atx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisories_red_hat_issued_atx ON public.red_hat_advisories USING btree (red_hat_issued_at); + + +-- +-- Name: red_hat_advisories_severityx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisories_severityx ON public.red_hat_advisories USING btree (severity); + + +-- +-- Name: red_hat_advisories_synopsisx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisories_synopsisx ON public.red_hat_advisories USING btree (synopsis); + + +-- +-- Name: red_hat_advisory_affected_products_archx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisory_affected_products_archx ON public.red_hat_advisory_affected_products USING btree (arch); + + +-- +-- Name: red_hat_advisory_affected_products_major_versionx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisory_affected_products_major_versionx ON public.red_hat_advisory_affected_products USING btree (major_version); + + +-- +-- Name: red_hat_advisory_affected_products_minor_versionx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisory_affected_products_minor_versionx ON public.red_hat_advisory_affected_products USING btree (minor_version); + + +-- +-- Name: red_hat_advisory_affected_products_namex; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisory_affected_products_namex ON public.red_hat_advisory_affected_products USING btree (name); + + +-- +-- Name: red_hat_advisory_affected_products_variantx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisory_affected_products_variantx ON public.red_hat_advisory_affected_products USING btree (variant); + + +-- +-- Name: red_hat_advisory_bugzilla_bugs_bugzilla_bug_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisory_bugzilla_bugs_bugzilla_bug_idx ON public.red_hat_advisory_bugzilla_bugs USING btree (bugzilla_bug_id); + + +-- +-- Name: red_hat_advisory_cvex; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisory_cvex ON public.red_hat_advisory_cves USING btree (cve); + + +-- +-- Name: red_hat_advisory_packages_nevrax; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX red_hat_advisory_packages_nevrax ON public.red_hat_advisory_packages USING btree (nevra); + + +-- +-- Name: supported_products_eol_atx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX supported_products_eol_atx ON public.supported_products USING btree (eol_at); + + +-- +-- Name: supported_products_namex; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX supported_products_namex ON public.supported_products USING btree (name); + + +-- +-- Name: supported_products_rhel_major_versionx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX supported_products_rhel_major_versionx ON public.supported_products USING btree (rhel_major_version); + + +-- +-- Name: supported_products_rhel_minor_versionx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX supported_products_rhel_minor_versionx ON public.supported_products USING btree (rhel_minor_version); + + +-- +-- Name: red_hat_advisory_affected_products red_hat_advisory_affected_products_red_hat_advisory_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_affected_products + ADD CONSTRAINT red_hat_advisory_affected_products_red_hat_advisory_id_fkey FOREIGN KEY (red_hat_advisory_id) REFERENCES public.red_hat_advisories(id) ON DELETE CASCADE; + + +-- +-- Name: red_hat_advisory_bugzilla_bugs red_hat_advisory_bugzilla_bugs_red_hat_advisory_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_bugzilla_bugs + ADD CONSTRAINT red_hat_advisory_bugzilla_bugs_red_hat_advisory_id_fkey FOREIGN KEY (red_hat_advisory_id) REFERENCES public.red_hat_advisories(id) ON DELETE CASCADE; + + +-- +-- Name: red_hat_advisory_cves red_hat_advisory_cves_red_hat_advisory_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_cves + ADD CONSTRAINT red_hat_advisory_cves_red_hat_advisory_id_fkey FOREIGN KEY (red_hat_advisory_id) REFERENCES public.red_hat_advisories(id) ON DELETE CASCADE; + + +-- +-- Name: red_hat_advisory_packages red_hat_advisory_packages_red_hat_advisory_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.red_hat_advisory_packages + ADD CONSTRAINT red_hat_advisory_packages_red_hat_advisory_id_fkey FOREIGN KEY (red_hat_advisory_id) REFERENCES public.red_hat_advisories(id) ON DELETE CASCADE; + + +-- +-- PostgreSQL database dump complete +-- + + +-- +-- Dbmate schema migrations +-- + +INSERT INTO public.schema_migrations (version) VALUES + ('20230128201227'); diff --git a/apollo/server/BUILD.bazel b/apollo/server/BUILD.bazel new file mode 100644 index 0000000..dfa8b6d --- /dev/null +++ b/apollo/server/BUILD.bazel @@ -0,0 +1,48 @@ +load("@aspect_rules_py//py:defs.bzl", "py_library") +load("//build/macros:fastapi.bzl", "fastapi_binary") + +py_library( + name = "server_lib", + srcs = [ + "roles.py", + "routes/admin_index.py", + "routes/advisories.py", + "routes/api_advisories.py", + "routes/api_compat.py", + "routes/api_red_hat.py", + "routes/login.py", + "routes/logout.py", + "routes/red_hat_advisories.py", + "routes/statistics.py", + "server.py", + "settings.py", + "utils.py", + ], + data = [ + ":assets", + ":templates", + "//apollo/server/static", + ], + imports = ["../.."], + visibility = ["//:__subpackages__"], + deps = [ + "//apollo/db:db_lib", + "//apollo/db/serialize:serialize_lib", + "//common:common_lib", + "@pypi_fastapi//:pkg", + "@pypi_fastapi_pagination//:pkg", + "@pypi_itsdangerous//:pkg", + "@pypi_jinja2//:pkg", + "@pypi_passlib//:pkg", + "@pypi_python_multipart//:pkg", + "@pypi_starlette//:pkg", + "@pypi_tortoise_orm//:pkg", + ], +) + +fastapi_binary( + name = "server", + imports = ["../.."], + path = "apollo.server.server", + port = "9999", +) diff --git a/apollo/server/assets/pd-logo-np.svg b/apollo/server/assets/pd-logo-np.svg new file mode 100644 index 0000000..caa1daa --- /dev/null +++ b/apollo/server/assets/pd-logo-np.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apollo/server/roles.py b/apollo/server/roles.py new file mode 100644 index 0000000..89bad11 --- /dev/null +++ b/apollo/server/roles.py @@ -0,0 +1,2 @@ +ADMIN = "admin" +ELEVATED = "elevated" diff --git a/apollo/server/routes/admin_index.py b/apollo/server/routes/admin_index.py new file mode 100644 index 0000000..fbed5af --- /dev/null +++ b/apollo/server/routes/admin_index.py @@ -0,0 +1,15 @@ +from fastapi import APIRouter, Request +from fastapi.responses import HTMLResponse + +from apollo.server.utils import templates + +router = APIRouter(tags=["non-api"]) + + +@router.get("/", response_class=HTMLResponse) +async def admin_general(request: Request): + return templates.TemplateResponse( + "admin_index.jinja", { + "request": request, + } + ) diff --git a/apollo/server/routes/advisories.py b/apollo/server/routes/advisories.py new file mode 100644 index 0000000..293f380 --- /dev/null +++ b/apollo/server/routes/advisories.py @@ -0,0 +1,127 @@ +from math import ceil + +from tortoise import connections + +from fastapi import APIRouter, Request, Depends +from fastapi.responses import HTMLResponse +from fastapi_pagination import Params +from fastapi_pagination.ext.tortoise import paginate, create_page + +from apollo.db import Advisory +from apollo.server.utils import templates + +router = APIRouter(tags=["non-api"]) + + +@router.get( + "/", + response_class=HTMLResponse, +) +async def list_advisories( + request: Request, + params: Params = Depends(), + search: str = None, +): + params.size = 50 + if search: + a = """ + with vars (search, size, page_offset) as ( + values ($1 :: text, $2 :: bigint, $3 :: bigint) + ) + select + a.id, + a.created_at, + a.updated_at, + a.published_at, + a.name, + a.synopsis, + a.description, + a.kind, + a.severity, + a.red_hat_advisory_id, + count(a.*) over () as total + from + advisories a + left outer join advisory_affected_products ap on ap.advisory_id = a.id + left outer join advisory_cves c on c.advisory_id = a.id + left outer join advisory_fixes f on f.advisory_id = a.id + where + (select search from vars) is null or + ap.name ilike '%' || (select search from vars) || '%' or + a.synopsis ilike '%' || (select search from vars) || '%' or + a.description ilike '%' || (select search from vars) || '%' or + exists (select cve from advisory_cves where advisory_id = a.id and cve ilike '%' || (select search from vars) || '%') or + exists (select ticket_id from advisory_fixes where advisory_id = a.id and ticket_id ilike '%' || (select search from vars) || '%') or + a.name ilike '%' || (select search from vars) || '%' + group by a.id + order by a.published_at desc + limit (select size from vars) offset (select page_offset from vars) + """ + + connection = connections.get("default") + results = await connections.execute_query( + a, [search, params.size, params.size * (params.page - 1)] + ) + count = 0 + if results: + if results[1]: + count = results[1][0]["total"] + + advisories = create_page( + results[1], + count, + params, + ) + else: + advisories = await paginate( + Advisory.all().order_by("-published_at"), + params=params, + ) + return templates.TemplateResponse( + "advisories.jinja", { + "request": request, + "params": params, + "search": search if search else "", + "advisories": advisories, + "advisories_pages": ceil(advisories.total / advisories.size) + } + ) + + +@router.get( + "/{advisory_name}", + response_class=HTMLResponse, +) +async def get_advisory(request: Request, advisory_name: str): + advisory = await Advisory.get_or_none(name=advisory_name, + ).prefetch_related( + "red_hat_advisory", + "packages", + "cves", + "fixes", + "affected_products", + ) + if advisory is None: + return templates.TemplateResponse( + "error.jinja", { + "request": request, + "message": "Requested advisory not found", + } + ) + + package_map = {} + for package in advisory.packages: + name = f"{package.product_name} - {package.repo_name}" + if name not in package_map: + package_map[name] = [] + + package_map[name].append(package.nevra) + + return templates.TemplateResponse( + "advisory.jinja", { + "request": request, + "title": f"Advisory {advisory.id}", + "advisory": advisory, + "package_map": package_map, + } + ) \ No newline at end of file diff --git a/apollo/server/routes/api_advisories.py b/apollo/server/routes/api_advisories.py new file mode 100644 index 0000000..c9bd34b --- /dev/null +++ b/apollo/server/routes/api_advisories.py @@ -0,0 +1,56 @@ +from typing import TypeVar, Generic + +from fastapi import APIRouter, Request +from fastapi.exceptions import HTTPException +from fastapi_pagination.links import Page +from fastapi_pagination.ext.tortoise import paginate + +from apollo.db import Advisory +from apollo.db.serialize import Advisory_Pydantic + +router = APIRouter(tags=["advisories"]) + +T = TypeVar("T") + + +class Pagination(Page[T], Generic[T]): + class Config: + allow_population_by_field_name = True + fields = {"items": {"alias": "advisories"}} + + +@router.get( + "/", + response_model=Pagination[Advisory_Pydantic], +) +async def list_advisories(): + advisories = await paginate( + Advisory.all().prefetch_related( + "red_hat_advisory", + "packages", + "cves", + "fixes", + "affected_products", + ).order_by("-published_at"), + ) + + return advisories + + +@router.get( + "/{advisory_name}", + response_model=Advisory_Pydantic, +) +async def get_advisory(advisory_name: str): + advisory = await Advisory.filter(name=advisory_name).prefetch_related( + "packages", + "cves", + "fixes", + "affected_products", + "red_hat_advisory", + ).first() + + if advisory is None: + raise HTTPException(404) + + return await Advisory_Pydantic.from_tortoise_orm(advisory) diff --git a/apollo/server/routes/api_compat.py b/apollo/server/routes/api_compat.py new file mode 100644 index 0000000..a7b2dd6 --- /dev/null +++ b/apollo/server/routes/api_compat.py @@ -0,0 +1,241 @@ +""" +This module implements the compatibility API for Apollo V2 advisories +""" + +import datetime +from typing import TypeVar, Generic, Optional + +from tortoise import connections + +from fastapi import APIRouter, Depends, Query +from fastapi.exceptions import HTTPException +from fastapi_pagination.links import Page +from fastapi_pagination import Params +from fastapi_pagination.ext.tortoise import create_page + +from apollo.db import Advisory, RedHatIndexState +from apollo.db.serialize import Advisory_Pydantic_V2, Advisory_Pydantic_V2_CVE, Advisory_Pydantic_V2_Fix + +from common.fastapi import RenderErrorTemplateException + +router = APIRouter(tags=["v2_compat"]) + +T = TypeVar("T") + + +class Pagination(Page[T], Generic[T]): + lastUpdated: Optional[str] # noqa # pylint: disable=invalid-name + + class Config: + allow_population_by_field_name = True + fields = {"items": {"alias": "advisories"}} + + +def v3_advisory_to_v2( + advisory: Advisory, + include_rpms=True, +) -> Advisory_Pydantic_V2: + kind = "TYPE_SECURITY" + if advisory.kind == "Bug Fix": + kind = "TYPE_BUGFIX" + elif advisory.kind == "Enhancement": + kind = "TYPE_ENHANCEMENT" + + affected_products = list( + set( + [ + f"{ap.variant} {ap.major_version}" + for ap in advisory.affected_products + ] + ) + ) + + cves = [] + for cve in advisory.cves: + cves.append( + Advisory_Pydantic_V2_CVE( + name=cve.cve, + cvss3ScoringVector=cve.cvss3_scoring_vector, + cvss3BaseScore=cve.cvss3_base_score, + cwe=cve.cwe, + sourceBy="Red Hat", + sourceLink= + f"https://access.redhat.com/hydra/rest/securitydata/cve/{cve.cve}.json", + ) + ) + + fixes = [] + for fix in advisory.fixes: + fixes.append( + Advisory_Pydantic_V2_Fix( + ticket=fix.ticket_id, + sourceBy="Red Hat", + sourceLink=fix.source, + description=fix.description, + ) + ) + + rpms = {} + if include_rpms: + for pkg in advisory.packages: + name = f"{pkg.supported_product.variant} {pkg.supported_products_rh_mirror.match_major_version}" + if name not in rpms: + rpms[name] = [] + rpms[name].append(pkg.nevra) + + return Advisory_Pydantic_V2( + id=advisory.id, + publishedAt=advisory.published_at, + name=advisory.name, + synopsis=advisory.synopsis, + description=advisory.description, + type=kind, + severity=f"SEVERITY_{advisory.severity.upper()}", + shortCode=advisory.name[0:2], + topic=advisory.topic if advisory.topic else "", + solution=None, + rpms=rpms, + affectedProducts=affected_products, + references=[], + rebootSuggested=False, + buildReferences=[], + fixes=fixes, + cves=cves, + ) + + +@router.get( + "/", + response_model=Pagination[Advisory_Pydantic_V2], +) +async def list_advisories_compat_v2( + params: Params = Depends(), + product: str = Query(default=None, alias="filters.product"), + before_raw: str = Query(default=None, alias="filters.before"), + after_raw: str = Query(default=None, alias="filters.after"), + cve: str = Query(default=None, alias="filters.cve"), + synopsis: str = Query(default=None, alias="filters.synopsis"), + keyword: str = Query(default=None, alias="filters.keyword"), + severity: str = Query(default=None, alias="filters.severity"), + kind: str = Query(default=None, alias="filters.type"), +): + before = None + after = None + + try: + if before_raw: + before = datetime.datetime.fromisoformat( + before_raw.removesuffix("Z") + ) + except: + raise RenderErrorTemplateException("Invalid before date", 400) + + try: + if after_raw: + after = datetime.datetime.fromisoformat(after_raw.removesuffix("Z")) + except: + raise RenderErrorTemplateException("Invalid after date", 400) + + state = await RedHatIndexState.first() + + a = """ + with vars (search, size, page_offset, product, before, after, cve, synopsis, severity, kind) as ( + values ($1 :: text, $2 :: bigint, $3 :: bigint, $4 :: text, $5 :: timestamp, $6 :: timestamp, $7 :: text, $8 :: text, $9 :: text, $10 :: text) + ) + select + a.id, + a.created_at, + a.updated_at, + a.published_at, + a.name, + a.synopsis, + a.description, + a.kind, + a.severity, + a.topic, + a.red_hat_advisory_id, + count(a.*) over () as total + from + advisories a + left outer join advisory_affected_products ap on ap.advisory_id = a.id + left outer join advisory_cves c on c.advisory_id = a.id + left outer join advisory_fixes f on f.advisory_id = a.id + where + ((select product from vars) is null or ap.name ilike '%' || (select product from vars) || '%') + and ((select before from vars) is null or a.published_at < (select before from vars)) + and ((select after from vars) is null or a.published_at > (select after from vars)) + and (a.published_at is not null) + and ((select cve from vars) is null or exists (select cve from advisory_cves where advisory_id = a.id and cve ilike '%' || (select cve from vars) || '%')) + and ((select synopsis from vars) is null or a.synopsis ilike '%' || (select synopsis from vars) || '%') + and ((select severity from vars) is null or a.severity = (select severity from vars)) + and ((select kind from vars) is null or a.kind = (select kind from vars)) + and ((select search from vars) is null or + ap.name ilike '%' || (select search from vars) || '%' or + a.synopsis ilike '%' || (select search from vars) || '%' or + a.description ilike '%' || (select search from vars) || '%' or + exists (select cve from advisory_cves where advisory_id = a.id and cve ilike '%' || (select search from vars) || '%') or + exists (select ticket_id from advisory_fixes where advisory_id = a.id and ticket_id ilike '%' || (select search from vars) || '%') or + a.name ilike '%' || (select search from vars) || '%') + group by a.id + order by a.published_at desc + limit (select size from vars) offset (select page_offset from vars) + """ + + connection = connections.get("default") + results = await connection.execute_query( + a, [ + keyword, params.size, params.size * (params.page - 1), product, + before, after, cve, synopsis, severity, kind + ] + ) + + count = 0 + if results: + if results[1]: + count = results[1][0]["total"] + + advisories = [] + for adv in results[1]: + advisory = Advisory(**adv) + await advisory.fetch_related( + "packages", + "cves", + "fixes", + "affected_products", + "packages", + "packages__supported_product", + "packages__supported_products_rh_mirror", + ) + advisories.append(advisory) + + v2_advisories: list[Advisory_Pydantic_V2] = [] + for advisory in advisories: + v2_advisories.append(v3_advisory_to_v2(advisory)) + + page = create_page(v2_advisories, count, params) + page.lastUpdated = state.last_indexed_at.isoformat("T").replace( + "+00:00", "" + ) + "Z" + + return page + + +@router.get( + "/{advisory_name}", + response_model=Advisory_Pydantic_V2, +) +async def get_advisory_compat_v2(advisory_name: str): + advisory = await Advisory.filter(name=advisory_name).prefetch_related( + "packages", + "cves", + "fixes", + "affected_products", + "packages", + "packages__supported_product", + "packages__supported_products_rh_mirror", + ).get_or_none() + + if not advisory: + raise HTTPException(404) + + return Advisory_Pydantic_V2.from_orm(v3_advisory_to_v2(advisory)) diff --git a/apollo/server/routes/api_red_hat.py b/apollo/server/routes/api_red_hat.py new file mode 100644 index 0000000..ab75cf7 --- /dev/null +++ b/apollo/server/routes/api_red_hat.py @@ -0,0 +1,59 @@ +from typing import TypeVar, Generic + +from fastapi import APIRouter, Request +from fastapi.exceptions import HTTPException +from fastapi_pagination.links import Page +from fastapi_pagination.ext.tortoise import paginate + +from apollo.db import RedHatAdvisory +from apollo.db.serialize import RedHatAdvisory_Pydantic + +router = APIRouter(tags=["red_hat"]) + +T = TypeVar("T") + + +class Pagination(Page[T], Generic[T]): + class Config: + allow_population_by_field_name = True + fields = {"items": {"alias": "advisories"}} + + +@router.get( + "/advisories", + response_model=Pagination[RedHatAdvisory_Pydantic], +) +async def list_red_hat_advisories(request: Request): + if not request.state.settings.serve_rh_advisories: + raise HTTPException(404) + + advisories = await paginate( + RedHatAdvisory.all().prefetch_related( + "packages", + "cves", + "bugzilla_tickets", + "affected_products", + ).order_by("-red_hat_issued_at") + ) + return advisories + + +@router.get( + "/advisories/{advisory_name}", + response_model=RedHatAdvisory_Pydantic, +) +async def get_red_hat_advisory(request: Request, advisory_name: str): + if not request.state.settings.serve_rh_advisories: + raise HTTPException(404) + + advisory = await RedHatAdvisory.filter(name=advisory_name).prefetch_related( + "packages", + "cves", + "bugzilla_tickets", + "affected_products", + ).first() + + if advisory is None: + raise HTTPException(404) + + return RedHatAdvisory_Pydantic.from_orm(advisory) diff --git a/apollo/server/routes/login.py b/apollo/server/routes/login.py new file mode 100644 index 0000000..176a9ec --- /dev/null +++ b/apollo/server/routes/login.py @@ -0,0 +1,132 @@ +from fastapi import APIRouter, Request, Form +from fastapi.responses import HTMLResponse, RedirectResponse +from tortoise.expressions import Q + +from apollo.server.utils import templates +from apollo.server.roles import ADMIN +from apollo.server.utils import pwd_context +from apollo.server.settings import OIDC_PROVIDER_NAME, OIDC_PROVIDER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET +from apollo.db import User, Settings + +router = APIRouter(tags=["non-api"]) + + +@router.get("/", response_class=HTMLResponse) +async def login_page(request: Request): + if request.session.get("user"): + return RedirectResponse("/", status_code=302) + + user_count = await User.all().count() + should_show_setup = user_count == 0 + + ctx = { + "request": request, + "should_show_setup": should_show_setup, + } + if not should_show_setup: + # Check if we have OIDC_PROVIDER, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET set + # If so, show the OIDC login button + # If OIDC_PROVIDER_NAME is set, use that as the button text + # Otherwise, use "Login with OIDC" + settings = await Settings.filter( + Q(name=OIDC_PROVIDER_NAME) | Q(name=OIDC_PROVIDER) | + Q(name=OIDC_CLIENT_ID) | Q(name=OIDC_CLIENT_SECRET) + ).all() + if len(settings) >= 3: + provider_name = "Login with OIDC" + # Check if we have OIDC_PROVIDER_NAME set + for setting in settings: + if setting.name == OIDC_PROVIDER_NAME: + provider_name = setting.value + break + ctx["oidc_provider_name"] = provider_name + + return templates.TemplateResponse("login.jinja", ctx) + + +@router.post("/", response_class=HTMLResponse) +async def do_login( + request: Request, + email: str = Form(default=None), + password: str = Form(default=None) +): + if request.session.get("user"): + return RedirectResponse("/", status_code=302) + if not email or not password: + return templates.TemplateResponse( + "login.jinja", { + "request": request, + "error": "Email and password are required", + } + ) + + user = await User.get(email=email) + if not user: + return templates.TemplateResponse( + "login.jinja", { + "request": request, + "error": "Invalid email or password", + } + ) + + if not pwd_context.verify(password, user.password): + return templates.TemplateResponse( + "login.jinja", { + "request": request, + "error": "Invalid email or password", + } + ) + + request.session["user"] = user.id + request.session["user.name"] = user.name + request.session["user.role"] = user.role + return RedirectResponse("/", status_code=302) + + +@router.post( + "/setup", + response_class=HTMLResponse, +) +async def setup_page( + request: Request, + name: str = Form(default=None), + email: str = Form(default=None), + password: str = Form(default=None), + confirm_password: str = Form(default=None), +): + user_count = await User.all().count() + if user_count > 0: + return RedirectResponse("/") + + error = None + if not name: + error = "Name is required" + elif not email: + error = "Email is required" + elif "@" not in email: + error = "Email is invalid" + elif not password: + error = "Password is required" + elif not confirm_password: + error = "Confirm password is required" + elif password != confirm_password: + error = "Passwords do not match" + + if error: + return templates.TemplateResponse( + "login.jinja", { + "request": request, + "should_show_setup": True, + "error": error, + } + ) + + await User.create( + name=name, email=email, password=pwd_context.hash(password), role=ADMIN + ) + return templates.TemplateResponse( + "login.jinja", { + "request": request, + "should_show_setup_success": True, + } + ) diff --git a/apollo/server/routes/logout.py b/apollo/server/routes/logout.py new file mode 100644 index 0000000..cb064e2 --- /dev/null +++ b/apollo/server/routes/logout.py @@ -0,0 +1,16 @@ +from fastapi import APIRouter, Request +from fastapi.responses import RedirectResponse + +router = APIRouter(tags=["non-api"]) + + +@router.get("/") +async def logout(request: Request): + if request.session.get("user"): + request.session.pop("user") + if request.session.get("user.name"): + request.session.pop("user.name") + if request.session.get("user.role"): + request.session.pop("user.role") + + return RedirectResponse("/", status_code=302) diff --git a/apollo/server/routes/red_hat_advisories.py b/apollo/server/routes/red_hat_advisories.py new file mode 100644 index 0000000..42b07b5 --- /dev/null +++ b/apollo/server/routes/red_hat_advisories.py @@ -0,0 +1,85 @@ +from math import ceil + +from fastapi import APIRouter, Request, Depends, Form +from fastapi.responses import HTMLResponse +from fastapi_pagination import Params +from fastapi_pagination.ext.tortoise import paginate + +from apollo.db import RedHatAdvisory +from apollo.server.utils import admin_user_scheme, templates + +from common.fastapi import RenderErrorTemplateException + +router = APIRouter(tags=["non-api"]) + + +@router.get( + "/advisories", + response_class=HTMLResponse, +) +async def list_red_hat_advisories(request: Request, params: Params = Depends()): + if not request.state.settings.serve_rh_advisories: + raise RenderErrorTemplateException() + + params.size = 50 + advisories = await paginate( + RedHatAdvisory.all().order_by("-red_hat_issued_at"), + params=params, + ) + return templates.TemplateResponse( + "red_hat_advisories.jinja", { + "request": request, + "advisories": advisories, + "advisories_pages": ceil(advisories.total / advisories.size), + } + ) + + +@router.get( + "/advisories/{advisory_name}", + response_class=HTMLResponse, +) +async def get_red_hat_advisory(request: Request, advisory_name: str): + if not request.state.settings.serve_rh_advisories: + raise RenderErrorTemplateException() + + advisory = await RedHatAdvisory.get_or_none( + name=advisory_name, + ).prefetch_related( + "packages", + "cves", + "bugzilla_tickets", + "affected_products", + "rpm_rh_overrides", + "rpm_rh_overrides__supported_products_rh_mirror", + "published_advisories", + ) + if advisory is None: + return templates.TemplateResponse( + "error.jinja", { + "request": request, + "message": "Requested advisory not found", + } + ) + + return templates.TemplateResponse( + "red_hat_advisory.jinja", { + "request": request, + "advisory": advisory, + "title": f"Red Hat Advisory {advisory.id}", + } + ) + + +@router.post( + "/advisories/{advisory_name}", + response_class=HTMLResponse, + dependencies=[Depends(admin_user_scheme)], +) +async def execute_red_hat_advisory_action( + request: Request, + advisory_name: str, + action: str = Form(), + data: str = Form() +): + pass diff --git a/apollo/server/routes/statistics.py b/apollo/server/routes/statistics.py new file mode 100644 index 0000000..06fe58a --- /dev/null +++ b/apollo/server/routes/statistics.py @@ -0,0 +1,20 @@ +from fastapi import APIRouter, Request +from fastapi.responses import HTMLResponse +from apollo.db import RedHatAdvisory, Advisory + +from apollo.server.utils import templates + +router = APIRouter(tags=["non-api"]) + + +@router.get("/", response_class=HTMLResponse) +async def statistics(request: Request): + rh_advisory_count = await RedHatAdvisory.all().count() + advisory_count = await Advisory.all().count() + return templates.TemplateResponse( + "index.jinja", { + "request": request, + "rh_advisory_count": rh_advisory_count, + "advisory_count": advisory_count, + } + ) diff --git a/apollo/server/server.py b/apollo/server/server.py new file mode 100644 index 0000000..999cc7b --- /dev/null +++ b/apollo/server/server.py @@ -0,0 +1,103 @@ +import secrets + +from tortoise import Tortoise + +Tortoise.init_models(["apollo.db"], "models") # noqa # pylint: disable=wrong-import-position + +from fastapi import FastAPI, Request, Depends +from fastapi.responses import JSONResponse +from starlette.middleware.sessions import SessionMiddleware +from fastapi_pagination import add_pagination + +from apollo.server.routes.statistics import router as statistics_router +from apollo.server.routes.login import router as login_router +from apollo.server.routes.logout import router as logout_router +from apollo.server.routes.admin_index import router as admin_index_router +from apollo.server.routes.api_advisories import router as api_advisories_router +from apollo.server.routes.api_compat import router as api_compat_router +from apollo.server.routes.api_red_hat import router as api_red_hat_router +from apollo.server.routes.advisories import router as advisories_router +from apollo.server.routes.red_hat_advisories import router as red_hat_advisories_router +from apollo.server.settings import SECRET_KEY, SettingsMiddleware, get_setting +from apollo.server.utils import admin_user_scheme, templates +from apollo.db import Settings + +from common.info import Info +from common.logger import Logger +from common.database import Database +from common.fastapi import StaticFilesSym, RenderErrorTemplateException + +app = FastAPI() + +app.mount( + "/static", StaticFilesSym(directory="apollo/server/static"), name="static" +) +app.mount( + "/assets", StaticFilesSym(directory="apollo/server/assets"), name="assets" +) + +app.add_middleware(SettingsMiddleware) + +app.include_router(advisories_router) +app.include_router(statistics_router, prefix="/statistics") +app.include_router(login_router, prefix="/login") +app.include_router(logout_router, prefix="/logout") +app.include_router( + admin_index_router, + prefix="/admin", + dependencies=[Depends(admin_user_scheme)] +) +app.include_router(red_hat_advisories_router, prefix="/red_hat") +app.include_router(api_advisories_router, prefix="/api/v3/advisories") +app.include_router(api_compat_router, prefix="/v2/advisories") +app.include_router(api_red_hat_router, prefix="/api/v3/red_hat") + +add_pagination(app) + +Info("apollo2") +Logger() +Database(True, app, ["apollo.db"]) + + +@app.exception_handler(404) +async def not_found_handler(request, exc): + if request.url.path.startswith("/api" + ) or request.url.path.startswith("/v2"): + return JSONResponse({"error": "Not found"}, status_code=404) + return await render_template_exception_handler(request, None) + + +@app.exception_handler(RenderErrorTemplateException) +async def render_template_exception_handler( + request: Request, exc: RenderErrorTemplateException +): + if request.url.path.startswith("/api" + ) or request.url.path.startswith("/v2"): + return JSONResponse( + {"error": exc.msg if exc and exc.msg else "Not found"}, + status_code=exc.status_code if exc and exc.status_code else 404, + ) + return templates.TemplateResponse( + "error.jinja", { + "request": request, + "message": exc.msg if exc and exc.msg else "Page not found", + }, + status_code=exc.status_code if exc and exc.status_code else 404 + ) + + +@app.on_event("startup") +async def startup(): + # Generate secret-key if it does not exist in the database + secret_key = await get_setting(SECRET_KEY) + if not secret_key: + # Generate random secret key + secret_key = secrets.token_hex(32) + await Settings.create(name=SECRET_KEY, value=secret_key) + + # Mount SessionMiddleware + app.add_middleware( + SessionMiddleware, + secret_key=secret_key, + max_age=60 * 60 * 24 * 7, # 1 week + ) diff --git a/apollo/server/settings.py b/apollo/server/settings.py new file mode 100644 index 0000000..f324192 --- /dev/null +++ b/apollo/server/settings.py @@ -0,0 +1,64 @@ +from typing import Optional +from dataclasses import dataclass + +from fastapi import Request, Response +from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint + +from apollo.db import Settings +from apollo.server.utils import is_admin_user + +SECRET_KEY = "secret-key" +OIDC_PROVIDER_NAME = "oidc-provider-name" +OIDC_PROVIDER = "oidc-provider" +OIDC_CLIENT_ID = "oidc-client-id" +OIDC_CLIENT_SECRET = "oidc-client-secret" +OIDC_ADMIN_ROLE = "oidc-admin-role" +OIDC_ELEVATED_ROLE = "oidc-elevated-role" +RH_MATCH_STALE = "rh-match-stale" +DISABLE_SERVING_RH_ADVISORIES = "disable-serving-rh-advisories" + + +async def get_setting(name: str) -> Optional[str]: + setting = await Settings.filter(name=name).get_or_none() + if setting is None: + return None + return setting.value + + +async def get_setting_bool(name: str) -> Optional[bool]: + setting = await Settings.filter(name=name).get_or_none() + if setting is None: + return None + return setting.value == "True" + + +async def should_serve_red_hat_advisories(request: Request) -> bool: + setting = await get_setting_bool(DISABLE_SERVING_RH_ADVISORIES) + admin_user = await is_admin_user(request) + + if setting and not admin_user: + return False + + return True + + +@dataclass +class SettingsContext: + serve_rh_advisories: bool + is_admin: bool + + +class SettingsMiddleware(BaseHTTPMiddleware): + async def dispatch( + self, request: Request, call_next: RequestResponseEndpoint + ) -> Response: + should_serve_rh_advisories = await should_serve_red_hat_advisories( + request + ) + + request.state.settings = SettingsContext( + serve_rh_advisories=should_serve_rh_advisories, + is_admin=await is_admin_user(request), + ) + + return await call_next(request) diff --git a/apollo/server/static/BUILD.bazel b/apollo/server/static/BUILD.bazel new file mode 100644 index 0000000..75e4255 --- /dev/null +++ b/apollo/server/static/BUILD.bazel @@ -0,0 +1,20 @@ +load("@aspect_rules_esbuild//esbuild:defs.bzl", "esbuild") + +srcs = glob([ + "*.ts", + "*.scss", +]) + +esbuild( + name = "static", + srcs = srcs + [ + "//:node_modules/@carbon/themes", + "//:node_modules/@carbon/web-components", + "//:node_modules/carbon-components", + "//:node_modules/esbuild-sass-plugin", + ], + config = ":esbuild.config.mjs", + entry_point = "index.ts", + output_css = "static.css", + visibility = ["//visibility:public"], +) diff --git a/apollo/server/static/esbuild.config.mjs b/apollo/server/static/esbuild.config.mjs new file mode 100644 index 0000000..0672758 --- /dev/null +++ b/apollo/server/static/esbuild.config.mjs @@ -0,0 +1,11 @@ +import { sassPlugin } from 'esbuild-sass-plugin'; + +export default { + keepNames: true, + resolveExtensions: ['.ts', '.js', '.tsx', '.jsx'], + plugins: [sassPlugin()], + define: { + 'process.env.NODE_ENV': '"production"', + 'window.process.env.DEBUG': 'undefined', + }, +}; diff --git a/apollo/server/static/index.ts b/apollo/server/static/index.ts new file mode 100644 index 0000000..387a619 --- /dev/null +++ b/apollo/server/static/index.ts @@ -0,0 +1,94 @@ +import './theme.scss'; +import '@carbon/web-components/es/components/ui-shell'; +import '@carbon/web-components/es/components/structured-list'; +import '@carbon/web-components/es/components/data-table'; +import '@carbon/web-components/es/components/pagination'; +import '@carbon/web-components/es/components/form'; +import '@carbon/web-components/es/components/input'; +import '@carbon/web-components/es/components/button'; +import '@carbon/web-components/es/components/notification'; +import '@carbon/web-components/es/components/tag'; +import '@carbon/web-components/es/components/list'; + +function fixForm() { + const buttons = document.querySelectorAll('bx-btn'); + buttons.forEach((button) => { + if (!button.getAttribute('form_id')) { + return; + } + + const form: any = document.querySelector( + 'form#' + button.getAttribute('form_id') + ); + if (form) { + button.addEventListener('click', () => { + form.submit(); + }); + } + }); + + // Also do the same for bx-input and enter key + const inputs = document.querySelectorAll('bx-input'); + inputs.forEach((input) => { + input.addEventListener('keydown', (evt: any) => { + if (!input.getAttribute('form_id')) { + return; + } + + if (evt.key === 'Enter') { + const form: any = document.querySelector( + 'form#' + input.getAttribute('form_id') + ); + if (form) { + form.submit(); + } + } + }); + }); +} + +document.addEventListener('DOMContentLoaded', function () { + document.querySelectorAll('bx-pagination').forEach((el) => { + el.addEventListener('bx-pagination-changed-current', function (evt: any) { + const pageSize = parseInt(el.getAttribute('page-size') || '0'); + const newPage = Math.ceil(evt.detail.start / pageSize) + 1; + + const searchParams = new URLSearchParams(window.location.search); + searchParams.set('page', newPage.toString()); + window.location.search = searchParams.toString(); + }); + }); + + // Add "active" if location has prefix, e.g. /admin/ -> /admin + // For / only we need to check if the location is exactly / + const pathname = window.location.pathname; + document.querySelectorAll('bx-side-nav-link').forEach((el) => { + const href = el.getAttribute('href'); + if (href === '/') { + if (pathname === '/') { + el.setAttribute('active', ''); + } + } else if (pathname.startsWith(href || '')) { + el.setAttribute('active', ''); + } + }); + + // Change "search" query parameter when the search field encounters Enter + const searchToolbar: any = document.querySelector('bx-table-toolbar-search'); + if (searchToolbar) { + const searchBar: any = + searchToolbar.shadowRoot.querySelector('.bx--search-input'); + if (searchBar) { + searchBar.addEventListener('keydown', (evt: any) => { + if (evt.key === 'Enter') { + const searchParams = new URLSearchParams(window.location.search); + searchParams.set('search', searchBar.value); + searchParams.set('page', '1'); + window.location.search = searchParams.toString(); + } + }); + } + } + + fixForm(); +}); diff --git a/apollo/server/static/theme.scss b/apollo/server/static/theme.scss new file mode 100644 index 0000000..11e0a93 --- /dev/null +++ b/apollo/server/static/theme.scss @@ -0,0 +1,19 @@ +$feature-flags: ( + enable-css-custom-properties: true, + grid-columns-16: true, +); + +@use 'carbon-components/scss/globals/scss/vendor/@carbon/elements/scss/themes/generated/themes'; +@import 'carbon-components/scss/globals/scss/styles.scss'; + +:root { + @include carbon--theme($carbon--theme--g90, true); +} + +a { + color: map-get($carbon--theme--g90, 'link-01'); +} + +.bx--inline-notification__text-wrapper { + width: 100%; +} diff --git a/apollo/server/templates/BUILD.bazel b/apollo/server/templates/BUILD.bazel new file mode 100644 index 0000000..e69de29 diff --git a/apollo/server/templates/admin_index.jinja b/apollo/server/templates/admin_index.jinja new file mode 100644 index 0000000..930d413 --- /dev/null +++ b/apollo/server/templates/admin_index.jinja @@ -0,0 +1,5 @@ +{% extends "admin_layout.jinja" %} + +{% block admin_content %} +test +{% endblock %} \ No newline at end of file diff --git a/apollo/server/templates/admin_layout.jinja b/apollo/server/templates/admin_layout.jinja new file mode 100644 index 0000000..d55a2b8 --- /dev/null +++ b/apollo/server/templates/admin_layout.jinja @@ -0,0 +1,53 @@ +{% extends "layout.jinja" %} + +{% block head %} + +{% endblock %} + +{% block content %} + + + General + Users + OIDC + + + +{% block admin_content %}{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/apollo/server/templates/advisories.jinja b/apollo/server/templates/advisories.jinja new file mode 100644 index 0000000..2d328ed --- /dev/null +++ b/apollo/server/templates/advisories.jinja @@ -0,0 +1,45 @@ +{% extends "layout.jinja" %} + +{% block content %} +

Advisories

+ + + + + + + + + + + + + + + + + + Name + Synopsis + Created at + Issued at + Kind + Severity + + + + {% for advisory in advisories.items -%} + + {{ advisory.name }} + {{ advisory.synopsis }} + {{ advisory.created_at.date() }} + {{ advisory.published_at.date() }} + {{ advisory.kind }} + {{ advisory.severity }} + + {% endfor %} + + + +{% endblock %} \ No newline at end of file diff --git a/apollo/server/templates/advisory.jinja b/apollo/server/templates/advisory.jinja new file mode 100644 index 0000000..bb92270 --- /dev/null +++ b/apollo/server/templates/advisory.jinja @@ -0,0 +1,103 @@ +{% extends "layout.jinja" %} + +{% block content %} +
+
+

{{ advisory.name }}

+ {% if advisory.kind == "Security" %} + {% set advisory_tag_type = "red" %} + {% elif advisory.kind == "Bug Fix" %} + {% set advisory_tag_type = "purple" %} + {% elif advisory.kind == "Enhancement" %} + {% set advisory_tag_type = "teal" %} + {% endif %} + {{ advisory.kind }} + {% if advisory.red_hat_advisory_id %} + + Mirrored from + {% if request.state.settings.serve_rh_advisories %} + + {{advisory.red_hat_advisory.name }} + + {% else %} + + {{ advisory.red_hat_advisory.name }} + + {% endif %} + + {% endif %} +
+
+
+ Issued at: {{ advisory.published_at.date() }} +
+
Updated at: {{ advisory.updated_at.date() }}
+
+
+ +
+
+
+
+

Synopsis

+

{{ advisory.synopsis }}

+

+ +

Description

+ {% set description = advisory.description.split("\n") %} + {% for line in description %} +

{{ line }}

+ {% endfor %} +

+ +

Affected products

+ + {% for product in advisory.affected_products %} + {{ product.name }} + {% endfor %} + +

+ +

Fixes

+ + {% for fix in advisory.fixes %} + + {{ fix.ticket_id }} + + {% endfor %} + +

+ +

CVEs

+ + {% for cve in advisory.cves %} + + + {{ cve.cve }} + + + {% endfor %} + +
+
+
+
+

Affected packages

+ {% for product_repo_name, nevras in package_map.items() %} +

{{ product_repo_name }}

+ + {% for nevra in nevras %} + + {{ nevra }} + + {% endfor %} + + + {% endfor %} +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/apollo/server/templates/error.jinja b/apollo/server/templates/error.jinja new file mode 100644 index 0000000..33c3903 --- /dev/null +++ b/apollo/server/templates/error.jinja @@ -0,0 +1,5 @@ +{% extends "layout.jinja" %} + +{% block content %} +

{{ message }}

+{% endblock %} \ No newline at end of file diff --git a/apollo/server/templates/index.jinja b/apollo/server/templates/index.jinja new file mode 100644 index 0000000..7a388ae --- /dev/null +++ b/apollo/server/templates/index.jinja @@ -0,0 +1,27 @@ +{% extends "layout.jinja" %} + +{% block content %} +

Statistics

+ + + + + Service + Resource + Value + + + + + rpmworker + Advisories + {{ advisory_count }} + + + rhworker + Red Hat Advisories + {{ rh_advisory_count }} + + + +{% endblock %} \ No newline at end of file diff --git a/apollo/server/templates/launch_icon.jinja b/apollo/server/templates/launch_icon.jinja new file mode 100644 index 0000000..5b8be62 --- /dev/null +++ b/apollo/server/templates/launch_icon.jinja @@ -0,0 +1,12 @@ + + + launch + + + + \ No newline at end of file diff --git a/apollo/server/templates/layout.jinja b/apollo/server/templates/layout.jinja new file mode 100644 index 0000000..200a13f --- /dev/null +++ b/apollo/server/templates/layout.jinja @@ -0,0 +1,127 @@ + + + + + + + + + {% block title %}Peridot Apollo{% endblock %} + + + + + + + + + {% if notification %} + {% if notification.get("kind") == "error" %} + + {% elif notification.get("kind") == "success" %} + + {% elif notification.get("kind") == "warning" %} + + {% else %} + + {% endif %} + {% endif %} + + {% block head %}{% endblock %} + + + + + + [Apollo] + + Advisories + {% if request.state.settings.serve_rh_advisories %} + Red Hat Advisories + {% endif %} + Statistics + {% if request.state.settings.is_admin %} + Admin + {% endif %} + + + {% if request.session.get("user.name") %} + {{ request.session.get("user.name") }} + Logout + {% else %} + Login + {% endif %} + + + +
+ {% block outer_content %}{% endblock %} + {% if title %} + + + {% endif %} + {% if notification %} +
+ +
+ {% endif %} +
+ +
+ {% block content %}{% endblock %} +
+ + + \ No newline at end of file diff --git a/apollo/server/templates/login.jinja b/apollo/server/templates/login.jinja new file mode 100644 index 0000000..e01d647 --- /dev/null +++ b/apollo/server/templates/login.jinja @@ -0,0 +1,89 @@ + + + + + + + + Peridot Apollo + + + + + + + + +
+
+ Peridot logo
+

Apollo
Errata Management

+
+ + + {% if error %} +
+
+ {{ error }} +
+
+ {% endif %} + {% if should_show_setup_success %} +
+
+ Admin user created successfully. Please login. +
+
+ {% endif %} + +
+ {% if should_show_setup %} +
+ + + Name + + + Email + + + Password + + + Confirm Password + +
+ Create + admin user +
+
+
+ {% else %} +
+ + + Email + + + Password + +
+ Login + {% if oidc_provider_name %} + {{ oidc_provider_name }} + {% endif %} +
+
+
+ {% endif %} +
+
+ + + \ No newline at end of file diff --git a/apollo/server/templates/red_hat_advisories.jinja b/apollo/server/templates/red_hat_advisories.jinja new file mode 100644 index 0000000..b0de796 --- /dev/null +++ b/apollo/server/templates/red_hat_advisories.jinja @@ -0,0 +1,39 @@ +{% extends "layout.jinja" %} + +{% block content %} +

Red Hat Advisories

+ + + + + + + + + + + + Name + Synopsis + Issued at + Indexed at + Kind + Severity + + + + {% for advisory in advisories.items -%} + + {{ advisory.name }} + {{ advisory.synopsis }} + {{ advisory.red_hat_issued_at.date() }} + {{ advisory.created_at.date() }} + {{ advisory.kind }} + {{ advisory.severity }} + + {% endfor %} + + + +{% endblock %} \ No newline at end of file diff --git a/apollo/server/templates/red_hat_advisory.jinja b/apollo/server/templates/red_hat_advisory.jinja new file mode 100644 index 0000000..99e0d21 --- /dev/null +++ b/apollo/server/templates/red_hat_advisory.jinja @@ -0,0 +1,146 @@ +{% extends "layout.jinja" %} + +{% block content %} +
+
+
+
+

{{ advisory.name }}

+ {% if advisory.kind == "Security" %} + {% set advisory_tag_type = "red" %} + {% elif advisory.kind == "Bug Fix" %} + {% set advisory_tag_type = "purple" %} + {% elif advisory.kind == "Enhancement" %} + {% set advisory_tag_type = "teal" %} + {% endif %} + {{ advisory.kind }} + {% for override in advisory.rpm_rh_overrides %} + + Override for {{ override.supported_products_rh_mirror.name }} + + {% endfor %} +
+
+
+ Issued at: {{advisory.red_hat_issued_at.date() }} +
+
+
+
+
+
+
+ + {% include "launch_icon.jinja" %} +
+ Open original +
+
+ +
+ + Override + +
+
+
+ +
+ +
+
+
+
+

Synopsis

+

{{ advisory.synopsis }}

+

+ +

Description

+ {% set description = advisory.description.split("\n") %} + {% for line in description %} +

{{ line }}

+ {% endfor %} +

+ +

Affected products

+ + {% for product in advisory.affected_products %} + + {{ product.name }} - {{ product.major_version }}{% if product.minor_version %}.{{ product.minor_version }}{% + endif %} + + {% endfor %} + +

+ +

Fixes

+ + {% for ticket in advisory.bugzilla_tickets %} + + + {{ ticket.bugzilla_bug_id }} + + + {% endfor %} + +

+ +

CVEs

+ + {% for cve in advisory.cves %} + + + {{ cve.cve }} + + + {% endfor %} + +
+
+
+
+ {% set pkg_list = {} %} + {% for pkg in advisory.packages %} + {% if pkg.repo_name in pkg_list %} + {% set x=pkg_list.__getitem__(pkg.repo_name).append(pkg) %} + {% else %} + {% set x=pkg_list.__setitem__(pkg.repo_name, [pkg]) %} + {% endif %} + {% endfor %} + +

Affected packages

+ {% for repo_name, pkg in pkg_list.items() %} +

{{ repo_name }}

+ + {% for p in pkg %} + + {{ p.nevra }} + + {% endfor %} + + + {% endfor %} +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/apollo/server/utils.py b/apollo/server/utils.py new file mode 100644 index 0000000..ca09f88 --- /dev/null +++ b/apollo/server/utils.py @@ -0,0 +1,50 @@ +from fastapi import Request +from fastapi.templating import Jinja2Templates +from passlib.context import CryptContext + +from apollo.db import User +from apollo.server.roles import ADMIN + +from common.fastapi import RenderErrorTemplateException + +# Do not remove import (for gazelle) +import jinja2 # noqa # pylint: disable=unused-import +import multipart # noqa # pylint: disable=unused-import +import itsdangerous # noqa # pylint: disable=unused-import + +templates = Jinja2Templates(directory="apollo/server/templates") + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + + +async def admin_user_scheme(request: Request) -> User: + user = await user_scheme(request, raise_exc=False) + if not user: + raise RenderErrorTemplateException( + "You need to log in to access this page", + status_code=401, + ) + elif user.role != ADMIN: + raise RenderErrorTemplateException( + "You are not authorized to view this page", + status_code=403, + ) + return user + + +async def user_scheme(request: Request, raise_exc=True) -> User: + user_id = request.session.get("user") + if not user_id: + if raise_exc: + raise RenderErrorTemplateException( + "You need to log in to access this page", + status_code=401, + ) + else: + return None + return await User.get(id=user_id) + + +async def is_admin_user(request: Request) -> bool: + user = await user_scheme(request, raise_exc=False) + return user.role == ADMIN if user else False diff --git a/build/BUILD.bazel b/build/BUILD.bazel new file mode 100644 index 0000000..e69de29 diff --git a/build/macros/BUILD.bazel b/build/macros/BUILD.bazel new file mode 100644 index 0000000..e69de29 diff --git a/build/macros/fastapi.bzl b/build/macros/fastapi.bzl new file mode 100644 index 0000000..9fef7e9 --- /dev/null +++ b/build/macros/fastapi.bzl @@ -0,0 +1,32 @@ +load("@aspect_rules_py//py:defs.bzl", "py_binary") + +def fastapi_binary(name, path, port, deps = [], tags = [], **kwargs): + py_binary( + name = name, + srcs = ["@pypi_hypercorn//:rules_python_wheel_entry_point_hypercorn.py"], + args = ["{}:app".format(path), "--reload", "--bind 127.0.0.1:{}".format(port)], + visibility = ["//:__subpackages__"], + deps = deps + [ + ":{}_lib".format(name), + "@pypi_hypercorn//:pkg", + ], + tags = tags + [ + "ibazel_notify_changes", + ], + **kwargs + ) + + py_binary( + name = "{}.prod".format(name), + srcs = ["@pypi_hypercorn//:rules_python_wheel_entry_point_hypercorn.py"], + args = ["{}:app".format(path), "--reload", "--bind 127.0.0.1:{}".format(port)], + visibility = ["//:__subpackages__"], + deps = deps + [ + ":{}_lib".format(name), + "@pypi_hypercorn//:pkg", + ], + tags = tags + [ + "ibazel_notify_changes", + ], + **kwargs + ) diff --git a/build/patches/0001-Fix-Quart-and-Hypercorn-failing-to-install-with-rule.patch b/build/patches/0001-Fix-Quart-and-Hypercorn-failing-to-install-with-rule.patch new file mode 100644 index 0000000..51b1d0c --- /dev/null +++ b/build/patches/0001-Fix-Quart-and-Hypercorn-failing-to-install-with-rule.patch @@ -0,0 +1,38 @@ +From e0c49b576595abdfa5167fa7726e604701f79cd8 Mon Sep 17 00:00:00 2001 +From: Mustafa Gezen +Date: Sun, 29 Jan 2023 05:54:58 +0100 +Subject: [PATCH] Fix Quart and Hypercorn failing to install with rules_python + +--- + .../tools/wheel_installer/wheel_installer.py | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/python/pip_install/tools/wheel_installer/wheel_installer.py b/python/pip_install/tools/wheel_installer/wheel_installer.py +index 1f6eaf2..3bbaaaf 100644 +--- a/python/pip_install/tools/wheel_installer/wheel_installer.py ++++ b/python/pip_install/tools/wheel_installer/wheel_installer.py +@@ -422,6 +422,21 @@ def main() -> None: + extras = {name: extras_for_pkg} if extras_for_pkg and name else dict() + + whl = next(iter(glob.glob("*.whl"))) ++ ++ # If wheel starts with "Quart" then rename it to "quart" ++ # For some reason the quart package publishes a wheel with a capital Q ++ # but internally it is referenced as "quart". ++ # This leads to the "installer" package failing to read the WHEEL file ++ # Ugly hack, but it is what it is. ++ # The same problem is also present with "Hypercorn". ++ # This developer is definitely doing something weird but whatever. ++ if whl.startswith("Quart"): ++ os.rename(whl, whl.replace("Quart", "quart")) ++ whl = whl.replace("Quart", "quart") ++ if whl.startswith("Hypercorn"): ++ os.rename(whl, whl.replace("Hypercorn", "hypercorn")) ++ whl = whl.replace("Hypercorn", "hypercorn") ++ + _extract_wheel( + wheel_file=whl, + extras=extras, +-- +2.32.0 (Apple Git-132) + diff --git a/build/patches/BUILD.bazel b/build/patches/BUILD.bazel new file mode 100644 index 0000000..ac801d0 --- /dev/null +++ b/build/patches/BUILD.bazel @@ -0,0 +1 @@ +exports_files(["0001-Fix-Quart-and-Hypercorn-failing-to-install-with-rule.patch"]) diff --git a/common/BUILD.bazel b/common/BUILD.bazel new file mode 100644 index 0000000..c0da893 --- /dev/null +++ b/common/BUILD.bazel @@ -0,0 +1,20 @@ +load("@aspect_rules_py//py:defs.bzl", "py_library") + +py_library( + name = "common_lib", + srcs = [ + "database.py", + "env.py", + "fastapi.py", + "info.py", + "logger.py", + "temporal.py", + ], + imports = [".."], + visibility = ["//:__subpackages__"], + deps = [ + "@pypi_fastapi//:pkg", + "@pypi_temporalio//:pkg", + "@pypi_tortoise_orm//:pkg", + ], +) diff --git a/common/database.py b/common/database.py new file mode 100644 index 0000000..149a075 --- /dev/null +++ b/common/database.py @@ -0,0 +1,42 @@ +""" +Database helper methods +""" +from tortoise import Tortoise +from tortoise.contrib.fastapi import register_tortoise + +from common.info import Info + + +class Database(object): + """ + Database connection singleton class + """ + + initialized = False + + def __init__(self, initialize=False, tortoise_app=None, models=None): + if not Database.initialized and not initialize: + raise Exception("Database connection not initialized") + + if tortoise_app: + register_tortoise( + tortoise_app, + db_url=self.conn_str(), + modules={"models": models}, + add_exception_handlers=True, + ) + self.initialized = True + + def conn_str(self): + info = Info() + + return f"postgres://{info.dbuser()}:{info.dbpassword()}@{info.dbhost()}:{info.dbport()}/{info.dbname()}" + + async def init(self, models): + if Database.initialized: + return + await Tortoise.init( + db_url=self.conn_str(), use_tz=True, modules={"models": models} + ) + + self.initialized = True diff --git a/common/env.py b/common/env.py new file mode 100644 index 0000000..9782afe --- /dev/null +++ b/common/env.py @@ -0,0 +1,16 @@ +""" +Environment variables +""" +import os + + +def get_env(): + return os.environ.get("ENV", "development") + + +def is_prod(): + return get_env() == "1" + + +def is_k8s(): + return os.environ.get("KUBERNETES", "0") == "1" diff --git a/common/fastapi.py b/common/fastapi.py new file mode 100644 index 0000000..15d9ef6 --- /dev/null +++ b/common/fastapi.py @@ -0,0 +1,22 @@ +import os + +from fastapi.staticfiles import StaticFiles + + +class StaticFilesSym(StaticFiles): + "subclass StaticFiles middleware to allow symlinks" + def lookup_path(self, path): + for directory in self.all_directories: + full_path = os.path.realpath(os.path.join(directory, path)) + try: + stat_result = os.stat(full_path) + return (full_path, stat_result) + except FileNotFoundError: + pass + return ("", None) + + +class RenderErrorTemplateException(Exception): + def __init__(self, msg=None, status_code=404): + self.msg = msg + self.status_code = status_code diff --git a/common/info.py b/common/info.py new file mode 100644 index 0000000..5013759 --- /dev/null +++ b/common/info.py @@ -0,0 +1,56 @@ +""" +Application information +""" +import os + +from common.env import get_env, is_k8s + + +class Info: + """ + Application information singleton class + """ + + _name = None + _dbname = None + + def __init__(self, name=None, dbname=None): + if not self._name and not name: + raise ValueError("Info.name is not set") + if self._name and name: + raise ValueError("Info.name is already set") + if name: + Info._name = name + Info._dbname = dbname if dbname else name + + self._name = Info._name + + def name(self): + return self._name + + def dbname(self): + return f"{self._dbname}{get_env()}" + + def dbuser(self): + return os.environ.get("DB_USER", "postgres") + + def dbpassword(self): + return os.environ.get("DB_PASSWORD", "postgres") + + def dbhost(self): + return os.environ.get("DB_HOST", "localhost") + + def dbport(self): + return os.environ.get("DB_PORT", "5432") + + def dbsslmode(self): + return os.environ.get("DB_SSLMODE", "disable") + + def temporal_host(self): + if is_k8s(): + return "workflow-temporal-frontend.workflow.svc.cluster.local:7233" + else: + return os.environ.get("TEMPORAL_HOSTPORT", "localhost:7233") + + def temporal_namespace(self): + return os.environ.get("TEMPORAL_NAMESPACE", "default") diff --git a/common/logger.py b/common/logger.py new file mode 100644 index 0000000..a496665 --- /dev/null +++ b/common/logger.py @@ -0,0 +1,52 @@ +""" +This module provides a logger class that +can be used to log messages to the console. +""" +import logging + +from common.env import is_prod +from common.info import Info + + +class Logger(object): + """ + This class provides a logger that can be used to log messages to the console. + """ + + logger = None + + def __init__(self): + info = Info() + + if Logger.logger is None: + level = logging.INFO + if not is_prod(): + level = logging.DEBUG + logging.basicConfig( + level=level, + format="[%(name)s:%(levelname)s:%(asctime)s] %(message)s" + ) + Logger.logger = logging.getLogger(info.name()) + + self.logger = Logger.logger + + def warning(self, msg, *args, **kwargs): + self.logger.warning(msg, *args, **kwargs) + + def error(self, msg, *args, **kwargs): + self.logger.error(msg, *args, **kwargs) + + def info(self, msg, *args, **kwargs): + self.logger.info(msg, *args, **kwargs) + + def debug(self, msg, *args, **kwargs): + self.logger.debug(msg, *args, **kwargs) + + def exception(self, msg, *args, **kwargs): + self.logger.exception(msg, *args, **kwargs) + + def critical(self, msg, *args, **kwargs): + self.logger.critical(msg, *args, **kwargs) + + def fatal(self, msg, *args, **kwargs): + self.logger.fatal(msg, *args, **kwargs) diff --git a/common/temporal.py b/common/temporal.py new file mode 100644 index 0000000..9d5b721 --- /dev/null +++ b/common/temporal.py @@ -0,0 +1,29 @@ +""" +Temporal helper methods +""" + +from temporalio.client import Client + +from common.info import Info + + +class Temporal(object): + """ + Temporal helper singleton class + """ + + client = None + + def __init__(self, initialize=False): + if Temporal.client is None and not initialize: + raise Exception("Temporal client not initialized") + + self.client = Temporal.client + + async def connect(self): + info = Info() + Temporal.client = await Client.connect( + info.temporal_host(), + namespace=info.temporal_namespace(), + ) + self.client = Temporal.client diff --git a/gazelle_python.yaml b/gazelle_python.yaml new file mode 100644 index 0000000..f794c71 --- /dev/null +++ b/gazelle_python.yaml @@ -0,0 +1,1500 @@ +# GENERATED FILE - DO NOT EDIT! +# +# To update this file, run: +# bazel run //:gazelle_python_manifest.update + +manifest: + modules_mapping: + aiohttp: aiohttp + aiohttp.abc: aiohttp + aiohttp.base_protocol: aiohttp + aiohttp.client: aiohttp + aiohttp.client_exceptions: aiohttp + aiohttp.client_proto: aiohttp + aiohttp.client_reqrep: aiohttp + aiohttp.client_ws: aiohttp + aiohttp.connector: aiohttp + aiohttp.cookiejar: aiohttp + aiohttp.formdata: aiohttp + aiohttp.hdrs: aiohttp + aiohttp.helpers: aiohttp + aiohttp.http: aiohttp + aiohttp.http_exceptions: aiohttp + aiohttp.http_parser: aiohttp + aiohttp.http_websocket: aiohttp + aiohttp.http_writer: aiohttp + aiohttp.locks: aiohttp + aiohttp.log: aiohttp + aiohttp.multipart: aiohttp + aiohttp.payload: aiohttp + aiohttp.payload_streamer: aiohttp + aiohttp.pytest_plugin: aiohttp + aiohttp.resolver: aiohttp + aiohttp.streams: aiohttp + aiohttp.tcp_helpers: aiohttp + aiohttp.test_utils: aiohttp + aiohttp.tracing: aiohttp + aiohttp.typedefs: aiohttp + aiohttp.web: aiohttp + aiohttp.web_app: aiohttp + aiohttp.web_exceptions: aiohttp + aiohttp.web_fileresponse: aiohttp + aiohttp.web_log: aiohttp + aiohttp.web_middlewares: aiohttp + aiohttp.web_protocol: aiohttp + aiohttp.web_request: aiohttp + aiohttp.web_response: aiohttp + aiohttp.web_routedef: aiohttp + aiohttp.web_runner: aiohttp + aiohttp.web_server: aiohttp + aiohttp.web_urldispatcher: aiohttp + aiohttp.web_ws: aiohttp + aiohttp.worker: aiohttp + aiosignal: aiosignal + aiosqlite: aiosqlite + aiosqlite.context: aiosqlite + aiosqlite.core: aiosqlite + aiosqlite.cursor: aiosqlite + aiosqlite.tests: aiosqlite + aiosqlite.tests.helpers: aiosqlite + aiosqlite.tests.perf: aiosqlite + aiosqlite.tests.smoke: aiosqlite + anyio: anyio + anyio.abc: anyio + anyio.from_thread: anyio + anyio.lowlevel: anyio + anyio.pytest_plugin: anyio + anyio.streams: anyio + anyio.streams.buffered: anyio + anyio.streams.file: anyio + anyio.streams.memory: anyio + anyio.streams.stapled: anyio + anyio.streams.text: anyio + anyio.streams.tls: anyio + anyio.to_process: anyio + anyio.to_thread: anyio + astroid: astroid + astroid.arguments: astroid + astroid.astroid_manager: astroid + astroid.bases: astroid + astroid.brain: astroid + astroid.brain.brain_argparse: astroid + astroid.brain.brain_attrs: astroid + astroid.brain.brain_boto3: astroid + astroid.brain.brain_builtin_inference: astroid + astroid.brain.brain_collections: astroid + astroid.brain.brain_crypt: astroid + astroid.brain.brain_ctypes: astroid + astroid.brain.brain_curses: astroid + astroid.brain.brain_dataclasses: astroid + astroid.brain.brain_dateutil: astroid + astroid.brain.brain_fstrings: astroid + astroid.brain.brain_functools: astroid + astroid.brain.brain_gi: astroid + astroid.brain.brain_hashlib: astroid + astroid.brain.brain_http: astroid + astroid.brain.brain_hypothesis: astroid + astroid.brain.brain_io: astroid + astroid.brain.brain_mechanize: astroid + astroid.brain.brain_multiprocessing: astroid + astroid.brain.brain_namedtuple_enum: astroid + astroid.brain.brain_nose: astroid + astroid.brain.brain_numpy_core_einsumfunc: astroid + astroid.brain.brain_numpy_core_fromnumeric: astroid + astroid.brain.brain_numpy_core_function_base: astroid + astroid.brain.brain_numpy_core_multiarray: astroid + astroid.brain.brain_numpy_core_numeric: astroid + astroid.brain.brain_numpy_core_numerictypes: astroid + astroid.brain.brain_numpy_core_umath: astroid + astroid.brain.brain_numpy_ma: astroid + astroid.brain.brain_numpy_ndarray: astroid + astroid.brain.brain_numpy_random_mtrand: astroid + astroid.brain.brain_numpy_utils: astroid + astroid.brain.brain_pathlib: astroid + astroid.brain.brain_pkg_resources: astroid + astroid.brain.brain_pytest: astroid + astroid.brain.brain_qt: astroid + astroid.brain.brain_random: astroid + astroid.brain.brain_re: astroid + astroid.brain.brain_regex: astroid + astroid.brain.brain_responses: astroid + astroid.brain.brain_scipy_signal: astroid + astroid.brain.brain_signal: astroid + astroid.brain.brain_six: astroid + astroid.brain.brain_sqlalchemy: astroid + astroid.brain.brain_ssl: astroid + astroid.brain.brain_subprocess: astroid + astroid.brain.brain_threading: astroid + astroid.brain.brain_type: astroid + astroid.brain.brain_typing: astroid + astroid.brain.brain_unittest: astroid + astroid.brain.brain_uuid: astroid + astroid.brain.helpers: astroid + astroid.builder: astroid + astroid.const: astroid + astroid.constraint: astroid + astroid.context: astroid + astroid.decorators: astroid + astroid.exceptions: astroid + astroid.filter_statements: astroid + astroid.helpers: astroid + astroid.inference: astroid + astroid.inference_tip: astroid + astroid.interpreter: astroid + astroid.interpreter.dunder_lookup: astroid + astroid.interpreter.objectmodel: astroid + astroid.manager: astroid + astroid.mixins: astroid + astroid.modutils: astroid + astroid.node_classes: astroid + astroid.nodes: astroid + astroid.nodes.as_string: astroid + astroid.nodes.const: astroid + astroid.nodes.node_classes: astroid + astroid.nodes.node_ng: astroid + astroid.nodes.scoped_nodes: astroid + astroid.nodes.scoped_nodes.mixin: astroid + astroid.nodes.scoped_nodes.scoped_nodes: astroid + astroid.nodes.scoped_nodes.utils: astroid + astroid.nodes.utils: astroid + astroid.objects: astroid + astroid.protocols: astroid + astroid.raw_building: astroid + astroid.rebuilder: astroid + astroid.scoped_nodes: astroid + astroid.test_utils: astroid + astroid.transforms: astroid + astroid.typing: astroid + astroid.util: astroid + async_timeout: async_timeout + asyncpg: asyncpg + asyncpg.cluster: asyncpg + asyncpg.compat: asyncpg + asyncpg.connect_utils: asyncpg + asyncpg.connection: asyncpg + asyncpg.connresource: asyncpg + asyncpg.cursor: asyncpg + asyncpg.exceptions: asyncpg + asyncpg.introspection: asyncpg + asyncpg.pgproto: asyncpg + asyncpg.pgproto.pgproto: asyncpg + asyncpg.pgproto.types: asyncpg + asyncpg.pool: asyncpg + asyncpg.prepared_stmt: asyncpg + asyncpg.protocol: asyncpg + asyncpg.protocol.codecs: asyncpg + asyncpg.protocol.protocol: asyncpg + asyncpg.serverversion: asyncpg + asyncpg.transaction: asyncpg + asyncpg.types: asyncpg + asyncpg.utils: asyncpg + attr: attrs + attr.converters: attrs + attr.exceptions: attrs + attr.filters: attrs + attr.setters: attrs + attr.validators: attrs + attrs: attrs + attrs.converters: attrs + attrs.exceptions: attrs + attrs.filters: attrs + attrs.setters: attrs + attrs.validators: attrs + autoflake: autoflake + bcrypt: bcrypt + black: black + black.brackets: black + black.cache: black + black.comments: black + black.concurrency: black + black.const: black + black.debug: black + black.files: black + black.handle_ipynb_magics: black + black.linegen: black + black.lines: black + black.mode: black + black.nodes: black + black.numerics: black + black.output: black + black.parsing: black + black.report: black + black.rusty: black + black.strings: black + black.trans: black + blackd: black + blackd.middlewares: black + blib2to3: black + blib2to3.pgen2: black + blib2to3.pgen2.conv: black + blib2to3.pgen2.driver: black + blib2to3.pgen2.grammar: black + blib2to3.pgen2.literals: black + blib2to3.pgen2.parse: black + blib2to3.pgen2.pgen: black + blib2to3.pgen2.token: black + blib2to3.pgen2.tokenize: black + blib2to3.pygram: black + blib2to3.pytree: black + bs4: beautifulsoup4 + bs4.builder: beautifulsoup4 + bs4.dammit: beautifulsoup4 + bs4.diagnose: beautifulsoup4 + bs4.element: beautifulsoup4 + bs4.formatter: beautifulsoup4 + bs4.tests: beautifulsoup4 + bs4.tests.test_builder: beautifulsoup4 + bs4.tests.test_builder_registry: beautifulsoup4 + bs4.tests.test_dammit: beautifulsoup4 + bs4.tests.test_docs: beautifulsoup4 + bs4.tests.test_element: beautifulsoup4 + bs4.tests.test_formatter: beautifulsoup4 + bs4.tests.test_html5lib: beautifulsoup4 + bs4.tests.test_htmlparser: beautifulsoup4 + bs4.tests.test_lxml: beautifulsoup4 + bs4.tests.test_navigablestring: beautifulsoup4 + bs4.tests.test_pageelement: beautifulsoup4 + bs4.tests.test_soup: beautifulsoup4 + bs4.tests.test_tag: beautifulsoup4 + bs4.tests.test_tree: beautifulsoup4 + certifi: certifi + certifi.core: certifi + charset_normalizer: charset_normalizer + charset_normalizer.api: charset_normalizer + charset_normalizer.assets: charset_normalizer + charset_normalizer.cd: charset_normalizer + charset_normalizer.cli: charset_normalizer + charset_normalizer.cli.normalizer: charset_normalizer + charset_normalizer.constant: charset_normalizer + charset_normalizer.legacy: charset_normalizer + charset_normalizer.md: charset_normalizer + charset_normalizer.models: charset_normalizer + charset_normalizer.utils: charset_normalizer + charset_normalizer.version: charset_normalizer + click: click + click.core: click + click.decorators: click + click.exceptions: click + click.formatting: click + click.globals: click + click.parser: click + click.shell_completion: click + click.termui: click + click.testing: click + click.types: click + click.utils: click + dataclass_wizard: dataclass_wizard + dataclass_wizard.abstractions: dataclass_wizard + dataclass_wizard.bases: dataclass_wizard + dataclass_wizard.bases_meta: dataclass_wizard + dataclass_wizard.class_helper: dataclass_wizard + dataclass_wizard.constants: dataclass_wizard + dataclass_wizard.decorators: dataclass_wizard + dataclass_wizard.dumpers: dataclass_wizard + dataclass_wizard.enums: dataclass_wizard + dataclass_wizard.errors: dataclass_wizard + dataclass_wizard.lazy_imports: dataclass_wizard + dataclass_wizard.loaders: dataclass_wizard + dataclass_wizard.log: dataclass_wizard + dataclass_wizard.models: dataclass_wizard + dataclass_wizard.parsers: dataclass_wizard + dataclass_wizard.property_wizard: dataclass_wizard + dataclass_wizard.serial_json: dataclass_wizard + dataclass_wizard.type_def: dataclass_wizard + dataclass_wizard.utils: dataclass_wizard + dataclass_wizard.utils.dict_helper: dataclass_wizard + dataclass_wizard.utils.lazy_loader: dataclass_wizard + dataclass_wizard.utils.string_conv: dataclass_wizard + dataclass_wizard.utils.type_conv: dataclass_wizard + dataclass_wizard.utils.typing_compat: dataclass_wizard + dataclass_wizard.utils.wrappers: dataclass_wizard + dataclass_wizard.wizard_cli: dataclass_wizard + dataclass_wizard.wizard_cli.cli: dataclass_wizard + dataclass_wizard.wizard_cli.schema: dataclass_wizard + dataclass_wizard.wizard_mixins: dataclass_wizard + dateutil: python_dateutil + dateutil.easter: python_dateutil + dateutil.parser: python_dateutil + dateutil.parser.isoparser: python_dateutil + dateutil.relativedelta: python_dateutil + dateutil.rrule: python_dateutil + dateutil.tz: python_dateutil + dateutil.tz.tz: python_dateutil + dateutil.tz.win: python_dateutil + dateutil.tzwin: python_dateutil + dateutil.utils: python_dateutil + dateutil.zoneinfo: python_dateutil + dateutil.zoneinfo.rebuild: python_dateutil + dill: dill + dill.detect: dill + dill.logger: dill + dill.objtypes: dill + dill.pointers: dill + dill.session: dill + dill.settings: dill + dill.source: dill + dill.temp: dill + dill.tests: dill + dill.tests.test_check: dill + dill.tests.test_classdef: dill + dill.tests.test_dataclasses: dill + dill.tests.test_detect: dill + dill.tests.test_dictviews: dill + dill.tests.test_diff: dill + dill.tests.test_extendpickle: dill + dill.tests.test_fglobals: dill + dill.tests.test_file: dill + dill.tests.test_functions: dill + dill.tests.test_functors: dill + dill.tests.test_logger: dill + dill.tests.test_mixins: dill + dill.tests.test_module: dill + dill.tests.test_moduledict: dill + dill.tests.test_nested: dill + dill.tests.test_objects: dill + dill.tests.test_properties: dill + dill.tests.test_pycapsule: dill + dill.tests.test_recursive: dill + dill.tests.test_registered: dill + dill.tests.test_restricted: dill + dill.tests.test_selected: dill + dill.tests.test_session: dill + dill.tests.test_source: dill + dill.tests.test_temp: dill + dill.tests.test_weakref: dill + fastapi: fastapi + fastapi.applications: fastapi + fastapi.background: fastapi + fastapi.concurrency: fastapi + fastapi.datastructures: fastapi + fastapi.dependencies: fastapi + fastapi.dependencies.models: fastapi + fastapi.dependencies.utils: fastapi + fastapi.encoders: fastapi + fastapi.exception_handlers: fastapi + fastapi.exceptions: fastapi + fastapi.logger: fastapi + fastapi.middleware: fastapi + fastapi.middleware.asyncexitstack: fastapi + fastapi.middleware.cors: fastapi + fastapi.middleware.gzip: fastapi + fastapi.middleware.httpsredirect: fastapi + fastapi.middleware.trustedhost: fastapi + fastapi.middleware.wsgi: fastapi + fastapi.openapi: fastapi + fastapi.openapi.constants: fastapi + fastapi.openapi.docs: fastapi + fastapi.openapi.models: fastapi + fastapi.openapi.utils: fastapi + fastapi.param_functions: fastapi + fastapi.params: fastapi + fastapi.requests: fastapi + fastapi.responses: fastapi + fastapi.routing: fastapi + fastapi.security: fastapi + fastapi.security.api_key: fastapi + fastapi.security.base: fastapi + fastapi.security.http: fastapi + fastapi.security.oauth2: fastapi + fastapi.security.open_id_connect_url: fastapi + fastapi.security.utils: fastapi + fastapi.staticfiles: fastapi + fastapi.templating: fastapi + fastapi.testclient: fastapi + fastapi.types: fastapi + fastapi.utils: fastapi + fastapi.websockets: fastapi + fastapi_pagination: fastapi_pagination + fastapi_pagination.api: fastapi_pagination + fastapi_pagination.bases: fastapi_pagination + fastapi_pagination.cursor: fastapi_pagination + fastapi_pagination.default: fastapi_pagination + fastapi_pagination.ext: fastapi_pagination + fastapi_pagination.ext.async_sqlalchemy: fastapi_pagination + fastapi_pagination.ext.async_sqlmodel: fastapi_pagination + fastapi_pagination.ext.asyncpg: fastapi_pagination + fastapi_pagination.ext.beanie: fastapi_pagination + fastapi_pagination.ext.cassandra: fastapi_pagination + fastapi_pagination.ext.databases: fastapi_pagination + fastapi_pagination.ext.django: fastapi_pagination + fastapi_pagination.ext.gino: fastapi_pagination + fastapi_pagination.ext.mongoengine: fastapi_pagination + fastapi_pagination.ext.motor: fastapi_pagination + fastapi_pagination.ext.orm: fastapi_pagination + fastapi_pagination.ext.ormar: fastapi_pagination + fastapi_pagination.ext.piccolo: fastapi_pagination + fastapi_pagination.ext.pony: fastapi_pagination + fastapi_pagination.ext.pymongo: fastapi_pagination + fastapi_pagination.ext.sqlalchemy: fastapi_pagination + fastapi_pagination.ext.sqlalchemy_future: fastapi_pagination + fastapi_pagination.ext.sqlmodel: fastapi_pagination + fastapi_pagination.ext.tortoise: fastapi_pagination + fastapi_pagination.ext.utils: fastapi_pagination + fastapi_pagination.iterables: fastapi_pagination + fastapi_pagination.limit_offset: fastapi_pagination + fastapi_pagination.links: fastapi_pagination + fastapi_pagination.links.bases: fastapi_pagination + fastapi_pagination.links.default: fastapi_pagination + fastapi_pagination.links.limmit_offset: fastapi_pagination + fastapi_pagination.paginator: fastapi_pagination + fastapi_pagination.types: fastapi_pagination + fastapi_pagination.utils: fastapi_pagination + frozenlist: frozenlist + google.protobuf: protobuf + google.protobuf.any_pb2: protobuf + google.protobuf.api_pb2: protobuf + google.protobuf.compiler: protobuf + google.protobuf.compiler.plugin_pb2: protobuf + google.protobuf.descriptor: protobuf + google.protobuf.descriptor_database: protobuf + google.protobuf.descriptor_pb2: protobuf + google.protobuf.descriptor_pool: protobuf + google.protobuf.duration_pb2: protobuf + google.protobuf.empty_pb2: protobuf + google.protobuf.field_mask_pb2: protobuf + google.protobuf.internal: protobuf + google.protobuf.internal.api_implementation: protobuf + google.protobuf.internal.builder: protobuf + google.protobuf.internal.containers: protobuf + google.protobuf.internal.decoder: protobuf + google.protobuf.internal.descriptor_database_test: protobuf + google.protobuf.internal.descriptor_pool_test: protobuf + google.protobuf.internal.descriptor_test: protobuf + google.protobuf.internal.encoder: protobuf + google.protobuf.internal.enum_type_wrapper: protobuf + google.protobuf.internal.extension_dict: protobuf + google.protobuf.internal.generator_test: protobuf + google.protobuf.internal.import_test: protobuf + google.protobuf.internal.import_test_package: protobuf + google.protobuf.internal.json_format_test: protobuf + google.protobuf.internal.keywords_test: protobuf + google.protobuf.internal.message_factory_test: protobuf + google.protobuf.internal.message_listener: protobuf + google.protobuf.internal.message_test: protobuf + google.protobuf.internal.proto_builder_test: protobuf + google.protobuf.internal.python_message: protobuf + google.protobuf.internal.reflection_test: protobuf + google.protobuf.internal.service_reflection_test: protobuf + google.protobuf.internal.symbol_database_test: protobuf + google.protobuf.internal.test_util: protobuf + google.protobuf.internal.testing_refleaks: protobuf + google.protobuf.internal.text_encoding_test: protobuf + google.protobuf.internal.text_format_test: protobuf + google.protobuf.internal.type_checkers: protobuf + google.protobuf.internal.unknown_fields_test: protobuf + google.protobuf.internal.well_known_types: protobuf + google.protobuf.internal.well_known_types_test: protobuf + google.protobuf.internal.wire_format: protobuf + google.protobuf.internal.wire_format_test: protobuf + google.protobuf.json_format: protobuf + google.protobuf.message: protobuf + google.protobuf.message_factory: protobuf + google.protobuf.proto_builder: protobuf + google.protobuf.pyext: protobuf + google.protobuf.pyext.cpp_message: protobuf + google.protobuf.reflection: protobuf + google.protobuf.service: protobuf + google.protobuf.service_reflection: protobuf + google.protobuf.source_context_pb2: protobuf + google.protobuf.struct_pb2: protobuf + google.protobuf.symbol_database: protobuf + google.protobuf.text_encoding: protobuf + google.protobuf.text_format: protobuf + google.protobuf.timestamp_pb2: protobuf + google.protobuf.type_pb2: protobuf + google.protobuf.unknown_fields: protobuf + google.protobuf.util: protobuf + google.protobuf.wrappers_pb2: protobuf + h11: h11 + h11.tests: h11 + h11.tests.helpers: h11 + h11.tests.test_against_stdlib_http: h11 + h11.tests.test_connection: h11 + h11.tests.test_events: h11 + h11.tests.test_headers: h11 + h11.tests.test_helpers: h11 + h11.tests.test_io: h11 + h11.tests.test_receivebuffer: h11 + h11.tests.test_state: h11 + h11.tests.test_util: h11 + h2: h2 + h2.config: h2 + h2.connection: h2 + h2.errors: h2 + h2.events: h2 + h2.exceptions: h2 + h2.frame_buffer: h2 + h2.settings: h2 + h2.stream: h2 + h2.utilities: h2 + h2.windows: h2 + hpack: hpack + hpack.exceptions: hpack + hpack.hpack: hpack + hpack.huffman: hpack + hpack.huffman_constants: hpack + hpack.huffman_table: hpack + hpack.struct: hpack + hpack.table: hpack + httpcore: httpcore + httpcore.backends: httpcore + httpcore.backends.asyncio: httpcore + httpcore.backends.auto: httpcore + httpcore.backends.base: httpcore + httpcore.backends.mock: httpcore + httpcore.backends.sync: httpcore + httpcore.backends.trio: httpcore + httpx: httpx + hypercorn: hypercorn + hypercorn.app_wrappers: hypercorn + hypercorn.asyncio: hypercorn + hypercorn.asyncio.lifespan: hypercorn + hypercorn.asyncio.run: hypercorn + hypercorn.asyncio.statsd: hypercorn + hypercorn.asyncio.task_group: hypercorn + hypercorn.asyncio.tcp_server: hypercorn + hypercorn.asyncio.udp_server: hypercorn + hypercorn.asyncio.worker_context: hypercorn + hypercorn.config: hypercorn + hypercorn.events: hypercorn + hypercorn.logging: hypercorn + hypercorn.middleware: hypercorn + hypercorn.middleware.dispatcher: hypercorn + hypercorn.middleware.http_to_https: hypercorn + hypercorn.middleware.wsgi: hypercorn + hypercorn.protocol: hypercorn + hypercorn.protocol.events: hypercorn + hypercorn.protocol.h11: hypercorn + hypercorn.protocol.h2: hypercorn + hypercorn.protocol.h3: hypercorn + hypercorn.protocol.http_stream: hypercorn + hypercorn.protocol.quic: hypercorn + hypercorn.protocol.ws_stream: hypercorn + hypercorn.run: hypercorn + hypercorn.statsd: hypercorn + hypercorn.trio: hypercorn + hypercorn.trio.lifespan: hypercorn + hypercorn.trio.run: hypercorn + hypercorn.trio.statsd: hypercorn + hypercorn.trio.task_group: hypercorn + hypercorn.trio.tcp_server: hypercorn + hypercorn.trio.udp_server: hypercorn + hypercorn.trio.worker_context: hypercorn + hypercorn.typing: hypercorn + hypercorn.utils: hypercorn + hyperframe: hyperframe + hyperframe.exceptions: hyperframe + hyperframe.flags: hyperframe + hyperframe.frame: hyperframe + idna: idna + idna.codec: idna + idna.compat: idna + idna.core: idna + idna.idnadata: idna + idna.intranges: idna + idna.package_data: idna + idna.uts46data: idna + iso8601: iso8601 + iso8601.iso8601: iso8601 + iso8601.test_iso8601: iso8601 + isort: isort + isort.api: isort + isort.comments: isort + isort.core: isort + isort.deprecated: isort + isort.deprecated.finders: isort + isort.exceptions: isort + isort.files: isort + isort.format: isort + isort.hooks: isort + isort.identify: isort + isort.io: isort + isort.literal: isort + isort.logo: isort + isort.main: isort + isort.output: isort + isort.parse: isort + isort.place: isort + isort.profiles: isort + isort.pylama_isort: isort + isort.sections: isort + isort.settings: isort + isort.setuptools_commands: isort + isort.sorting: isort + isort.stdlibs: isort + isort.stdlibs.all: isort + isort.stdlibs.py2: isort + isort.stdlibs.py27: isort + isort.stdlibs.py3: isort + isort.stdlibs.py310: isort + isort.stdlibs.py311: isort + isort.stdlibs.py36: isort + isort.stdlibs.py37: isort + isort.stdlibs.py38: isort + isort.stdlibs.py39: isort + isort.utils: isort + isort.wrap: isort + isort.wrap_modes: isort + itsdangerous: itsdangerous + itsdangerous.encoding: itsdangerous + itsdangerous.exc: itsdangerous + itsdangerous.serializer: itsdangerous + itsdangerous.signer: itsdangerous + itsdangerous.timed: itsdangerous + itsdangerous.url_safe: itsdangerous + jinja2: Jinja2 + jinja2.async_utils: Jinja2 + jinja2.bccache: Jinja2 + jinja2.compiler: Jinja2 + jinja2.constants: Jinja2 + jinja2.debug: Jinja2 + jinja2.defaults: Jinja2 + jinja2.environment: Jinja2 + jinja2.exceptions: Jinja2 + jinja2.ext: Jinja2 + jinja2.filters: Jinja2 + jinja2.idtracking: Jinja2 + jinja2.lexer: Jinja2 + jinja2.loaders: Jinja2 + jinja2.meta: Jinja2 + jinja2.nativetypes: Jinja2 + jinja2.nodes: Jinja2 + jinja2.optimizer: Jinja2 + jinja2.parser: Jinja2 + jinja2.runtime: Jinja2 + jinja2.sandbox: Jinja2 + jinja2.tests: Jinja2 + jinja2.utils: Jinja2 + jinja2.visitor: Jinja2 + lazy_object_proxy: lazy_object_proxy + lazy_object_proxy.cext: lazy_object_proxy + lazy_object_proxy.compat: lazy_object_proxy + lazy_object_proxy.simple: lazy_object_proxy + lazy_object_proxy.slots: lazy_object_proxy + lazy_object_proxy.utils: lazy_object_proxy + markupsafe: MarkupSafe + mccabe: mccabe + multidict: multidict + multipart: python_multipart + multipart.decoders: python_multipart + multipart.exceptions: python_multipart + multipart.multipart: python_multipart + multipart.tests: python_multipart + multipart.tests.compat: python_multipart + multipart.tests.test_multipart: python_multipart + mypy_extensions: mypy_extensions + openapi_python_client: openapi_python_client + openapi_python_client.cli: openapi_python_client + openapi_python_client.config: openapi_python_client + openapi_python_client.parser: openapi_python_client + openapi_python_client.parser.errors: openapi_python_client + openapi_python_client.parser.openapi: openapi_python_client + openapi_python_client.parser.properties: openapi_python_client + openapi_python_client.parser.properties.converter: openapi_python_client + openapi_python_client.parser.properties.enum_property: openapi_python_client + openapi_python_client.parser.properties.model_property: openapi_python_client + openapi_python_client.parser.properties.property: openapi_python_client + openapi_python_client.parser.properties.schemas: openapi_python_client + openapi_python_client.parser.responses: openapi_python_client + openapi_python_client.schema: openapi_python_client + openapi_python_client.schema.data_type: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.callback: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.components: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.contact: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.discriminator: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.encoding: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.example: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.external_documentation: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.header: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.info: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.license: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.link: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.media_type: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.oauth_flow: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.oauth_flows: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.open_api: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.operation: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.parameter: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.path_item: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.paths: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.reference: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.request_body: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.response: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.responses: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.schema: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.security_requirement: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.security_scheme: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.server: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.server_variable: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.tag: openapi_python_client + openapi_python_client.schema.openapi_schema_pydantic.xml: openapi_python_client + openapi_python_client.schema.parameter_location: openapi_python_client + openapi_python_client.utils: openapi_python_client + passlib: passlib + passlib.apache: passlib + passlib.apps: passlib + passlib.context: passlib + passlib.crypto: passlib + passlib.crypto.des: passlib + passlib.crypto.digest: passlib + passlib.crypto.scrypt: passlib + passlib.exc: passlib + passlib.ext: passlib + passlib.ext.django: passlib + passlib.ext.django.models: passlib + passlib.ext.django.utils: passlib + passlib.handlers: passlib + passlib.handlers.argon2: passlib + passlib.handlers.bcrypt: passlib + passlib.handlers.cisco: passlib + passlib.handlers.des_crypt: passlib + passlib.handlers.digests: passlib + passlib.handlers.django: passlib + passlib.handlers.fshp: passlib + passlib.handlers.ldap_digests: passlib + passlib.handlers.md5_crypt: passlib + passlib.handlers.misc: passlib + passlib.handlers.mssql: passlib + passlib.handlers.mysql: passlib + passlib.handlers.oracle: passlib + passlib.handlers.pbkdf2: passlib + passlib.handlers.phpass: passlib + passlib.handlers.postgres: passlib + passlib.handlers.roundup: passlib + passlib.handlers.scram: passlib + passlib.handlers.scrypt: passlib + passlib.handlers.sha1_crypt: passlib + passlib.handlers.sha2_crypt: passlib + passlib.handlers.sun_md5_crypt: passlib + passlib.handlers.windows: passlib + passlib.hash: passlib + passlib.hosts: passlib + passlib.ifc: passlib + passlib.pwd: passlib + passlib.registry: passlib + passlib.tests: passlib + passlib.tests.backports: passlib + passlib.tests.test_apache: passlib + passlib.tests.test_apps: passlib + passlib.tests.test_context: passlib + passlib.tests.test_context_deprecated: passlib + passlib.tests.test_crypto_builtin_md4: passlib + passlib.tests.test_crypto_des: passlib + passlib.tests.test_crypto_digest: passlib + passlib.tests.test_crypto_scrypt: passlib + passlib.tests.test_ext_django: passlib + passlib.tests.test_ext_django_source: passlib + passlib.tests.test_handlers: passlib + passlib.tests.test_handlers_argon2: passlib + passlib.tests.test_handlers_bcrypt: passlib + passlib.tests.test_handlers_cisco: passlib + passlib.tests.test_handlers_django: passlib + passlib.tests.test_handlers_pbkdf2: passlib + passlib.tests.test_handlers_scrypt: passlib + passlib.tests.test_hosts: passlib + passlib.tests.test_pwd: passlib + passlib.tests.test_registry: passlib + passlib.tests.test_totp: passlib + passlib.tests.test_utils: passlib + passlib.tests.test_utils_handlers: passlib + passlib.tests.test_utils_md4: passlib + passlib.tests.test_utils_pbkdf2: passlib + passlib.tests.test_win32: passlib + passlib.tests.tox_support: passlib + passlib.tests.utils: passlib + passlib.totp: passlib + passlib.utils: passlib + passlib.utils.binary: passlib + passlib.utils.compat: passlib + passlib.utils.decor: passlib + passlib.utils.des: passlib + passlib.utils.handlers: passlib + passlib.utils.md4: passlib + passlib.utils.pbkdf2: passlib + passlib.win32: passlib + pathspec: pathspec + pathspec.gitignore: pathspec + pathspec.pathspec: pathspec + pathspec.pattern: pathspec + pathspec.patterns: pathspec + pathspec.patterns.gitwildmatch: pathspec + pathspec.util: pathspec + pkg_resources: setuptools + pkg_resources.extern: setuptools + pkg_resources.tests.data.my-test-package-source.setup: setuptools + platformdirs: platformdirs + platformdirs.android: platformdirs + platformdirs.api: platformdirs + platformdirs.macos: platformdirs + platformdirs.unix: platformdirs + platformdirs.version: platformdirs + platformdirs.windows: platformdirs + priority: priority + priority.priority: priority + pydantic: pydantic + pydantic.annotated_types: pydantic + pydantic.class_validators: pydantic + pydantic.color: pydantic + pydantic.config: pydantic + pydantic.dataclasses: pydantic + pydantic.datetime_parse: pydantic + pydantic.decorator: pydantic + pydantic.env_settings: pydantic + pydantic.error_wrappers: pydantic + pydantic.errors: pydantic + pydantic.fields: pydantic + pydantic.generics: pydantic + pydantic.json: pydantic + pydantic.main: pydantic + pydantic.mypy: pydantic + pydantic.networks: pydantic + pydantic.parse: pydantic + pydantic.schema: pydantic + pydantic.tools: pydantic + pydantic.types: pydantic + pydantic.typing: pydantic + pydantic.utils: pydantic + pydantic.validators: pydantic + pydantic.version: pydantic + pyflakes: pyflakes + pyflakes.api: pyflakes + pyflakes.checker: pyflakes + pyflakes.messages: pyflakes + pyflakes.reporter: pyflakes + pyflakes.scripts: pyflakes + pyflakes.scripts.pyflakes: pyflakes + pyflakes.test: pyflakes + pyflakes.test.harness: pyflakes + pyflakes.test.test_api: pyflakes + pyflakes.test.test_builtin: pyflakes + pyflakes.test.test_code_segment: pyflakes + pyflakes.test.test_dict: pyflakes + pyflakes.test.test_doctests: pyflakes + pyflakes.test.test_imports: pyflakes + pyflakes.test.test_is_literal: pyflakes + pyflakes.test.test_match: pyflakes + pyflakes.test.test_other: pyflakes + pyflakes.test.test_type_annotations: pyflakes + pyflakes.test.test_undefined_names: pyflakes + pylint: pylint + pylint.checkers: pylint + pylint.checkers.async: pylint + pylint.checkers.base: pylint + pylint.checkers.base.basic_checker: pylint + pylint.checkers.base.basic_error_checker: pylint + pylint.checkers.base.comparison_checker: pylint + pylint.checkers.base.docstring_checker: pylint + pylint.checkers.base.name_checker: pylint + pylint.checkers.base.name_checker.checker: pylint + pylint.checkers.base.name_checker.naming_style: pylint + pylint.checkers.base.pass_checker: pylint + pylint.checkers.base_checker: pylint + pylint.checkers.classes: pylint + pylint.checkers.classes.class_checker: pylint + pylint.checkers.classes.special_methods_checker: pylint + pylint.checkers.deprecated: pylint + pylint.checkers.design_analysis: pylint + pylint.checkers.dunder_methods: pylint + pylint.checkers.ellipsis_checker: pylint + pylint.checkers.exceptions: pylint + pylint.checkers.format: pylint + pylint.checkers.imports: pylint + pylint.checkers.lambda_expressions: pylint + pylint.checkers.logging: pylint + pylint.checkers.mapreduce_checker: pylint + pylint.checkers.method_args: pylint + pylint.checkers.misc: pylint + pylint.checkers.modified_iterating_checker: pylint + pylint.checkers.newstyle: pylint + pylint.checkers.non_ascii_names: pylint + pylint.checkers.raw_metrics: pylint + pylint.checkers.refactoring: pylint + pylint.checkers.refactoring.implicit_booleaness_checker: pylint + pylint.checkers.refactoring.not_checker: pylint + pylint.checkers.refactoring.recommendation_checker: pylint + pylint.checkers.refactoring.refactoring_checker: pylint + pylint.checkers.similar: pylint + pylint.checkers.spelling: pylint + pylint.checkers.stdlib: pylint + pylint.checkers.strings: pylint + pylint.checkers.threading_checker: pylint + pylint.checkers.typecheck: pylint + pylint.checkers.unicode: pylint + pylint.checkers.unsupported_version: pylint + pylint.checkers.utils: pylint + pylint.checkers.variables: pylint + pylint.config: pylint + pylint.config.argument: pylint + pylint.config.arguments_manager: pylint + pylint.config.arguments_provider: pylint + pylint.config.callback_actions: pylint + pylint.config.config_file_parser: pylint + pylint.config.config_initialization: pylint + pylint.config.configuration_mixin: pylint + pylint.config.deprecation_actions: pylint + pylint.config.environment_variable: pylint + pylint.config.exceptions: pylint + pylint.config.find_default_config_files: pylint + pylint.config.help_formatter: pylint + pylint.config.option: pylint + pylint.config.option_manager_mixin: pylint + pylint.config.option_parser: pylint + pylint.config.options_provider_mixin: pylint + pylint.config.utils: pylint + pylint.constants: pylint + pylint.epylint: pylint + pylint.exceptions: pylint + pylint.extensions: pylint + pylint.extensions.bad_builtin: pylint + pylint.extensions.broad_try_clause: pylint + pylint.extensions.check_elif: pylint + pylint.extensions.code_style: pylint + pylint.extensions.comparetozero: pylint + pylint.extensions.comparison_placement: pylint + pylint.extensions.confusing_elif: pylint + pylint.extensions.consider_ternary_expression: pylint + pylint.extensions.docparams: pylint + pylint.extensions.docstyle: pylint + pylint.extensions.empty_comment: pylint + pylint.extensions.emptystring: pylint + pylint.extensions.eq_without_hash: pylint + pylint.extensions.for_any_all: pylint + pylint.extensions.mccabe: pylint + pylint.extensions.no_self_use: pylint + pylint.extensions.overlapping_exceptions: pylint + pylint.extensions.private_import: pylint + pylint.extensions.redefined_loop_name: pylint + pylint.extensions.redefined_variable_type: pylint + pylint.extensions.set_membership: pylint + pylint.extensions.typing: pylint + pylint.extensions.while_used: pylint + pylint.graph: pylint + pylint.interfaces: pylint + pylint.lint: pylint + pylint.lint.base_options: pylint + pylint.lint.caching: pylint + pylint.lint.expand_modules: pylint + pylint.lint.message_state_handler: pylint + pylint.lint.parallel: pylint + pylint.lint.pylinter: pylint + pylint.lint.report_functions: pylint + pylint.lint.run: pylint + pylint.lint.utils: pylint + pylint.message: pylint + pylint.message.message: pylint + pylint.message.message_definition: pylint + pylint.message.message_definition_store: pylint + pylint.message.message_id_store: pylint + pylint.pyreverse: pylint + pylint.pyreverse.diadefslib: pylint + pylint.pyreverse.diagrams: pylint + pylint.pyreverse.dot_printer: pylint + pylint.pyreverse.inspector: pylint + pylint.pyreverse.main: pylint + pylint.pyreverse.mermaidjs_printer: pylint + pylint.pyreverse.plantuml_printer: pylint + pylint.pyreverse.printer: pylint + pylint.pyreverse.printer_factory: pylint + pylint.pyreverse.utils: pylint + pylint.pyreverse.vcg_printer: pylint + pylint.pyreverse.writer: pylint + pylint.reporters: pylint + pylint.reporters.base_reporter: pylint + pylint.reporters.collecting_reporter: pylint + pylint.reporters.json_reporter: pylint + pylint.reporters.multi_reporter: pylint + pylint.reporters.reports_handler_mix_in: pylint + pylint.reporters.text: pylint + pylint.reporters.ureports: pylint + pylint.reporters.ureports.base_writer: pylint + pylint.reporters.ureports.nodes: pylint + pylint.reporters.ureports.text_writer: pylint + pylint.testutils: pylint + pylint.testutils.checker_test_case: pylint + pylint.testutils.configuration_test: pylint + pylint.testutils.constants: pylint + pylint.testutils.decorator: pylint + pylint.testutils.functional: pylint + pylint.testutils.functional.find_functional_tests: pylint + pylint.testutils.functional.lint_module_output_update: pylint + pylint.testutils.functional.test_file: pylint + pylint.testutils.functional_test_file: pylint + pylint.testutils.get_test_info: pylint + pylint.testutils.global_test_linter: pylint + pylint.testutils.lint_module_test: pylint + pylint.testutils.output_line: pylint + pylint.testutils.pyreverse: pylint + pylint.testutils.reporter_for_tests: pylint + pylint.testutils.tokenize_str: pylint + pylint.testutils.unittest_linter: pylint + pylint.testutils.utils: pylint + pylint.typing: pylint + pylint.utils: pylint + pylint.utils.ast_walker: pylint + pylint.utils.docs: pylint + pylint.utils.file_state: pylint + pylint.utils.linterstats: pylint + pylint.utils.pragma_parser: pylint + pylint.utils.utils: pylint + pypika: pypika_tortoise + pypika.analytics: pypika_tortoise + pypika.dialects: pypika_tortoise + pypika.dialects.mssql: pypika_tortoise + pypika.dialects.mysql: pypika_tortoise + pypika.dialects.oracle: pypika_tortoise + pypika.dialects.postgresql: pypika_tortoise + pypika.dialects.sqlite: pypika_tortoise + pypika.enums: pypika_tortoise + pypika.functions: pypika_tortoise + pypika.pseudocolumns: pypika_tortoise + pypika.queries: pypika_tortoise + pypika.terms: pypika_tortoise + pypika.utils: pypika_tortoise + pytz: pytz + pytz.exceptions: pytz + pytz.lazy: pytz + pytz.reference: pytz + pytz.tzfile: pytz + pytz.tzinfo: pytz + rfc3986: rfc3986 + rfc3986.abnf_regexp: rfc3986 + rfc3986.api: rfc3986 + rfc3986.builder: rfc3986 + rfc3986.compat: rfc3986 + rfc3986.exceptions: rfc3986 + rfc3986.iri: rfc3986 + rfc3986.misc: rfc3986 + rfc3986.normalizers: rfc3986 + rfc3986.parseresult: rfc3986 + rfc3986.uri: rfc3986 + rfc3986.validators: rfc3986 + setuptools: setuptools + setuptools.archive_util: setuptools + setuptools.build_meta: setuptools + setuptools.command: setuptools + setuptools.command.alias: setuptools + setuptools.command.bdist_egg: setuptools + setuptools.command.bdist_rpm: setuptools + setuptools.command.build_clib: setuptools + setuptools.command.build_ext: setuptools + setuptools.command.build_py: setuptools + setuptools.command.develop: setuptools + setuptools.command.dist_info: setuptools + setuptools.command.easy_install: setuptools + setuptools.command.egg_info: setuptools + setuptools.command.install: setuptools + setuptools.command.install_egg_info: setuptools + setuptools.command.install_lib: setuptools + setuptools.command.install_scripts: setuptools + setuptools.command.py36compat: setuptools + setuptools.command.register: setuptools + setuptools.command.rotate: setuptools + setuptools.command.saveopts: setuptools + setuptools.command.sdist: setuptools + setuptools.command.setopt: setuptools + setuptools.command.test: setuptools + setuptools.command.upload: setuptools + setuptools.command.upload_docs: setuptools + setuptools.config: setuptools + setuptools.dep_util: setuptools + setuptools.depends: setuptools + setuptools.dist: setuptools + setuptools.errors: setuptools + setuptools.extension: setuptools + setuptools.extern: setuptools + setuptools.glob: setuptools + setuptools.installer: setuptools + setuptools.launch: setuptools + setuptools.monkey: setuptools + setuptools.msvc: setuptools + setuptools.namespaces: setuptools + setuptools.package_index: setuptools + setuptools.py34compat: setuptools + setuptools.sandbox: setuptools + setuptools.unicode_utils: setuptools + setuptools.version: setuptools + setuptools.wheel: setuptools + setuptools.windows_support: setuptools + shellingham: shellingham + shellingham.nt: shellingham + shellingham.posix: shellingham + shellingham.posix.proc: shellingham + shellingham.posix.ps: shellingham + six: six + sniffio: sniffio + soupsieve: soupsieve + soupsieve.css_match: soupsieve + soupsieve.css_parser: soupsieve + soupsieve.css_types: soupsieve + soupsieve.pretty: soupsieve + soupsieve.util: soupsieve + starlette: starlette + starlette.applications: starlette + starlette.authentication: starlette + starlette.background: starlette + starlette.concurrency: starlette + starlette.config: starlette + starlette.convertors: starlette + starlette.datastructures: starlette + starlette.endpoints: starlette + starlette.exceptions: starlette + starlette.formparsers: starlette + starlette.middleware: starlette + starlette.middleware.authentication: starlette + starlette.middleware.base: starlette + starlette.middleware.cors: starlette + starlette.middleware.errors: starlette + starlette.middleware.exceptions: starlette + starlette.middleware.gzip: starlette + starlette.middleware.httpsredirect: starlette + starlette.middleware.sessions: starlette + starlette.middleware.trustedhost: starlette + starlette.middleware.wsgi: starlette + starlette.requests: starlette + starlette.responses: starlette + starlette.routing: starlette + starlette.schemas: starlette + starlette.staticfiles: starlette + starlette.status: starlette + starlette.templating: starlette + starlette.testclient: starlette + starlette.types: starlette + starlette.websockets: starlette + temporalio: temporalio + temporalio.activity: temporalio + temporalio.api: temporalio + temporalio.api.batch: temporalio + temporalio.api.batch.v1: temporalio + temporalio.api.batch.v1.message_pb2: temporalio + temporalio.api.cluster: temporalio + temporalio.api.cluster.v1: temporalio + temporalio.api.cluster.v1.message_pb2: temporalio + temporalio.api.cluster.v1.message_pb2_grpc: temporalio + temporalio.api.command: temporalio + temporalio.api.command.v1: temporalio + temporalio.api.command.v1.message_pb2: temporalio + temporalio.api.common: temporalio + temporalio.api.common.v1: temporalio + temporalio.api.common.v1.grpc_status_pb2: temporalio + temporalio.api.common.v1.message_pb2: temporalio + temporalio.api.dependencies: temporalio + temporalio.api.dependencies.gogoproto: temporalio + temporalio.api.dependencies.gogoproto.gogo_pb2: temporalio + temporalio.api.enums: temporalio + temporalio.api.enums.v1: temporalio + temporalio.api.enums.v1.batch_operation_pb2: temporalio + temporalio.api.enums.v1.command_type_pb2: temporalio + temporalio.api.enums.v1.common_pb2: temporalio + temporalio.api.enums.v1.event_type_pb2: temporalio + temporalio.api.enums.v1.failed_cause_pb2: temporalio + temporalio.api.enums.v1.interaction_type_pb2: temporalio + temporalio.api.enums.v1.namespace_pb2: temporalio + temporalio.api.enums.v1.query_pb2: temporalio + temporalio.api.enums.v1.reset_pb2: temporalio + temporalio.api.enums.v1.schedule_pb2: temporalio + temporalio.api.enums.v1.task_queue_pb2: temporalio + temporalio.api.enums.v1.update_pb2: temporalio + temporalio.api.enums.v1.workflow_pb2: temporalio + temporalio.api.errordetails: temporalio + temporalio.api.errordetails.v1: temporalio + temporalio.api.errordetails.v1.message_pb2: temporalio + temporalio.api.failure: temporalio + temporalio.api.failure.v1: temporalio + temporalio.api.failure.v1.message_pb2: temporalio + temporalio.api.filter: temporalio + temporalio.api.filter.v1: temporalio + temporalio.api.filter.v1.message_pb2: temporalio + temporalio.api.history: temporalio + temporalio.api.history.v1: temporalio + temporalio.api.history.v1.message_pb2: temporalio + temporalio.api.interaction: temporalio + temporalio.api.interaction.v1: temporalio + temporalio.api.interaction.v1.message_pb2: temporalio + temporalio.api.namespace: temporalio + temporalio.api.namespace.v1: temporalio + temporalio.api.namespace.v1.message_pb2: temporalio + temporalio.api.operatorservice: temporalio + temporalio.api.operatorservice.v1: temporalio + temporalio.api.operatorservice.v1.request_response_pb2: temporalio + temporalio.api.operatorservice.v1.request_response_pb2_grpc: temporalio + temporalio.api.operatorservice.v1.service_pb2: temporalio + temporalio.api.operatorservice.v1.service_pb2_grpc: temporalio + temporalio.api.query: temporalio + temporalio.api.query.v1: temporalio + temporalio.api.query.v1.message_pb2: temporalio + temporalio.api.replication: temporalio + temporalio.api.replication.v1: temporalio + temporalio.api.replication.v1.message_pb2: temporalio + temporalio.api.schedule: temporalio + temporalio.api.schedule.v1: temporalio + temporalio.api.schedule.v1.message_pb2: temporalio + temporalio.api.taskqueue: temporalio + temporalio.api.taskqueue.v1: temporalio + temporalio.api.taskqueue.v1.message_pb2: temporalio + temporalio.api.testservice: temporalio + temporalio.api.testservice.v1: temporalio + temporalio.api.testservice.v1.request_response_pb2: temporalio + temporalio.api.testservice.v1.request_response_pb2_grpc: temporalio + temporalio.api.testservice.v1.service_pb2: temporalio + temporalio.api.testservice.v1.service_pb2_grpc: temporalio + temporalio.api.update: temporalio + temporalio.api.update.v1: temporalio + temporalio.api.update.v1.message_pb2: temporalio + temporalio.api.version: temporalio + temporalio.api.version.v1: temporalio + temporalio.api.version.v1.message_pb2: temporalio + temporalio.api.workflow: temporalio + temporalio.api.workflow.v1: temporalio + temporalio.api.workflow.v1.message_pb2: temporalio + temporalio.api.workflowservice: temporalio + temporalio.api.workflowservice.v1: temporalio + temporalio.api.workflowservice.v1.request_response_pb2: temporalio + temporalio.api.workflowservice.v1.request_response_pb2_grpc: temporalio + temporalio.api.workflowservice.v1.service_pb2: temporalio + temporalio.api.workflowservice.v1.service_pb2_grpc: temporalio + temporalio.bridge: temporalio + temporalio.bridge.client: temporalio + temporalio.bridge.proto: temporalio + temporalio.bridge.proto.activity_result: temporalio + temporalio.bridge.proto.activity_result.activity_result_pb2: temporalio + temporalio.bridge.proto.activity_task: temporalio + temporalio.bridge.proto.activity_task.activity_task_pb2: temporalio + temporalio.bridge.proto.bridge: temporalio + temporalio.bridge.proto.bridge.bridge_pb2: temporalio + temporalio.bridge.proto.child_workflow: temporalio + temporalio.bridge.proto.child_workflow.child_workflow_pb2: temporalio + temporalio.bridge.proto.common: temporalio + temporalio.bridge.proto.common.common_pb2: temporalio + temporalio.bridge.proto.core_interface_pb2: temporalio + temporalio.bridge.proto.core_interface_pb2_grpc: temporalio + temporalio.bridge.proto.external_data: temporalio + temporalio.bridge.proto.external_data.external_data_pb2: temporalio + temporalio.bridge.proto.health: temporalio + temporalio.bridge.proto.health.v1: temporalio + temporalio.bridge.proto.health.v1.health_pb2: temporalio + temporalio.bridge.proto.workflow_activation: temporalio + temporalio.bridge.proto.workflow_activation.workflow_activation_pb2: temporalio + temporalio.bridge.proto.workflow_commands: temporalio + temporalio.bridge.proto.workflow_commands.workflow_commands_pb2: temporalio + temporalio.bridge.proto.workflow_completion: temporalio + temporalio.bridge.proto.workflow_completion.workflow_completion_pb2: temporalio + temporalio.bridge.runtime: temporalio + temporalio.bridge.temporal_sdk_bridge: temporalio + temporalio.bridge.testing: temporalio + temporalio.bridge.worker: temporalio + temporalio.client: temporalio + temporalio.common: temporalio + temporalio.contrib: temporalio + temporalio.contrib.opentelemetry: temporalio + temporalio.converter: temporalio + temporalio.exceptions: temporalio + temporalio.runtime: temporalio + temporalio.service: temporalio + temporalio.testing: temporalio + temporalio.types: temporalio + temporalio.worker: temporalio + temporalio.worker.workflow_sandbox: temporalio + temporalio.workflow: temporalio + test_autoflake: autoflake + toml: toml + toml.decoder: toml + toml.encoder: toml + toml.ordered: toml + toml.tz: toml + tomli: tomli + tomlkit: tomlkit + tomlkit.api: tomlkit + tomlkit.container: tomlkit + tomlkit.exceptions: tomlkit + tomlkit.items: tomlkit + tomlkit.parser: tomlkit + tomlkit.source: tomlkit + tomlkit.toml_char: tomlkit + tomlkit.toml_document: tomlkit + tomlkit.toml_file: tomlkit + tortoise: tortoise_orm + tortoise.backends: tortoise_orm + tortoise.backends.asyncpg: tortoise_orm + tortoise.backends.asyncpg.client: tortoise_orm + tortoise.backends.asyncpg.executor: tortoise_orm + tortoise.backends.asyncpg.schema_generator: tortoise_orm + tortoise.backends.base: tortoise_orm + tortoise.backends.base.client: tortoise_orm + tortoise.backends.base.config_generator: tortoise_orm + tortoise.backends.base.executor: tortoise_orm + tortoise.backends.base.schema_generator: tortoise_orm + tortoise.backends.base_postgres: tortoise_orm + tortoise.backends.base_postgres.client: tortoise_orm + tortoise.backends.base_postgres.executor: tortoise_orm + tortoise.backends.base_postgres.schema_generator: tortoise_orm + tortoise.backends.mssql: tortoise_orm + tortoise.backends.mssql.client: tortoise_orm + tortoise.backends.mssql.executor: tortoise_orm + tortoise.backends.mssql.schema_generator: tortoise_orm + tortoise.backends.mysql: tortoise_orm + tortoise.backends.mysql.client: tortoise_orm + tortoise.backends.mysql.executor: tortoise_orm + tortoise.backends.mysql.schema_generator: tortoise_orm + tortoise.backends.odbc: tortoise_orm + tortoise.backends.odbc.client: tortoise_orm + tortoise.backends.odbc.executor: tortoise_orm + tortoise.backends.oracle: tortoise_orm + tortoise.backends.oracle.client: tortoise_orm + tortoise.backends.oracle.executor: tortoise_orm + tortoise.backends.oracle.schema_generator: tortoise_orm + tortoise.backends.psycopg: tortoise_orm + tortoise.backends.psycopg.client: tortoise_orm + tortoise.backends.psycopg.executor: tortoise_orm + tortoise.backends.psycopg.schema_generator: tortoise_orm + tortoise.backends.sqlite: tortoise_orm + tortoise.backends.sqlite.client: tortoise_orm + tortoise.backends.sqlite.executor: tortoise_orm + tortoise.backends.sqlite.schema_generator: tortoise_orm + tortoise.connection: tortoise_orm + tortoise.contrib: tortoise_orm + tortoise.contrib.aiohttp: tortoise_orm + tortoise.contrib.blacksheep: tortoise_orm + tortoise.contrib.fastapi: tortoise_orm + tortoise.contrib.mysql: tortoise_orm + tortoise.contrib.mysql.fields: tortoise_orm + tortoise.contrib.mysql.functions: tortoise_orm + tortoise.contrib.mysql.indexes: tortoise_orm + tortoise.contrib.mysql.json_functions: tortoise_orm + tortoise.contrib.mysql.search: tortoise_orm + tortoise.contrib.postgres: tortoise_orm + tortoise.contrib.postgres.fields: tortoise_orm + tortoise.contrib.postgres.functions: tortoise_orm + tortoise.contrib.postgres.indexes: tortoise_orm + tortoise.contrib.postgres.json_functions: tortoise_orm + tortoise.contrib.postgres.search: tortoise_orm + tortoise.contrib.pydantic: tortoise_orm + tortoise.contrib.pydantic.base: tortoise_orm + tortoise.contrib.pydantic.creator: tortoise_orm + tortoise.contrib.pydantic.utils: tortoise_orm + tortoise.contrib.pylint: tortoise_orm + tortoise.contrib.quart: tortoise_orm + tortoise.contrib.sanic: tortoise_orm + tortoise.contrib.sqlite: tortoise_orm + tortoise.contrib.sqlite.functions: tortoise_orm + tortoise.contrib.starlette: tortoise_orm + tortoise.contrib.test: tortoise_orm + tortoise.contrib.test.condition: tortoise_orm + tortoise.converters: tortoise_orm + tortoise.exceptions: tortoise_orm + tortoise.expressions: tortoise_orm + tortoise.fields: tortoise_orm + tortoise.fields.base: tortoise_orm + tortoise.fields.data: tortoise_orm + tortoise.fields.relational: tortoise_orm + tortoise.filters: tortoise_orm + tortoise.functions: tortoise_orm + tortoise.indexes: tortoise_orm + tortoise.log: tortoise_orm + tortoise.manager: tortoise_orm + tortoise.models: tortoise_orm + tortoise.query_utils: tortoise_orm + tortoise.queryset: tortoise_orm + tortoise.router: tortoise_orm + tortoise.signals: tortoise_orm + tortoise.timezone: tortoise_orm + tortoise.transactions: tortoise_orm + tortoise.utils: tortoise_orm + tortoise.validators: tortoise_orm + typer: typer + typer.colors: typer + typer.completion: typer + typer.core: typer + typer.main: typer + typer.models: typer + typer.params: typer + typer.rich_utils: typer + typer.testing: typer + typer.utils: typer + typing_extensions: typing_extensions + wrapt: wrapt + wrapt.arguments: wrapt + wrapt.decorators: wrapt + wrapt.importer: wrapt + wrapt.wrappers: wrapt + wsproto: wsproto + wsproto.connection: wsproto + wsproto.events: wsproto + wsproto.extensions: wsproto + wsproto.frame_protocol: wsproto + wsproto.handshake: wsproto + wsproto.typing: wsproto + wsproto.utilities: wsproto + yaml: PyYAML + yaml.composer: PyYAML + yaml.constructor: PyYAML + yaml.cyaml: PyYAML + yaml.dumper: PyYAML + yaml.emitter: PyYAML + yaml.error: PyYAML + yaml.events: PyYAML + yaml.loader: PyYAML + yaml.nodes: PyYAML + yaml.parser: PyYAML + yaml.reader: PyYAML + yaml.representer: PyYAML + yaml.resolver: PyYAML + yaml.scanner: PyYAML + yaml.serializer: PyYAML + yaml.tokens: PyYAML + yapf: yapf + yapf.third_party: yapf + yapf.third_party.yapf_diff: yapf + yapf.third_party.yapf_diff.yapf_diff: yapf + yapf.yapflib: yapf + yapf.yapflib.blank_line_calculator: yapf + yapf.yapflib.comment_splicer: yapf + yapf.yapflib.continuation_splicer: yapf + yapf.yapflib.errors: yapf + yapf.yapflib.file_resources: yapf + yapf.yapflib.format_decision_state: yapf + yapf.yapflib.format_token: yapf + yapf.yapflib.identify_container: yapf + yapf.yapflib.line_joiner: yapf + yapf.yapflib.logical_line: yapf + yapf.yapflib.object_state: yapf + yapf.yapflib.py3compat: yapf + yapf.yapflib.pytree_unwrapper: yapf + yapf.yapflib.pytree_utils: yapf + yapf.yapflib.pytree_visitor: yapf + yapf.yapflib.reformatter: yapf + yapf.yapflib.split_penalty: yapf + yapf.yapflib.style: yapf + yapf.yapflib.subtype_assigner: yapf + yapf.yapflib.subtypes: yapf + yapf.yapflib.verifier: yapf + yapf.yapflib.yapf_api: yapf + yapftests: yapf + yapftests.blank_line_calculator_test: yapf + yapftests.comment_splicer_test: yapf + yapftests.file_resources_test: yapf + yapftests.format_decision_state_test: yapf + yapftests.format_token_test: yapf + yapftests.line_joiner_test: yapf + yapftests.logical_line_test: yapf + yapftests.main_test: yapf + yapftests.pytree_unwrapper_test: yapf + yapftests.pytree_utils_test: yapf + yapftests.pytree_visitor_test: yapf + yapftests.reformatter_basic_test: yapf + yapftests.reformatter_buganizer_test: yapf + yapftests.reformatter_facebook_test: yapf + yapftests.reformatter_pep8_test: yapf + yapftests.reformatter_python3_test: yapf + yapftests.reformatter_style_config_test: yapf + yapftests.reformatter_verify_test: yapf + yapftests.split_penalty_test: yapf + yapftests.style_test: yapf + yapftests.subtype_assigner_test: yapf + yapftests.utils: yapf + yapftests.yapf_test: yapf + yapftests.yapf_test_helper: yapf + yarl: yarl + pip_repository: + name: pypi +integrity: 6adcf30189668fa8123faef9b05219d91bf45a1950bc648c64a3c84e829b117d diff --git a/package.json b/package.json new file mode 100644 index 0000000..5429c3f --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "distro-tools", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "BSD-3-Clause", + "dependencies": { + "@carbon/themes": "^11.16.0", + "@carbon/web-components": "^1.23.0", + "carbon-components": "^10.58.3", + "esbuild-sass-plugin": "^2.4.5", + "lit-element": "^3.2.2", + "lit-html": "^2.6.1" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..6a1dd1c --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,587 @@ +lockfileVersion: 5.4 + +specifiers: + '@carbon/themes': ^11.16.0 + '@carbon/web-components': ^1.23.0 + carbon-components: ^10.58.3 + esbuild-sass-plugin: ^2.4.5 + lit-element: ^3.2.2 + lit-html: ^2.6.1 + +dependencies: + '@carbon/themes': 11.16.0 + '@carbon/web-components': 1.23.0 + carbon-components: 10.58.3 + esbuild-sass-plugin: 2.4.5 + lit-element: 3.2.2 + lit-html: 2.6.1 + +packages: + + /@babel/runtime/7.20.13: + resolution: {integrity: sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.11 + dev: false + + /@carbon/colors/11.12.0: + resolution: {integrity: sha512-rN/e6EXgS1RM8C/K0qQ/4/zE+En8iMGDE9u6BNLQ9ig2V2KTYncCdhItJW0rAbL7tt8xeP0UrACijS/XCqsm6w==} + dev: false + + /@carbon/grid/11.11.0: + resolution: {integrity: sha512-07U2cC5lgvy7gWveUtE0wuYUxu6Dndglnajy1FJAeV7BEd2t6cgU/t7AOaL8RhoXPjE7tarN5tnZImw5nmzQYQ==} + dependencies: + '@carbon/layout': 11.11.0 + dev: false + + /@carbon/layout/11.11.0: + resolution: {integrity: sha512-0AuBxoJ+4HiYDIou0xR/jRtGDjTZ2ew1qeViQd04aM0GdZDGhjPTwU9u9CLCKAZEK4dYcewqWat1FQI4diYaOA==} + dev: false + + /@carbon/telemetry/0.1.0: + resolution: {integrity: sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg==} + hasBin: true + dev: false + + /@carbon/themes/11.16.0: + resolution: {integrity: sha512-t5323D66p5M6ev9HwMo5tQUE5M1zQP+fa2KExOkHzyJXQfbR3rDSL3y8O/ALrnLgYfBKTftQCGOcUnxhR3LOTQ==} + dependencies: + '@carbon/colors': 11.12.0 + '@carbon/layout': 11.11.0 + '@carbon/type': 11.15.0 + color: 4.2.3 + dev: false + + /@carbon/type/11.15.0: + resolution: {integrity: sha512-oJm8imiLUF5x7AYaiuExhGfhF/aep8STvV4ckQ9VrPCVFwytJ1haJFmyDU2ssYNiTfgjVN0foy6sNbHMZYM0pg==} + dependencies: + '@carbon/grid': 11.11.0 + '@carbon/layout': 11.11.0 + dev: false + + /@carbon/web-components/1.23.0: + resolution: {integrity: sha512-dK28KsVS9pR9YEYCQ5t61imLf1DbcwdykCLcIQX7B1Ak8YZZHYdNEkkcmuiXpbn6Tfd/Rtn1i+5BiJmREN6kKQ==} + dependencies: + '@babel/runtime': 7.20.13 + carbon-components: 10.58.3 + flatpickr: 4.6.13 + lit-element: 2.5.1 + lit-html: 1.4.1 + lodash-es: 4.17.21 + dev: false + + /@esbuild/android-arm/0.15.18: + resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@esbuild/linux-loong64/0.15.18: + resolution: {integrity: sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@lit-labs/ssr-dom-shim/1.0.0: + resolution: {integrity: sha512-ic93MBXfApIFTrup4a70M/+ddD8xdt2zxxj9sRwHQzhS9ag/syqkD8JPdTXsc1gUy2K8TTirhlCqyTEM/sifNw==} + dev: false + + /@lit/reactive-element/1.6.1: + resolution: {integrity: sha512-va15kYZr7KZNNPZdxONGQzpUr+4sxVu7V/VG7a8mRfPPXUyhEYj5RzXCQmGrlP3tAh0L3HHm5AjBMFYRqlM9SA==} + dependencies: + '@lit-labs/ssr-dom-shim': 1.0.0 + dev: false + + /@types/trusted-types/2.0.2: + resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} + dev: false + + /anymatch/3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: false + + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: false + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: false + + /carbon-components/10.58.3: + resolution: {integrity: sha512-RjTnrWCGStsIZ7nErw97AZI9sQWxQ8oIgo3QMdV0FWFcpTOECA4I9Dy4WPpRRdSMBcQpLetTxqjDGourM4u8Tw==} + requiresBuild: true + dependencies: + '@carbon/telemetry': 0.1.0 + flatpickr: 4.6.1 + lodash.debounce: 4.0.8 + warning: 3.0.0 + dev: false + + /chokidar/3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: false + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: false + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: false + + /color-string/1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color/4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: false + + /esbuild-android-64/0.15.18: + resolution: {integrity: sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /esbuild-android-arm64/0.15.18: + resolution: {integrity: sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /esbuild-darwin-64/0.15.18: + resolution: {integrity: sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /esbuild-darwin-arm64/0.15.18: + resolution: {integrity: sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /esbuild-freebsd-64/0.15.18: + resolution: {integrity: sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /esbuild-freebsd-arm64/0.15.18: + resolution: {integrity: sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-32/0.15.18: + resolution: {integrity: sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-64/0.15.18: + resolution: {integrity: sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-arm/0.15.18: + resolution: {integrity: sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-arm64/0.15.18: + resolution: {integrity: sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-mips64le/0.15.18: + resolution: {integrity: sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-ppc64le/0.15.18: + resolution: {integrity: sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-riscv64/0.15.18: + resolution: {integrity: sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-linux-s390x/0.15.18: + resolution: {integrity: sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /esbuild-netbsd-64/0.15.18: + resolution: {integrity: sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: false + optional: true + + /esbuild-openbsd-64/0.15.18: + resolution: {integrity: sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: false + optional: true + + /esbuild-sass-plugin/2.4.5: + resolution: {integrity: sha512-di2hLaIwhRXe513uaPPxv+5bjynxAgrS8R+u38lbBfvp1g1xOki4ACXV2aXip2CRPGTbAVDySSxujd9iArFV0w==} + dependencies: + esbuild: 0.15.18 + resolve: 1.22.1 + sass: 1.57.1 + dev: false + + /esbuild-sunos-64/0.15.18: + resolution: {integrity: sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: false + optional: true + + /esbuild-windows-32/0.15.18: + resolution: {integrity: sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /esbuild-windows-64/0.15.18: + resolution: {integrity: sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /esbuild-windows-arm64/0.15.18: + resolution: {integrity: sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /esbuild/0.15.18: + resolution: {integrity: sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.15.18 + '@esbuild/linux-loong64': 0.15.18 + esbuild-android-64: 0.15.18 + esbuild-android-arm64: 0.15.18 + esbuild-darwin-64: 0.15.18 + esbuild-darwin-arm64: 0.15.18 + esbuild-freebsd-64: 0.15.18 + esbuild-freebsd-arm64: 0.15.18 + esbuild-linux-32: 0.15.18 + esbuild-linux-64: 0.15.18 + esbuild-linux-arm: 0.15.18 + esbuild-linux-arm64: 0.15.18 + esbuild-linux-mips64le: 0.15.18 + esbuild-linux-ppc64le: 0.15.18 + esbuild-linux-riscv64: 0.15.18 + esbuild-linux-s390x: 0.15.18 + esbuild-netbsd-64: 0.15.18 + esbuild-openbsd-64: 0.15.18 + esbuild-sunos-64: 0.15.18 + esbuild-windows-32: 0.15.18 + esbuild-windows-64: 0.15.18 + esbuild-windows-arm64: 0.15.18 + dev: false + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: false + + /flatpickr/4.6.1: + resolution: {integrity: sha512-3ULSxbXmcMIRzer/2jLNweoqHpwDvsjEawO2FUd9UFR8uPwLM+LruZcPDpuZStcEgbQKhuFOfXo4nYdGladSNw==} + dev: false + + /flatpickr/4.6.13: + resolution: {integrity: sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==} + dev: false + + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: false + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: false + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: false + + /immutable/4.2.2: + resolution: {integrity: sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==} + dev: false + + /is-arrayish/0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: false + + /is-core-module/2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + dependencies: + has: 1.0.3 + dev: false + + /is-extglob/2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: false + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: false + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: false + + /js-tokens/4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false + + /lit-element/2.5.1: + resolution: {integrity: sha512-ogu7PiJTA33bEK0xGu1dmaX5vhcRjBXCFexPja0e7P7jqLhTpNKYRPmE+GmiCaRVAbiQKGkUgkh/i6+bh++dPQ==} + dependencies: + lit-html: 1.4.1 + dev: false + + /lit-element/3.2.2: + resolution: {integrity: sha512-6ZgxBR9KNroqKb6+htkyBwD90XGRiqKDHVrW/Eh0EZ+l+iC+u+v+w3/BA5NGi4nizAVHGYvQBHUDuSmLjPp7NQ==} + dependencies: + '@lit/reactive-element': 1.6.1 + lit-html: 2.6.1 + dev: false + + /lit-html/1.4.1: + resolution: {integrity: sha512-B9btcSgPYb1q4oSOb/PrOT6Z/H+r6xuNzfH4lFli/AWhYwdtrgQkQWBbIc6mdnf6E2IL3gDXdkkqNktpU0OZQA==} + dev: false + + /lit-html/2.6.1: + resolution: {integrity: sha512-Z3iw+E+3KKFn9t2YKNjsXNEu/LRLI98mtH/C6lnFg7kvaqPIzPn124Yd4eT/43lyqrejpc5Wb6BHq3fdv4S8Rw==} + dependencies: + '@types/trusted-types': 2.0.2 + dev: false + + /lodash-es/4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + + /lodash.debounce/4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + dev: false + + /loose-envify/1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: false + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: false + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: false + + /picomatch/2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: false + + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: false + + /regenerator-runtime/0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + dev: false + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: false + + /sass/1.57.1: + resolution: {integrity: sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==} + engines: {node: '>=12.0.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + immutable: 4.2.2 + source-map-js: 1.0.2 + dev: false + + /simple-swizzle/0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + + /source-map-js/1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: false + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: false + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: false + + /warning/3.0.0: + resolution: {integrity: sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==} + dependencies: + loose-envify: 1.4.0 + dev: false diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6cc0ed4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,19 @@ +tortoise-orm[asyncpg]==0.19.2 +pylint==2.15.10 +yapf==0.32.0 +temporalio==1.0.0 +click==8.1.3 +aiohttp==3.8.3 +openapi-python-client==0.13.1 +dataclass-wizard==0.22.2 +fastapi==0.89.1 +fastapi-pagination==0.11.2 +Jinja2==3.1.2 +hypercorn==0.14.3 +setuptools==58.2.0 +pydantic==1.10.4 +passlib[bcrypt]==1.7.4 +python-multipart==0.0.5 +itsdangerous==2.1.2 +PyYAML==6.0 +beautifulsoup4==4.11.2 \ No newline at end of file diff --git a/requirements_lock.txt b/requirements_lock.txt new file mode 100644 index 0000000..d12064c --- /dev/null +++ b/requirements_lock.txt @@ -0,0 +1,932 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# bazel run //:requirements.update +# +aiohttp==3.8.3 \ + --hash=sha256:02f9a2c72fc95d59b881cf38a4b2be9381b9527f9d328771e90f72ac76f31ad8 \ + --hash=sha256:059a91e88f2c00fe40aed9031b3606c3f311414f86a90d696dd982e7aec48142 \ + --hash=sha256:05a3c31c6d7cd08c149e50dc7aa2568317f5844acd745621983380597f027a18 \ + --hash=sha256:08c78317e950e0762c2983f4dd58dc5e6c9ff75c8a0efeae299d363d439c8e34 \ + --hash=sha256:09e28f572b21642128ef31f4e8372adb6888846f32fecb288c8b0457597ba61a \ + --hash=sha256:0d2c6d8c6872df4a6ec37d2ede71eff62395b9e337b4e18efd2177de883a5033 \ + --hash=sha256:16c121ba0b1ec2b44b73e3a8a171c4f999b33929cd2397124a8c7fcfc8cd9e06 \ + --hash=sha256:1d90043c1882067f1bd26196d5d2db9aa6d268def3293ed5fb317e13c9413ea4 \ + --hash=sha256:1e56b9cafcd6531bab5d9b2e890bb4937f4165109fe98e2b98ef0dcfcb06ee9d \ + --hash=sha256:20acae4f268317bb975671e375493dbdbc67cddb5f6c71eebdb85b34444ac46b \ + --hash=sha256:21b30885a63c3f4ff5b77a5d6caf008b037cb521a5f33eab445dc566f6d092cc \ + --hash=sha256:21d69797eb951f155026651f7e9362877334508d39c2fc37bd04ff55b2007091 \ + --hash=sha256:256deb4b29fe5e47893fa32e1de2d73c3afe7407738bd3c63829874661d4822d \ + --hash=sha256:25892c92bee6d9449ffac82c2fe257f3a6f297792cdb18ad784737d61e7a9a85 \ + --hash=sha256:2ca9af5f8f5812d475c5259393f52d712f6d5f0d7fdad9acdb1107dd9e3cb7eb \ + --hash=sha256:2d252771fc85e0cf8da0b823157962d70639e63cb9b578b1dec9868dd1f4f937 \ + --hash=sha256:2dea10edfa1a54098703cb7acaa665c07b4e7568472a47f4e64e6319d3821ccf \ + --hash=sha256:2df5f139233060578d8c2c975128fb231a89ca0a462b35d4b5fcf7c501ebdbe1 \ + --hash=sha256:2feebbb6074cdbd1ac276dbd737b40e890a1361b3cc30b74ac2f5e24aab41f7b \ + --hash=sha256:309aa21c1d54b8ef0723181d430347d7452daaff93e8e2363db8e75c72c2fb2d \ + --hash=sha256:3828fb41b7203176b82fe5d699e0d845435f2374750a44b480ea6b930f6be269 \ + --hash=sha256:398701865e7a9565d49189f6c90868efaca21be65c725fc87fc305906be915da \ + --hash=sha256:43046a319664a04b146f81b40e1545d4c8ac7b7dd04c47e40bf09f65f2437346 \ + --hash=sha256:437399385f2abcd634865705bdc180c8314124b98299d54fe1d4c8990f2f9494 \ + --hash=sha256:45d88b016c849d74ebc6f2b6e8bc17cabf26e7e40c0661ddd8fae4c00f015697 \ + --hash=sha256:47841407cc89a4b80b0c52276f3cc8138bbbfba4b179ee3acbd7d77ae33f7ac4 \ + --hash=sha256:4a4fbc769ea9b6bd97f4ad0b430a6807f92f0e5eb020f1e42ece59f3ecfc4585 \ + --hash=sha256:4ab94426ddb1ecc6a0b601d832d5d9d421820989b8caa929114811369673235c \ + --hash=sha256:4b0f30372cef3fdc262f33d06e7b411cd59058ce9174ef159ad938c4a34a89da \ + --hash=sha256:4e3a23ec214e95c9fe85a58470b660efe6534b83e6cbe38b3ed52b053d7cb6ad \ + --hash=sha256:512bd5ab136b8dc0ffe3fdf2dfb0c4b4f49c8577f6cae55dca862cd37a4564e2 \ + --hash=sha256:527b3b87b24844ea7865284aabfab08eb0faf599b385b03c2aa91fc6edd6e4b6 \ + --hash=sha256:54d107c89a3ebcd13228278d68f1436d3f33f2dd2af5415e3feaeb1156e1a62c \ + --hash=sha256:5835f258ca9f7c455493a57ee707b76d2d9634d84d5d7f62e77be984ea80b849 \ + --hash=sha256:598adde339d2cf7d67beaccda3f2ce7c57b3b412702f29c946708f69cf8222aa \ + --hash=sha256:599418aaaf88a6d02a8c515e656f6faf3d10618d3dd95866eb4436520096c84b \ + --hash=sha256:5bf651afd22d5f0c4be16cf39d0482ea494f5c88f03e75e5fef3a85177fecdeb \ + --hash=sha256:5c59fcd80b9049b49acd29bd3598cada4afc8d8d69bd4160cd613246912535d7 \ + --hash=sha256:653acc3880459f82a65e27bd6526e47ddf19e643457d36a2250b85b41a564715 \ + --hash=sha256:66bd5f950344fb2b3dbdd421aaa4e84f4411a1a13fca3aeb2bcbe667f80c9f76 \ + --hash=sha256:6f3553510abdbec67c043ca85727396ceed1272eef029b050677046d3387be8d \ + --hash=sha256:7018ecc5fe97027214556afbc7c502fbd718d0740e87eb1217b17efd05b3d276 \ + --hash=sha256:713d22cd9643ba9025d33c4af43943c7a1eb8547729228de18d3e02e278472b6 \ + --hash=sha256:73a4131962e6d91109bca6536416aa067cf6c4efb871975df734f8d2fd821b37 \ + --hash=sha256:75880ed07be39beff1881d81e4a907cafb802f306efd6d2d15f2b3c69935f6fb \ + --hash=sha256:75e14eac916f024305db517e00a9252714fce0abcb10ad327fb6dcdc0d060f1d \ + --hash=sha256:8135fa153a20d82ffb64f70a1b5c2738684afa197839b34cc3e3c72fa88d302c \ + --hash=sha256:84b14f36e85295fe69c6b9789b51a0903b774046d5f7df538176516c3e422446 \ + --hash=sha256:86fc24e58ecb32aee09f864cb11bb91bc4c1086615001647dbfc4dc8c32f4008 \ + --hash=sha256:87f44875f2804bc0511a69ce44a9595d5944837a62caecc8490bbdb0e18b1342 \ + --hash=sha256:88c70ed9da9963d5496d38320160e8eb7e5f1886f9290475a881db12f351ab5d \ + --hash=sha256:88e5be56c231981428f4f506c68b6a46fa25c4123a2e86d156c58a8369d31ab7 \ + --hash=sha256:89d2e02167fa95172c017732ed7725bc8523c598757f08d13c5acca308e1a061 \ + --hash=sha256:8d6aaa4e7155afaf994d7924eb290abbe81a6905b303d8cb61310a2aba1c68ba \ + --hash=sha256:92a2964319d359f494f16011e23434f6f8ef0434acd3cf154a6b7bec511e2fb7 \ + --hash=sha256:96372fc29471646b9b106ee918c8eeb4cca423fcbf9a34daa1b93767a88a2290 \ + --hash=sha256:978b046ca728073070e9abc074b6299ebf3501e8dee5e26efacb13cec2b2dea0 \ + --hash=sha256:9c7149272fb5834fc186328e2c1fa01dda3e1fa940ce18fded6d412e8f2cf76d \ + --hash=sha256:a0239da9fbafd9ff82fd67c16704a7d1bccf0d107a300e790587ad05547681c8 \ + --hash=sha256:ad5383a67514e8e76906a06741febd9126fc7c7ff0f599d6fcce3e82b80d026f \ + --hash=sha256:ad61a9639792fd790523ba072c0555cd6be5a0baf03a49a5dd8cfcf20d56df48 \ + --hash=sha256:b29bfd650ed8e148f9c515474a6ef0ba1090b7a8faeee26b74a8ff3b33617502 \ + --hash=sha256:b97decbb3372d4b69e4d4c8117f44632551c692bb1361b356a02b97b69e18a62 \ + --hash=sha256:ba71c9b4dcbb16212f334126cc3d8beb6af377f6703d9dc2d9fb3874fd667ee9 \ + --hash=sha256:c37c5cce780349d4d51739ae682dec63573847a2a8dcb44381b174c3d9c8d403 \ + --hash=sha256:c971bf3786b5fad82ce5ad570dc6ee420f5b12527157929e830f51c55dc8af77 \ + --hash=sha256:d1fde0f44029e02d02d3993ad55ce93ead9bb9b15c6b7ccd580f90bd7e3de476 \ + --hash=sha256:d24b8bb40d5c61ef2d9b6a8f4528c2f17f1c5d2d31fed62ec860f6006142e83e \ + --hash=sha256:d5ba88df9aa5e2f806650fcbeedbe4f6e8736e92fc0e73b0400538fd25a4dd96 \ + --hash=sha256:d6f76310355e9fae637c3162936e9504b4767d5c52ca268331e2756e54fd4ca5 \ + --hash=sha256:d737fc67b9a970f3234754974531dc9afeea11c70791dcb7db53b0cf81b79784 \ + --hash=sha256:da22885266bbfb3f78218dc40205fed2671909fbd0720aedba39b4515c038091 \ + --hash=sha256:da37dcfbf4b7f45d80ee386a5f81122501ec75672f475da34784196690762f4b \ + --hash=sha256:db19d60d846283ee275d0416e2a23493f4e6b6028825b51290ac05afc87a6f97 \ + --hash=sha256:db4c979b0b3e0fa7e9e69ecd11b2b3174c6963cebadeecfb7ad24532ffcdd11a \ + --hash=sha256:e164e0a98e92d06da343d17d4e9c4da4654f4a4588a20d6c73548a29f176abe2 \ + --hash=sha256:e168a7560b7c61342ae0412997b069753f27ac4862ec7867eff74f0fe4ea2ad9 \ + --hash=sha256:e381581b37db1db7597b62a2e6b8b57c3deec95d93b6d6407c5b61ddc98aca6d \ + --hash=sha256:e65bc19919c910127c06759a63747ebe14f386cda573d95bcc62b427ca1afc73 \ + --hash=sha256:e7b8813be97cab8cb52b1375f41f8e6804f6507fe4660152e8ca5c48f0436017 \ + --hash=sha256:e8a78079d9a39ca9ca99a8b0ac2fdc0c4d25fc80c8a8a82e5c8211509c523363 \ + --hash=sha256:ebf909ea0a3fc9596e40d55d8000702a85e27fd578ff41a5500f68f20fd32e6c \ + --hash=sha256:ec40170327d4a404b0d91855d41bfe1fe4b699222b2b93e3d833a27330a87a6d \ + --hash=sha256:f178d2aadf0166be4df834c4953da2d7eef24719e8aec9a65289483eeea9d618 \ + --hash=sha256:f88df3a83cf9df566f171adba39d5bd52814ac0b94778d2448652fc77f9eb491 \ + --hash=sha256:f973157ffeab5459eefe7b97a804987876dd0a55570b8fa56b4e1954bf11329b \ + --hash=sha256:ff25f48fc8e623d95eca0670b8cc1469a83783c924a602e0fbd47363bb54aaca + # via -r ./requirements.txt +aiosignal==1.3.1 \ + --hash=sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc \ + --hash=sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17 + # via aiohttp +aiosqlite==0.17.0 \ + --hash=sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231 \ + --hash=sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51 + # via tortoise-orm +anyio==3.6.2 \ + --hash=sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421 \ + --hash=sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3 + # via + # httpcore + # starlette +astroid==2.13.3 \ + --hash=sha256:14c1603c41cc61aae731cad1884a073c4645e26f126d13ac8346113c95577f3b \ + --hash=sha256:6afc22718a48a689ca24a97981ad377ba7fb78c133f40335dfd16772f29bcfb1 + # via pylint +async-timeout==4.0.2 \ + --hash=sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15 \ + --hash=sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c + # via aiohttp +asyncpg==0.27.0 \ + --hash=sha256:16ba8ec2e85d586b4a12bcd03e8d29e3d99e832764d6a1d0b8c27dbbe4a2569d \ + --hash=sha256:18f77e8e71e826ba2d0c3ba6764930776719ae2b225ca07e014590545928b576 \ + --hash=sha256:1b6499de06fe035cf2fa932ec5617ed3f37d4ebbf663b655922e105a484a6af9 \ + --hash=sha256:20b596d8d074f6f695c13ffb8646d0b6bb1ab570ba7b0cfd349b921ff03cfc1e \ + --hash=sha256:2232ebae9796d4600a7819fc383da78ab51b32a092795f4555575fc934c1c89d \ + --hash=sha256:4750f5cf49ed48a6e49c6e5aed390eee367694636c2dcfaf4a273ca832c5c43c \ + --hash=sha256:4bb366ae34af5b5cabc3ac6a5347dfb6013af38c68af8452f27968d49085ecc0 \ + --hash=sha256:5710cb0937f696ce303f5eed6d272e3f057339bb4139378ccecafa9ee923a71c \ + --hash=sha256:609054a1f47292a905582a1cfcca51a6f3f30ab9d822448693e66fdddde27920 \ + --hash=sha256:62932f29cf2433988fcd799770ec64b374a3691e7902ecf85da14d5e0854d1ea \ + --hash=sha256:69aa1b443a182b13a17ff926ed6627af2d98f62f2fe5890583270cc4073f63bf \ + --hash=sha256:71cca80a056ebe19ec74b7117b09e650990c3ca535ac1c35234a96f65604192f \ + --hash=sha256:720986d9a4705dd8a40fdf172036f5ae787225036a7eb46e704c45aa8f62c054 \ + --hash=sha256:768e0e7c2898d40b16d4ef7a0b44e8150db3dd8995b4652aa1fe2902e92c7df8 \ + --hash=sha256:7a6206210c869ebd3f4eb9e89bea132aefb56ff3d1b7dd7e26b102b17e27bbb1 \ + --hash=sha256:7d8585707ecc6661d07367d444bbaa846b4e095d84451340da8df55a3757e152 \ + --hash=sha256:8113e17cfe236dc2277ec844ba9b3d5312f61bd2fdae6d3ed1c1cdd75f6cf2d8 \ + --hash=sha256:879c29a75969eb2722f94443752f4720d560d1e748474de54ae8dd230bc4956b \ + --hash=sha256:88b62164738239f62f4af92567b846a8ef7cf8abf53eddd83650603de4d52163 \ + --hash=sha256:8934577e1ed13f7d2d9cea3cc016cc6f95c19faedea2c2b56a6f94f257cea672 \ + --hash=sha256:9654085f2b22f66952124de13a8071b54453ff972c25c59b5ce1173a4283ffd9 \ + --hash=sha256:975a320baf7020339a67315284a4d3bf7460e664e484672bd3e71dbd881bc692 \ + --hash=sha256:9a3a4ff43702d39e3c97a8786314123d314e0f0e4dabc8367db5b665c93914de \ + --hash=sha256:a7a94c03386bb95456b12c66026b3a87d1b965f0f1e5733c36e7229f8f137747 \ + --hash=sha256:ab0f21c4818d46a60ca789ebc92327d6d874d3b7ccff3963f7af0a21dc6cff52 \ + --hash=sha256:bb71211414dd1eeb8d31ec529fe77cff04bf53efc783a5f6f0a32d84923f45cf \ + --hash=sha256:bf21ebf023ec67335258e0f3d3ad7b91bb9507985ba2b2206346de488267cad0 \ + --hash=sha256:bfc3980b4ba6f97138b04f0d32e8af21d6c9fa1f8e6e140c07d15690a0a99279 \ + --hash=sha256:c2232d4625c558f2aa001942cac1d7952aa9f0dbfc212f63bc754277769e1ef2 \ + --hash=sha256:ccddb9419ab4e1c48742457d0c0362dbdaeb9b28e6875115abfe319b29ee225d \ + --hash=sha256:d20dea7b83651d93b1eb2f353511fe7fd554752844523f17ad30115d8b9c8cd6 \ + --hash=sha256:e56ac8a8237ad4adec97c0cd4728596885f908053ab725e22900b5902e7f8e69 \ + --hash=sha256:eb4b2fdf88af4fb1cc569781a8f933d2a73ee82cd720e0cb4edabbaecf2a905b \ + --hash=sha256:eca01eb112a39d31cc4abb93a5aef2a81514c23f70956729f42fb83b11b3483f \ + --hash=sha256:fca608d199ffed4903dce1bcd97ad0fe8260f405c1c225bdf0002709132171c2 \ + --hash=sha256:fddcacf695581a8d856654bc4c8cfb73d5c9df26d5f55201722d3e6a699e9629 + # via tortoise-orm +attrs==22.2.0 \ + --hash=sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836 \ + --hash=sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99 + # via + # aiohttp + # openapi-python-client +autoflake==2.0.0 \ + --hash=sha256:7185b596e70d8970c6d4106c112ef41921e472bd26abf3613db99eca88cc8c2a \ + --hash=sha256:d58ed4187c6b4f623a942b9a90c43ff84bf6a266f3682f407b42ca52073c9678 + # via openapi-python-client +bcrypt==4.0.1 \ + --hash=sha256:089098effa1bc35dc055366740a067a2fc76987e8ec75349eb9484061c54f535 \ + --hash=sha256:08d2947c490093a11416df18043c27abe3921558d2c03e2076ccb28a116cb6d0 \ + --hash=sha256:0eaa47d4661c326bfc9d08d16debbc4edf78778e6aaba29c1bc7ce67214d4410 \ + --hash=sha256:27d375903ac8261cfe4047f6709d16f7d18d39b1ec92aaf72af989552a650ebd \ + --hash=sha256:2b3ac11cf45161628f1f3733263e63194f22664bf4d0c0f3ab34099c02134665 \ + --hash=sha256:2caffdae059e06ac23fce178d31b4a702f2a3264c20bfb5ff541b338194d8fab \ + --hash=sha256:3100851841186c25f127731b9fa11909ab7b1df6fc4b9f8353f4f1fd952fbf71 \ + --hash=sha256:5ad4d32a28b80c5fa6671ccfb43676e8c1cc232887759d1cd7b6f56ea4355215 \ + --hash=sha256:67a97e1c405b24f19d08890e7ae0c4f7ce1e56a712a016746c8b2d7732d65d4b \ + --hash=sha256:705b2cea8a9ed3d55b4491887ceadb0106acf7c6387699fca771af56b1cdeeda \ + --hash=sha256:8a68f4341daf7522fe8d73874de8906f3a339048ba406be6ddc1b3ccb16fc0d9 \ + --hash=sha256:a522427293d77e1c29e303fc282e2d71864579527a04ddcfda6d4f8396c6c36a \ + --hash=sha256:ae88eca3024bb34bb3430f964beab71226e761f51b912de5133470b649d82344 \ + --hash=sha256:b1023030aec778185a6c16cf70f359cbb6e0c289fd564a7cfa29e727a1c38f8f \ + --hash=sha256:b3b85202d95dd568efcb35b53936c5e3b3600c7cdcc6115ba461df3a8e89f38d \ + --hash=sha256:b57adba8a1444faf784394de3436233728a1ecaeb6e07e8c22c8848f179b893c \ + --hash=sha256:bf4fa8b2ca74381bb5442c089350f09a3f17797829d958fad058d6e44d9eb83c \ + --hash=sha256:ca3204d00d3cb2dfed07f2d74a25f12fc12f73e606fcaa6975d1f7ae69cacbb2 \ + --hash=sha256:cbb03eec97496166b704ed663a53680ab57c5084b2fc98ef23291987b525cb7d \ + --hash=sha256:e9a51bbfe7e9802b5f3508687758b564069ba937748ad7b9e890086290d2f79e \ + --hash=sha256:fbdaec13c5105f0c4e5c52614d04f0bca5f5af007910daa8b6b12095edaa67b3 + # via passlib +beautifulsoup4==4.11.2 \ + --hash=sha256:0e79446b10b3ecb499c1556f7e228a53e64a2bfcebd455f370d8927cb5b59e39 \ + --hash=sha256:bc4bdda6717de5a2987436fb8d72f45dc90dd856bdfd512a1314ce90349a0106 + # via -r ./requirements.txt +black==22.12.0 \ + --hash=sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320 \ + --hash=sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351 \ + --hash=sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350 \ + --hash=sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f \ + --hash=sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf \ + --hash=sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148 \ + --hash=sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4 \ + --hash=sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d \ + --hash=sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc \ + --hash=sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d \ + --hash=sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2 \ + --hash=sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f + # via openapi-python-client +certifi==2022.12.7 \ + --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ + --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 + # via + # httpcore + # httpx +charset-normalizer==2.1.1 \ + --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \ + --hash=sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f + # via aiohttp +click==8.1.3 \ + --hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \ + --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48 + # via + # -r ./requirements.txt + # black + # typer +dataclass-wizard==0.22.2 \ + --hash=sha256:211f842e5e9a8ace50ba891ef428cd78c82579fb98024f80f3e630ca8d1946f6 \ + --hash=sha256:49be36ecc64bc5a1e9a35a6bad1d71d33b6b9b06877404931a17c6a3a6dfbb10 + # via -r ./requirements.txt +dill==0.3.6 \ + --hash=sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0 \ + --hash=sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373 + # via pylint +fastapi==0.89.1 \ + --hash=sha256:15d9271ee52b572a015ca2ae5c72e1ce4241dd8532a534ad4f7ec70c376a580f \ + --hash=sha256:f9773ea22290635b2f48b4275b2bf69a8fa721fda2e38228bed47139839dc877 + # via + # -r ./requirements.txt + # fastapi-pagination +fastapi-pagination==0.11.2 \ + --hash=sha256:55ecf04131c22c9ee9b9c060d42b1555fba1188aa60fac826a9e475bb9246e83 \ + --hash=sha256:db4bde1c055a85e3840faa70b2a63d27e45542c0ebd97cba8ca786850bb48c6d + # via -r ./requirements.txt +frozenlist==1.3.3 \ + --hash=sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c \ + --hash=sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f \ + --hash=sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a \ + --hash=sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784 \ + --hash=sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27 \ + --hash=sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d \ + --hash=sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3 \ + --hash=sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678 \ + --hash=sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a \ + --hash=sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483 \ + --hash=sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8 \ + --hash=sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf \ + --hash=sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99 \ + --hash=sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c \ + --hash=sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48 \ + --hash=sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5 \ + --hash=sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56 \ + --hash=sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e \ + --hash=sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1 \ + --hash=sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401 \ + --hash=sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4 \ + --hash=sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e \ + --hash=sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649 \ + --hash=sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a \ + --hash=sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d \ + --hash=sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0 \ + --hash=sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6 \ + --hash=sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d \ + --hash=sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b \ + --hash=sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6 \ + --hash=sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf \ + --hash=sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef \ + --hash=sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7 \ + --hash=sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842 \ + --hash=sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba \ + --hash=sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420 \ + --hash=sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b \ + --hash=sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d \ + --hash=sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332 \ + --hash=sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936 \ + --hash=sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816 \ + --hash=sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91 \ + --hash=sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420 \ + --hash=sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448 \ + --hash=sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411 \ + --hash=sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4 \ + --hash=sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32 \ + --hash=sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b \ + --hash=sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0 \ + --hash=sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530 \ + --hash=sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669 \ + --hash=sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7 \ + --hash=sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1 \ + --hash=sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5 \ + --hash=sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce \ + --hash=sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4 \ + --hash=sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e \ + --hash=sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2 \ + --hash=sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d \ + --hash=sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9 \ + --hash=sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642 \ + --hash=sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0 \ + --hash=sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703 \ + --hash=sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb \ + --hash=sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1 \ + --hash=sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13 \ + --hash=sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab \ + --hash=sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38 \ + --hash=sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb \ + --hash=sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb \ + --hash=sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81 \ + --hash=sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8 \ + --hash=sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd \ + --hash=sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4 + # via + # aiohttp + # aiosignal +h11==0.14.0 \ + --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ + --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 + # via + # httpcore + # hypercorn + # wsproto +h2==4.1.0 \ + --hash=sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d \ + --hash=sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb + # via hypercorn +hpack==4.0.0 \ + --hash=sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c \ + --hash=sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095 + # via h2 +httpcore==0.16.3 \ + --hash=sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb \ + --hash=sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0 + # via httpx +httpx==0.23.3 \ + --hash=sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9 \ + --hash=sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6 + # via openapi-python-client +hypercorn==0.14.3 \ + --hash=sha256:4a87a0b7bbe9dc75fab06dbe4b301b9b90416e9866c23a377df21a969d6ab8dd \ + --hash=sha256:7c491d5184f28ee960dcdc14ab45d14633ca79d72ddd13cf4fcb4cb854d679ab + # via -r ./requirements.txt +hyperframe==6.0.1 \ + --hash=sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15 \ + --hash=sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914 + # via h2 +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 + # via + # anyio + # rfc3986 + # yarl +iso8601==1.1.0 \ + --hash=sha256:32811e7b81deee2063ea6d2e94f8819a86d1f3811e49d23623a41fa832bef03f \ + --hash=sha256:8400e90141bf792bce2634df533dc57e3bee19ea120a87bebcd3da89a58ad73f + # via tortoise-orm +isort==5.11.4 \ + --hash=sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6 \ + --hash=sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b + # via + # openapi-python-client + # pylint +itsdangerous==2.1.2 \ + --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ + --hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a + # via -r ./requirements.txt +jinja2==3.1.2 \ + --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ + --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 + # via + # -r ./requirements.txt + # openapi-python-client +lazy-object-proxy==1.9.0 \ + --hash=sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382 \ + --hash=sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82 \ + --hash=sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9 \ + --hash=sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494 \ + --hash=sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46 \ + --hash=sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30 \ + --hash=sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63 \ + --hash=sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4 \ + --hash=sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae \ + --hash=sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be \ + --hash=sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701 \ + --hash=sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd \ + --hash=sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006 \ + --hash=sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a \ + --hash=sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586 \ + --hash=sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8 \ + --hash=sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821 \ + --hash=sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07 \ + --hash=sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b \ + --hash=sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171 \ + --hash=sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b \ + --hash=sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2 \ + --hash=sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7 \ + --hash=sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4 \ + --hash=sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8 \ + --hash=sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e \ + --hash=sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f \ + --hash=sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda \ + --hash=sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4 \ + --hash=sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e \ + --hash=sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671 \ + --hash=sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11 \ + --hash=sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455 \ + --hash=sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734 \ + --hash=sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb \ + --hash=sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59 + # via astroid +markupsafe==2.1.2 \ + --hash=sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed \ + --hash=sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc \ + --hash=sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2 \ + --hash=sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460 \ + --hash=sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7 \ + --hash=sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0 \ + --hash=sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1 \ + --hash=sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa \ + --hash=sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03 \ + --hash=sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323 \ + --hash=sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65 \ + --hash=sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013 \ + --hash=sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036 \ + --hash=sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f \ + --hash=sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4 \ + --hash=sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419 \ + --hash=sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2 \ + --hash=sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619 \ + --hash=sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a \ + --hash=sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a \ + --hash=sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd \ + --hash=sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7 \ + --hash=sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666 \ + --hash=sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65 \ + --hash=sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859 \ + --hash=sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625 \ + --hash=sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff \ + --hash=sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156 \ + --hash=sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd \ + --hash=sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba \ + --hash=sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f \ + --hash=sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1 \ + --hash=sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094 \ + --hash=sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a \ + --hash=sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513 \ + --hash=sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed \ + --hash=sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d \ + --hash=sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3 \ + --hash=sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147 \ + --hash=sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c \ + --hash=sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603 \ + --hash=sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601 \ + --hash=sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a \ + --hash=sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1 \ + --hash=sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d \ + --hash=sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3 \ + --hash=sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54 \ + --hash=sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2 \ + --hash=sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6 \ + --hash=sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58 + # via jinja2 +mccabe==0.7.0 \ + --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ + --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e + # via pylint +multidict==6.0.4 \ + --hash=sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9 \ + --hash=sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8 \ + --hash=sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03 \ + --hash=sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710 \ + --hash=sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161 \ + --hash=sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664 \ + --hash=sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569 \ + --hash=sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067 \ + --hash=sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313 \ + --hash=sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706 \ + --hash=sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2 \ + --hash=sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636 \ + --hash=sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49 \ + --hash=sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93 \ + --hash=sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603 \ + --hash=sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0 \ + --hash=sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60 \ + --hash=sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4 \ + --hash=sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e \ + --hash=sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1 \ + --hash=sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60 \ + --hash=sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951 \ + --hash=sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc \ + --hash=sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe \ + --hash=sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95 \ + --hash=sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d \ + --hash=sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8 \ + --hash=sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed \ + --hash=sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2 \ + --hash=sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775 \ + --hash=sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87 \ + --hash=sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c \ + --hash=sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2 \ + --hash=sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98 \ + --hash=sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3 \ + --hash=sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe \ + --hash=sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78 \ + --hash=sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660 \ + --hash=sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176 \ + --hash=sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e \ + --hash=sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988 \ + --hash=sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c \ + --hash=sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c \ + --hash=sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0 \ + --hash=sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449 \ + --hash=sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f \ + --hash=sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde \ + --hash=sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5 \ + --hash=sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d \ + --hash=sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac \ + --hash=sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a \ + --hash=sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9 \ + --hash=sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca \ + --hash=sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11 \ + --hash=sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35 \ + --hash=sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063 \ + --hash=sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b \ + --hash=sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982 \ + --hash=sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258 \ + --hash=sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1 \ + --hash=sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52 \ + --hash=sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480 \ + --hash=sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7 \ + --hash=sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461 \ + --hash=sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d \ + --hash=sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc \ + --hash=sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779 \ + --hash=sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a \ + --hash=sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547 \ + --hash=sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0 \ + --hash=sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171 \ + --hash=sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf \ + --hash=sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d \ + --hash=sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba + # via + # aiohttp + # yarl +mypy-extensions==0.4.3 \ + --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ + --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 + # via black +openapi-python-client==0.13.1 \ + --hash=sha256:43bcd2e43e39dc31decba76ec09cbaeb6faad8709ce4684aec9b0228cd1bf3b5 \ + --hash=sha256:adb5d886946cae2ff654f26396bd4d3f497234d5a9dafee805ee19587acbfdce + # via -r ./requirements.txt +passlib[bcrypt]==1.7.4 \ + --hash=sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 \ + --hash=sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04 + # via -r ./requirements.txt +pathspec==0.11.0 \ + --hash=sha256:3a66eb970cbac598f9e5ccb5b2cf58930cd8e3ed86d393d541eaf2d8b1705229 \ + --hash=sha256:64d338d4e0914e91c1792321e6907b5a593f1ab1851de7fc269557a21b30ebbc + # via black +platformdirs==2.6.2 \ + --hash=sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490 \ + --hash=sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2 + # via + # black + # pylint +priority==2.0.0 \ + --hash=sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa \ + --hash=sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0 + # via hypercorn +protobuf==4.21.12 \ + --hash=sha256:1f22ac0ca65bb70a876060d96d914dae09ac98d114294f77584b0d2644fa9c30 \ + --hash=sha256:237216c3326d46808a9f7c26fd1bd4b20015fb6867dc5d263a493ef9a539293b \ + --hash=sha256:27f4d15021da6d2b706ddc3860fac0a5ddaba34ab679dc182b60a8bb4e1121cc \ + --hash=sha256:299ea899484ee6f44604deb71f424234f654606b983cb496ea2a53e3c63ab791 \ + --hash=sha256:3d164928ff0727d97022957c2b849250ca0e64777ee31efd7d6de2e07c494717 \ + --hash=sha256:6ab80df09e3208f742c98443b6166bcb70d65f52cfeb67357d52032ea1ae9bec \ + --hash=sha256:78a28c9fa223998472886c77042e9b9afb6fe4242bd2a2a5aced88e3f4422aa7 \ + --hash=sha256:7cd532c4566d0e6feafecc1059d04c7915aec8e182d1cf7adee8b24ef1e2e6ab \ + --hash=sha256:89f9149e4a0169cddfc44c74f230d7743002e3aa0b9472d8c28f0388102fc4c2 \ + --hash=sha256:a53fd3f03e578553623272dc46ac2f189de23862e68565e83dde203d41b76fc5 \ + --hash=sha256:b135410244ebe777db80298297a97fbb4c862c881b4403b71bac9d4107d61fd1 \ + --hash=sha256:b98d0148f84e3a3c569e19f52103ca1feacdac0d2df8d6533cf983d1fda28462 \ + --hash=sha256:d1736130bce8cf131ac7957fa26880ca19227d4ad68b4888b3be0dea1f95df97 \ + --hash=sha256:f45460f9ee70a0ec1b6694c6e4e348ad2019275680bd68a1d9314b8c7e01e574 + # via temporalio +pydantic==1.10.4 \ + --hash=sha256:05a81b006be15655b2a1bae5faa4280cf7c81d0e09fcb49b342ebf826abe5a72 \ + --hash=sha256:0b53e1d41e97063d51a02821b80538053ee4608b9a181c1005441f1673c55423 \ + --hash=sha256:2b3ce5f16deb45c472dde1a0ee05619298c864a20cded09c4edd820e1454129f \ + --hash=sha256:2e82a6d37a95e0b1b42b82ab340ada3963aea1317fd7f888bb6b9dfbf4fff57c \ + --hash=sha256:301d626a59edbe5dfb48fcae245896379a450d04baeed50ef40d8199f2733b06 \ + --hash=sha256:39f4a73e5342b25c2959529f07f026ef58147249f9b7431e1ba8414a36761f53 \ + --hash=sha256:4948f264678c703f3877d1c8877c4e3b2e12e549c57795107f08cf70c6ec7774 \ + --hash=sha256:4b05697738e7d2040696b0a66d9f0a10bec0efa1883ca75ee9e55baf511909d6 \ + --hash=sha256:51bdeb10d2db0f288e71d49c9cefa609bca271720ecd0c58009bd7504a0c464c \ + --hash=sha256:55b1625899acd33229c4352ce0ae54038529b412bd51c4915349b49ca575258f \ + --hash=sha256:572066051eeac73d23f95ba9a71349c42a3e05999d0ee1572b7860235b850cc6 \ + --hash=sha256:6a05a9db1ef5be0fe63e988f9617ca2551013f55000289c671f71ec16f4985e3 \ + --hash=sha256:6dc1cc241440ed7ca9ab59d9929075445da6b7c94ced281b3dd4cfe6c8cff817 \ + --hash=sha256:6e7124d6855b2780611d9f5e1e145e86667eaa3bd9459192c8dc1a097f5e9903 \ + --hash=sha256:75d52162fe6b2b55964fbb0af2ee58e99791a3138588c482572bb6087953113a \ + --hash=sha256:78cec42b95dbb500a1f7120bdf95c401f6abb616bbe8785ef09887306792e66e \ + --hash=sha256:7feb6a2d401f4d6863050f58325b8d99c1e56f4512d98b11ac64ad1751dc647d \ + --hash=sha256:8775d4ef5e7299a2f4699501077a0defdaac5b6c4321173bcb0f3c496fbadf85 \ + --hash=sha256:887ca463c3bc47103c123bc06919c86720e80e1214aab79e9b779cda0ff92a00 \ + --hash=sha256:9193d4f4ee8feca58bc56c8306bcb820f5c7905fd919e0750acdeeeef0615b28 \ + --hash=sha256:983e720704431a6573d626b00662eb78a07148c9115129f9b4351091ec95ecc3 \ + --hash=sha256:990406d226dea0e8f25f643b370224771878142155b879784ce89f633541a024 \ + --hash=sha256:9cbdc268a62d9a98c56e2452d6c41c0263d64a2009aac69246486f01b4f594c4 \ + --hash=sha256:a48f1953c4a1d9bd0b5167ac50da9a79f6072c63c4cef4cf2a3736994903583e \ + --hash=sha256:a9a6747cac06c2beb466064dda999a13176b23535e4c496c9d48e6406f92d42d \ + --hash=sha256:a9f2de23bec87ff306aef658384b02aa7c32389766af3c5dee9ce33e80222dfa \ + --hash=sha256:b5635de53e6686fe7a44b5cf25fcc419a0d5e5c1a1efe73d49d48fe7586db854 \ + --hash=sha256:b6f9d649892a6f54a39ed56b8dfd5e08b5f3be5f893da430bed76975f3735d15 \ + --hash=sha256:b9a3859f24eb4e097502a3be1fb4b2abb79b6103dd9e2e0edb70613a4459a648 \ + --hash=sha256:cd8702c5142afda03dc2b1ee6bc358b62b3735b2cce53fc77b31ca9f728e4bc8 \ + --hash=sha256:d7b5a3821225f5c43496c324b0d6875fde910a1c2933d726a743ce328fbb2a8c \ + --hash=sha256:d88c4c0e5c5dfd05092a4b271282ef0588e5f4aaf345778056fc5259ba098857 \ + --hash=sha256:eb992a1ef739cc7b543576337bebfc62c0e6567434e522e97291b251a41dad7f \ + --hash=sha256:f2f7eb6273dd12472d7f218e1fef6f7c7c2f00ac2e1ecde4db8824c457300416 \ + --hash=sha256:fdf88ab63c3ee282c76d652fc86518aacb737ff35796023fae56a65ced1a5978 \ + --hash=sha256:fdf8d759ef326962b4678d89e275ffc55b7ce59d917d9f72233762061fd04a2d + # via + # -r ./requirements.txt + # fastapi + # fastapi-pagination + # openapi-python-client +pyflakes==3.0.1 \ + --hash=sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf \ + --hash=sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd + # via autoflake +pylint==2.15.10 \ + --hash=sha256:9df0d07e8948a1c3ffa3b6e2d7e6e63d9fb457c5da5b961ed63106594780cc7e \ + --hash=sha256:b3dc5ef7d33858f297ac0d06cc73862f01e4f2e74025ec3eff347ce0bc60baf5 + # via -r ./requirements.txt +pypika-tortoise==0.1.6 \ + --hash=sha256:2d68bbb7e377673743cff42aa1059f3a80228d411fbcae591e4465e173109fd8 \ + --hash=sha256:d802868f479a708e3263724c7b5719a26ad79399b2a70cea065f4a4cadbebf36 + # via tortoise-orm +python-dateutil==2.8.2 \ + --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ + --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 + # via + # openapi-python-client + # temporalio +python-multipart==0.0.5 \ + --hash=sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43 + # via -r ./requirements.txt +pytz==2022.7.1 \ + --hash=sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0 \ + --hash=sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a + # via tortoise-orm +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 + # -r ./requirements.txt + # openapi-python-client +rfc3986[idna2008]==1.5.0 \ + --hash=sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835 \ + --hash=sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97 + # via httpx +setuptools==58.2.0 \ + --hash=sha256:2551203ae6955b9876741a26ab3e767bb3242dafe86a32a749ea0d78b6792f11 \ + --hash=sha256:2c55bdb85d5bb460bd2e3b12052b677879cffcf46c0c688f2e5bf51d36001145 + # via -r ./requirements.txt +shellingham==1.5.0.post1 \ + --hash=sha256:368bf8c00754fd4f55afb7bbb86e272df77e4dc76ac29dbcbb81a59e9fc15744 \ + --hash=sha256:823bc5fb5c34d60f285b624e7264f4dda254bc803a3774a147bf99c0e3004a28 + # via openapi-python-client +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 + # via + # python-dateutil + # python-multipart +sniffio==1.3.0 \ + --hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \ + --hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384 + # via + # anyio + # httpcore + # httpx +soupsieve==2.3.2.post1 \ + --hash=sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759 \ + --hash=sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d + # via beautifulsoup4 +starlette==0.22.0 \ + --hash=sha256:b092cbc365bea34dd6840b42861bdabb2f507f8671e642e8272d2442e08ea4ff \ + --hash=sha256:b5eda991ad5f0ee5d8ce4c4540202a573bb6691ecd0c712262d0bc85cf8f2c50 + # via fastapi +temporalio==1.0.0 \ + --hash=sha256:06126178cbb98d44667914ac57876f24fc3e3532384374cae8b38a6ac59ad8fe \ + --hash=sha256:4d17e953e93241162133cd6e9581bc8a3804a6e2e3aee8ac3c1d503536d7d8c9 \ + --hash=sha256:5efbdad3e409b2f2169c34167a5219ca9abd6ebd694fc52ae974f04b2d7c8d79 \ + --hash=sha256:7c18030d63f178c6c6d958d26c136e77888b7bd1d6db67b129de1bea9a1bd463 \ + --hash=sha256:7c82a875c3db9ab2c8492ddc01498dbb2636cad34cf8bc985a6f0f17bd627f99 \ + --hash=sha256:b2454ef6b68335a554adca1e4f14831b5c3ea33ef8adb25742dd91652bd38a82 + # via -r ./requirements.txt +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via hypercorn +tomli==2.0.1 \ + --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ + --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f + # via + # autoflake + # black + # pylint +tomlkit==0.11.6 \ + --hash=sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b \ + --hash=sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73 + # via pylint +tortoise-orm[asyncpg]==0.19.2 \ + --hash=sha256:a99b8c9f42d5cd49493c70471b5c9a5df8ecf49cc624f2f41b9dc75bba993ac5 \ + --hash=sha256:bff4d79abfca7fb805972bb2438e8e0cd2e6590bc2cfd7593a803518a027bbf0 + # via -r ./requirements.txt +typer==0.7.0 \ + --hash=sha256:b5e704f4e48ec263de1c0b3a2387cd405a13767d2f907f44c1a08cbad96f606d \ + --hash=sha256:ff797846578a9f2a201b53442aedeb543319466870fbe1c701eab66dd7681165 + # via openapi-python-client +types-protobuf==3.20.4.6 \ + --hash=sha256:ab2d315ba82246b83d28f8797c98dc0fe1dd5cfd187909e56faf87239aedaae3 \ + --hash=sha256:ba27443c592bbec1629dd69494a24c84461c63f0d3b7d648ce258aaae9680965 + # via temporalio +typing-extensions==4.4.0 \ + --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \ + --hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e + # via + # aiosqlite + # astroid + # black + # dataclass-wizard + # pydantic + # pylint + # starlette + # temporalio +wrapt==1.14.1 \ + --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \ + --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \ + --hash=sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4 \ + --hash=sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2 \ + --hash=sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656 \ + --hash=sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3 \ + --hash=sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff \ + --hash=sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310 \ + --hash=sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a \ + --hash=sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57 \ + --hash=sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069 \ + --hash=sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383 \ + --hash=sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe \ + --hash=sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87 \ + --hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d \ + --hash=sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b \ + --hash=sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907 \ + --hash=sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f \ + --hash=sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0 \ + --hash=sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28 \ + --hash=sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1 \ + --hash=sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853 \ + --hash=sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc \ + --hash=sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3 \ + --hash=sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3 \ + --hash=sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164 \ + --hash=sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1 \ + --hash=sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c \ + --hash=sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1 \ + --hash=sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7 \ + --hash=sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1 \ + --hash=sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320 \ + --hash=sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed \ + --hash=sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1 \ + --hash=sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248 \ + --hash=sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c \ + --hash=sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456 \ + --hash=sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77 \ + --hash=sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef \ + --hash=sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1 \ + --hash=sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7 \ + --hash=sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86 \ + --hash=sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4 \ + --hash=sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d \ + --hash=sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d \ + --hash=sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8 \ + --hash=sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5 \ + --hash=sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471 \ + --hash=sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00 \ + --hash=sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68 \ + --hash=sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3 \ + --hash=sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d \ + --hash=sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735 \ + --hash=sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d \ + --hash=sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569 \ + --hash=sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7 \ + --hash=sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59 \ + --hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \ + --hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \ + --hash=sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b \ + --hash=sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f \ + --hash=sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462 \ + --hash=sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015 \ + --hash=sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af + # via astroid +wsproto==1.2.0 \ + --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \ + --hash=sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736 + # via hypercorn +yapf==0.32.0 \ + --hash=sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32 \ + --hash=sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b + # via -r ./requirements.txt +yarl==1.8.2 \ + --hash=sha256:009a028127e0a1755c38b03244c0bea9d5565630db9c4cf9572496e947137a87 \ + --hash=sha256:0414fd91ce0b763d4eadb4456795b307a71524dbacd015c657bb2a39db2eab89 \ + --hash=sha256:0978f29222e649c351b173da2b9b4665ad1feb8d1daa9d971eb90df08702668a \ + --hash=sha256:0ef8fb25e52663a1c85d608f6dd72e19bd390e2ecaf29c17fb08f730226e3a08 \ + --hash=sha256:10b08293cda921157f1e7c2790999d903b3fd28cd5c208cf8826b3b508026996 \ + --hash=sha256:1684a9bd9077e922300ecd48003ddae7a7474e0412bea38d4631443a91d61077 \ + --hash=sha256:1b372aad2b5f81db66ee7ec085cbad72c4da660d994e8e590c997e9b01e44901 \ + --hash=sha256:1e21fb44e1eff06dd6ef971d4bdc611807d6bd3691223d9c01a18cec3677939e \ + --hash=sha256:2305517e332a862ef75be8fad3606ea10108662bc6fe08509d5ca99503ac2aee \ + --hash=sha256:24ad1d10c9db1953291f56b5fe76203977f1ed05f82d09ec97acb623a7976574 \ + --hash=sha256:272b4f1599f1b621bf2aabe4e5b54f39a933971f4e7c9aa311d6d7dc06965165 \ + --hash=sha256:2a1fca9588f360036242f379bfea2b8b44cae2721859b1c56d033adfd5893634 \ + --hash=sha256:2b4fa2606adf392051d990c3b3877d768771adc3faf2e117b9de7eb977741229 \ + --hash=sha256:3150078118f62371375e1e69b13b48288e44f6691c1069340081c3fd12c94d5b \ + --hash=sha256:326dd1d3caf910cd26a26ccbfb84c03b608ba32499b5d6eeb09252c920bcbe4f \ + --hash=sha256:34c09b43bd538bf6c4b891ecce94b6fa4f1f10663a8d4ca589a079a5018f6ed7 \ + --hash=sha256:388a45dc77198b2460eac0aca1efd6a7c09e976ee768b0d5109173e521a19daf \ + --hash=sha256:3adeef150d528ded2a8e734ebf9ae2e658f4c49bf413f5f157a470e17a4a2e89 \ + --hash=sha256:3edac5d74bb3209c418805bda77f973117836e1de7c000e9755e572c1f7850d0 \ + --hash=sha256:3f6b4aca43b602ba0f1459de647af954769919c4714706be36af670a5f44c9c1 \ + --hash=sha256:3fc056e35fa6fba63248d93ff6e672c096f95f7836938241ebc8260e062832fe \ + --hash=sha256:418857f837347e8aaef682679f41e36c24250097f9e2f315d39bae3a99a34cbf \ + --hash=sha256:42430ff511571940d51e75cf42f1e4dbdded477e71c1b7a17f4da76c1da8ea76 \ + --hash=sha256:44ceac0450e648de86da8e42674f9b7077d763ea80c8ceb9d1c3e41f0f0a9951 \ + --hash=sha256:47d49ac96156f0928f002e2424299b2c91d9db73e08c4cd6742923a086f1c863 \ + --hash=sha256:48dd18adcf98ea9cd721a25313aef49d70d413a999d7d89df44f469edfb38a06 \ + --hash=sha256:49d43402c6e3013ad0978602bf6bf5328535c48d192304b91b97a3c6790b1562 \ + --hash=sha256:4d04acba75c72e6eb90745447d69f84e6c9056390f7a9724605ca9c56b4afcc6 \ + --hash=sha256:57a7c87927a468e5a1dc60c17caf9597161d66457a34273ab1760219953f7f4c \ + --hash=sha256:58a3c13d1c3005dbbac5c9f0d3210b60220a65a999b1833aa46bd6677c69b08e \ + --hash=sha256:5df5e3d04101c1e5c3b1d69710b0574171cc02fddc4b23d1b2813e75f35a30b1 \ + --hash=sha256:63243b21c6e28ec2375f932a10ce7eda65139b5b854c0f6b82ed945ba526bff3 \ + --hash=sha256:64dd68a92cab699a233641f5929a40f02a4ede8c009068ca8aa1fe87b8c20ae3 \ + --hash=sha256:6604711362f2dbf7160df21c416f81fac0de6dbcf0b5445a2ef25478ecc4c778 \ + --hash=sha256:6c4fcfa71e2c6a3cb568cf81aadc12768b9995323186a10827beccf5fa23d4f8 \ + --hash=sha256:6d88056a04860a98341a0cf53e950e3ac9f4e51d1b6f61a53b0609df342cc8b2 \ + --hash=sha256:705227dccbe96ab02c7cb2c43e1228e2826e7ead880bb19ec94ef279e9555b5b \ + --hash=sha256:728be34f70a190566d20aa13dc1f01dc44b6aa74580e10a3fb159691bc76909d \ + --hash=sha256:74dece2bfc60f0f70907c34b857ee98f2c6dd0f75185db133770cd67300d505f \ + --hash=sha256:75c16b2a900b3536dfc7014905a128a2bea8fb01f9ee26d2d7d8db0a08e7cb2c \ + --hash=sha256:77e913b846a6b9c5f767b14dc1e759e5aff05502fe73079f6f4176359d832581 \ + --hash=sha256:7a66c506ec67eb3159eea5096acd05f5e788ceec7b96087d30c7d2865a243918 \ + --hash=sha256:8c46d3d89902c393a1d1e243ac847e0442d0196bbd81aecc94fcebbc2fd5857c \ + --hash=sha256:93202666046d9edadfe9f2e7bf5e0782ea0d497b6d63da322e541665d65a044e \ + --hash=sha256:97209cc91189b48e7cfe777237c04af8e7cc51eb369004e061809bcdf4e55220 \ + --hash=sha256:a48f4f7fea9a51098b02209d90297ac324241bf37ff6be6d2b0149ab2bd51b37 \ + --hash=sha256:a783cd344113cb88c5ff7ca32f1f16532a6f2142185147822187913eb989f739 \ + --hash=sha256:ae0eec05ab49e91a78700761777f284c2df119376e391db42c38ab46fd662b77 \ + --hash=sha256:ae4d7ff1049f36accde9e1ef7301912a751e5bae0a9d142459646114c70ecba6 \ + --hash=sha256:b05df9ea7496df11b710081bd90ecc3a3db6adb4fee36f6a411e7bc91a18aa42 \ + --hash=sha256:baf211dcad448a87a0d9047dc8282d7de59473ade7d7fdf22150b1d23859f946 \ + --hash=sha256:bb81f753c815f6b8e2ddd2eef3c855cf7da193b82396ac013c661aaa6cc6b0a5 \ + --hash=sha256:bcd7bb1e5c45274af9a1dd7494d3c52b2be5e6bd8d7e49c612705fd45420b12d \ + --hash=sha256:bf071f797aec5b96abfc735ab97da9fd8f8768b43ce2abd85356a3127909d146 \ + --hash=sha256:c15163b6125db87c8f53c98baa5e785782078fbd2dbeaa04c6141935eb6dab7a \ + --hash=sha256:cb6d48d80a41f68de41212f3dfd1a9d9898d7841c8f7ce6696cf2fd9cb57ef83 \ + --hash=sha256:ceff9722e0df2e0a9e8a79c610842004fa54e5b309fe6d218e47cd52f791d7ef \ + --hash=sha256:cfa2bbca929aa742b5084fd4663dd4b87c191c844326fcb21c3afd2d11497f80 \ + --hash=sha256:d617c241c8c3ad5c4e78a08429fa49e4b04bedfc507b34b4d8dceb83b4af3588 \ + --hash=sha256:d881d152ae0007809c2c02e22aa534e702f12071e6b285e90945aa3c376463c5 \ + --hash=sha256:da65c3f263729e47351261351b8679c6429151ef9649bba08ef2528ff2c423b2 \ + --hash=sha256:de986979bbd87272fe557e0a8fcb66fd40ae2ddfe28a8b1ce4eae22681728fef \ + --hash=sha256:df60a94d332158b444301c7f569659c926168e4d4aad2cfbf4bce0e8fb8be826 \ + --hash=sha256:dfef7350ee369197106805e193d420b75467b6cceac646ea5ed3049fcc950a05 \ + --hash=sha256:e59399dda559688461762800d7fb34d9e8a6a7444fd76ec33220a926c8be1516 \ + --hash=sha256:e6f3515aafe0209dd17fb9bdd3b4e892963370b3de781f53e1746a521fb39fc0 \ + --hash=sha256:e7fd20d6576c10306dea2d6a5765f46f0ac5d6f53436217913e952d19237efc4 \ + --hash=sha256:ebb78745273e51b9832ef90c0898501006670d6e059f2cdb0e999494eb1450c2 \ + --hash=sha256:efff27bd8cbe1f9bd127e7894942ccc20c857aa8b5a0327874f30201e5ce83d0 \ + --hash=sha256:f37db05c6051eff17bc832914fe46869f8849de5b92dc4a3466cd63095d23dfd \ + --hash=sha256:f8ca8ad414c85bbc50f49c0a106f951613dfa5f948ab69c10ce9b128d368baf8 \ + --hash=sha256:fb742dcdd5eec9f26b61224c23baea46c9055cf16f62475e11b9b15dfd5c117b \ + --hash=sha256:fc77086ce244453e074e445104f0ecb27530d6fd3a46698e33f6c38951d5a0f1 \ + --hash=sha256:ff205b58dc2929191f68162633d5e10e8044398d7a45265f90a0f1d51f85f72c + # via aiohttp